Skip to content

Commit 492fd48

Browse files
Merge pull request #158 from niteeshkanna-sh/claude/stoic-rubin-fnglq7
The panel fits the screen, and the screen stops scrolling
2 parents 8065612 + 61c2f98 commit 492fd48

3 files changed

Lines changed: 179 additions & 56 deletions

File tree

‎admin/admin.css‎

Lines changed: 147 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,24 @@ button { font-family: inherit; }
121121

122122
.ns { display: grid; grid-template-columns: var(--side, 244px) minmax(0, 1fr); min-height: 100vh; }
123123

124+
/* ---- the window itself does not scroll ----
125+
On a desktop the shell is the height of the screen and the page area inside
126+
it does the scrolling. The sidebar and the top bar then stay where they
127+
are, instead of the whole document sliding and taking the navigation with
128+
it -- which is what made a long tab feel like a web page rather than a
129+
panel. A phone keeps ordinary page scrolling: a fixed-height shell there
130+
fights the address bar hiding and showing, and loses. */
131+
@media (min-width: 1024px) {
132+
html, body { height: 100%; overflow: hidden; }
133+
.ns { height: 100dvh; min-height: 0; }
134+
.ns-main { min-height: 0; overflow: hidden; }
135+
.ns-content {
136+
min-height: 0;
137+
overflow-y: auto;
138+
overscroll-behavior: contain;
139+
}
140+
}
141+
124142
/* ---- the column ---- */
125143
.ns-side {
126144
grid-column: 1; background: var(--primary); color: #fff;
@@ -315,7 +333,7 @@ button { font-family: inherit; }
315333
outline: 2px solid var(--primary); outline-offset: 1px; opacity: 1;
316334
}
317335

318-
.ns-content { flex: 1; width: 100%; max-width: 1180px; padding: 24px; }
336+
.ns-content { flex: 1; width: 100%; max-width: 1180px; padding: 16px 20px; }
319337

320338
/* Except on a list. 1180px is a reading measure -- the width at which a
321339
paragraph or a form stays comfortable -- and a records table is neither. At
@@ -325,6 +343,11 @@ button { font-family: inherit; }
325343
and on any screen too narrow to use it, nothing changes. */
326344
.ns-content:has(.admin-panel:not([hidden]) .rec-tablewrap:not([hidden] *)) { max-width: 1520px; }
327345

346+
/* The overview too, and for the same reason: it is counters and lists, not a
347+
paragraph. Held to a reading measure it stacked ten sections down a narrow
348+
strip with a third of the monitor empty beside it. */
349+
.ns-content:has(#panel-dashboard:not([hidden])) { max-width: 1520px; }
350+
328351
/* The scrim only exists while the drawer is open, and only on a phone. */
329352
.ns-scrim { display: none; }
330353

@@ -357,17 +380,17 @@ button { font-family: inherit; }
357380
}
358381

359382
.panel-header {
360-
display: flex; justify-content: space-between; align-items: flex-end; flex-wrap: wrap; gap: 12px;
361-
margin-bottom: 14px;
383+
display: flex; justify-content: space-between; align-items: flex-end; flex-wrap: wrap; gap: 10px;
384+
margin-bottom: 9px;
362385
}
363386
/* Heading and description on one line. They were stacked, which cost a whole
364387
line of height at the top of every panel for a sentence that is context
365388
rather than content. */
366389
.panel-header > div {
367390
display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; min-width: 0;
368391
}
369-
.panel-header h2 { font-size: 1.15rem; margin-bottom: 0; }
370-
.panel-header p { color: var(--ink-dim); font-size: 0.84rem; }
392+
.panel-header h2 { font-size: 1.05rem; margin-bottom: 0; }
393+
.panel-header p { color: var(--ink-dim); font-size: 0.78rem; }
371394

372395
/* Shown only when a migration could not be applied, which is rare and worth
373396
interrupting for: the panel works, but something in it will not. */
@@ -605,22 +628,23 @@ button { font-family: inherit; }
605628

606629
/* ---------- Dashboard stats ---------- */
607630
.stat-grid {
608-
display: grid; grid-template-columns: repeat(auto-fill, minmax(158px, 1fr));
609-
gap: 10px; margin-bottom: 18px;
631+
display: grid; grid-template-columns: repeat(auto-fill, minmax(118px, 1fr));
632+
gap: 7px; margin-bottom: 12px;
610633
}
611634
.stat-grid-3 { grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); margin-bottom: 16px; }
612635
.stat-card {
613-
background: #fff; border: 1px solid var(--border); border-radius: var(--radius);
614-
padding: 11px 13px; display: flex; flex-direction: column; gap: 3px;
636+
background: #fff; border: 1px solid var(--border); border-radius: 10px;
637+
padding: 7px 10px; display: flex; flex-direction: column; gap: 1px;
638+
line-height: 1.3;
615639
}
616640
/* The plate on a counter card: top right, out of the way of the number, which
617641
is what anybody is here to read. Only the five cards that carry one -- the
618642
rest of the grid keeps its own rhythm without a hole where an icon is not. */
619643
.stat-card .ns-ico3d {
620-
position: absolute; top: 9px; right: 10px;
644+
position: absolute; top: 6px; right: 8px;
621645
transition: transform 0.22s cubic-bezier(0.22, 1, 0.36, 1);
622646
}
623-
.stat-card:has(.ns-ico3d) { position: relative; padding-right: 52px; }
647+
.stat-card:has(.ns-ico3d) { position: relative; padding-right: 44px; }
624648
.stat-card:hover .ns-ico3d { transform: translateY(-2px) scale(1.05); }
625649

626650
/* The sidebar's plates sit a hair lower than a hairline did, and brighten
@@ -635,21 +659,62 @@ button { font-family: inherit; }
635659
small, and it was already being used at 0.78rem. Shrinking it further would
636660
have made a failing contrast worse, so the label moves to --ink-dim at
637661
7.54:1. */
638-
.stat-label { font-size: 0.72rem; color: var(--ink-dim); font-weight: 600; text-transform: uppercase; letter-spacing: 0.03em; }
662+
.stat-label { font-size: 0.66rem; color: var(--ink-dim); font-weight: 600; text-transform: uppercase; letter-spacing: 0.02em; }
639663
/* margin-top:auto: grid stretches every card in a row to the tallest one, so a
640664
label that wraps to two lines ("Security deposit refunded") left its
641665
neighbours' numbers floating mid-card. Pinning the value to the bottom lines
642666
the numbers up across the row again. */
643667
.stat-value {
644-
font-size: 1.15rem; font-weight: 800; color: var(--ink);
668+
font-size: 1rem; font-weight: 800; color: var(--ink);
645669
font-variant-numeric: tabular-nums; margin-top: auto;
646670
}
647671
.stat-card-income .stat-value { color: #2F6B4F; }
648672
.stat-card-expense .stat-value { color: var(--danger); }
649673
.stat-card-balance .stat-value { color: var(--primary); }
650674

651-
.dashboard-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
652-
.dashboard-col h3 { font-size: 0.95rem; margin-bottom: 8px; }
675+
.dashboard-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
676+
677+
/* ---------- The overview, as two columns that fill the screen ----------
678+
The panel is a column: a fixed head (title, search, the six buttons) and a
679+
body that takes whatever height is left. The body's two columns scroll on
680+
their own, so the overview is exactly one screen whether there are two
681+
alerts or twenty -- and the head, with the search box in it, never scrolls
682+
away from under the cursor.
683+
684+
Only where there is height to divide. A short window and a phone get the
685+
plain stacked column back, because two 200px-tall scrolling panes side by
686+
side is a worse way to read this than one honest list. */
687+
@media (min-width: 1024px) and (min-height: 620px) {
688+
.ns-content > #panel-dashboard:not([hidden]) {
689+
display: flex;
690+
flex-direction: column;
691+
height: 100%;
692+
min-height: 0;
693+
}
694+
.dash-body {
695+
flex: 1 1 auto;
696+
min-height: 0;
697+
display: grid;
698+
grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
699+
gap: 14px;
700+
}
701+
.dash-col-main, .dash-col-side {
702+
min-height: 0;
703+
overflow-y: auto;
704+
overscroll-behavior: contain;
705+
/* Room for the scrollbar so a column that has one does not jump its
706+
contents sideways against one that does not. */
707+
padding-right: 4px;
708+
}
709+
/* Side by side the two recent lists are too narrow to read, so in the
710+
right-hand column they stack. */
711+
.dash-col-side .dashboard-cols { grid-template-columns: 1fr; }
712+
/* The last thing in a column needs no gap under it; the column's own
713+
scroll end is the edge. */
714+
.dash-col-main > *:last-child,
715+
.dash-col-side > *:last-child { margin-bottom: 0; }
716+
}
717+
.dashboard-col h3 { font-size: 0.85rem; margin-bottom: 5px; }
653718

654719
/* ---------- Finance ---------- */
655720
.finance-form {
@@ -792,10 +857,10 @@ label.finance-toggle {
792857
.modal-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 10px; margin-top: 6px; }
793858

794859
/* ---------- Dashboard: rental overview ---------- */
795-
.dashboard-subheading { font-size: 0.95rem; margin: 2px 0 10px; }
860+
.dashboard-subheading { font-size: 0.82rem; margin: 0 0 5px; }
796861
/* Was a fixed four columns. Auto-fill lets a wide screen show more and a
797862
narrow one fewer, without a breakpoint for each size. */
798-
.stat-grid-4 { grid-template-columns: repeat(auto-fill, minmax(158px, 1fr)); }
863+
.stat-grid-4 { grid-template-columns: repeat(auto-fill, minmax(112px, 1fr)); }
799864
.stat-card-deposit .stat-value { color: #8A5A00; }
800865

801866
/* ---------- Wide/tall modal variants ---------- */
@@ -813,10 +878,10 @@ label.finance-toggle {
813878
color: var(--danger); font-size: 0.85rem; font-weight: 600; margin: -6px 0 14px; min-height: 1.2em;
814879
}
815880
/* One box that finds anything, and the list it drops. */
816-
.search-wrap { position: relative; margin-bottom: 14px; }
881+
.search-wrap { position: relative; margin-bottom: 10px; }
817882
.search-box {
818-
width: 100%; padding: 11px 14px; font-size: 0.95rem;
819-
border: 1px solid var(--border); border-radius: 12px;
883+
width: 100%; padding: 8px 12px; font-size: 0.9rem;
884+
border: 1px solid var(--border); border-radius: 10px;
820885
background: var(--surface); color: var(--ink);
821886
}
822887
.search-box:focus-visible { outline: 2px solid var(--primary); outline-offset: 1px; }
@@ -843,34 +908,35 @@ label.finance-toggle {
843908
.search-empty { padding: 12px 14px; color: var(--ink-dim); font-size: 0.9rem; }
844909

845910
/* The six things started most often, each of which was three clicks away. */
846-
.quick-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 18px; }
911+
.quick-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 10px; }
847912

848913
/* The shape of the day. Apart from "Needs attention" on purpose: three
849914
pickups is not a problem, it is a morning. */
850-
.today-ops { margin-bottom: 22px; }
915+
.today-ops { margin-bottom: 12px; }
851916
.ops-grid {
852-
display: grid; gap: 10px; margin-top: 8px;
853-
grid-template-columns: repeat(auto-fit, minmax(135px, 1fr));
917+
display: grid; gap: 7px; margin-top: 6px;
918+
grid-template-columns: repeat(auto-fit, minmax(96px, 1fr));
854919
}
855920
.ops-card {
856-
border: 1px solid var(--border); border-radius: 12px;
857-
background: var(--surface); padding: 12px 14px;
858-
display: flex; flex-direction: column; gap: 2px;
921+
border: 1px solid var(--border); border-radius: 10px;
922+
background: var(--surface); padding: 7px 10px;
923+
display: flex; flex-direction: column-reverse; gap: 0;
859924
}
860-
.ops-value { font-size: 1.35rem; font-weight: 800; color: var(--ink); }
861-
.ops-label { font-size: 0.78rem; color: var(--ink-dim); }
925+
.ops-value { font-size: 1.1rem; font-weight: 800; color: var(--ink); line-height: 1.25; }
926+
.ops-label { font-size: 0.7rem; color: var(--ink-dim); line-height: 1.3; }
862927

863928
/* The amount a customer actually pays, beside the discount that made it. */
864929
.final-price { font-size: 1.25rem; font-weight: 800; color: var(--ink); margin: 6px 0 0; }
865930

866931
/* What needs doing, above the figures. Colour carries the urgency and a word
867932
carries it too, because colour alone is not a message to everyone. */
868-
.alerts { margin-bottom: 22px; }
869-
.alert-list { list-style: none; margin: 8px 0 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
933+
.alerts { margin-bottom: 12px; }
934+
.alert-list { list-style: none; margin: 6px 0 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
870935
.alert-row {
871-
display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
872-
border: 1px solid var(--border); border-left-width: 4px; border-radius: 10px;
873-
padding: 9px 12px; background: var(--surface); font-size: 0.9rem;
936+
display: flex; align-items: center; gap: 8px; flex-wrap: nowrap;
937+
border: 1px solid var(--border); border-left-width: 4px; border-radius: 9px;
938+
padding: 5px 10px; background: var(--surface); font-size: 0.84rem;
939+
line-height: 1.35;
874940
}
875941
.alert-row.is-overdue { border-left-color: var(--danger); }
876942
.alert-row.is-soon { border-left-color: var(--accent); }
@@ -1902,13 +1968,54 @@ label.finance-toggle {
19021968
overscroll-behavior: contain;
19031969
}
19041970

1971+
/* ---- a list fills the screen, and scrolls inside itself ----
1972+
With the window no longer scrolling, a fixed 72vh left dead space under
1973+
the table on a tall monitor and still cut it off on a short one. The
1974+
panel, the view and the wrapper around the list are each a column that
1975+
gives its remaining height to the one below, so the table ends where the
1976+
screen does: the filters and the pager stay put, the rows move, and the
1977+
sticky header stays over them.
1978+
1979+
:has() does the choosing, so this reaches the bookings list and the
1980+
enquiries list without either of them needing a class of its own, and
1981+
leaves the detail view -- which is a page of prose and photographs, and
1982+
wants to scroll normally -- alone. */
1983+
@media (min-width: 1024px) {
1984+
.ns-content > .admin-panel:not([hidden]):has(> div:not([hidden]) .rec-tablewrap) {
1985+
display: flex;
1986+
flex-direction: column;
1987+
height: 100%;
1988+
min-height: 0;
1989+
}
1990+
.admin-panel > div:not([hidden]):has(.rec-tablewrap) {
1991+
display: flex;
1992+
flex-direction: column;
1993+
flex: 1 1 auto;
1994+
min-height: 0;
1995+
}
1996+
.admin-panel div:not([hidden]) > .rec-tablewrap {
1997+
flex: 1 1 auto;
1998+
min-height: 140px;
1999+
max-height: none;
2000+
}
2001+
/* The wrapper the list is written into sits between the two and would
2002+
otherwise be a block of its own height. */
2003+
#bookingListWrap, #inquiriesWrap {
2004+
display: flex;
2005+
flex-direction: column;
2006+
flex: 1 1 auto;
2007+
min-height: 0;
2008+
}
2009+
.rec-pager { flex: none; }
2010+
}
2011+
19052012
/* separate, not collapse: a collapsed border belongs to the table rather than
19062013
to the cell, and a sticky cell carries its own borders with it or carries
19072014
none at all. With no vertical rules in this table the two look identical. */
1908-
.rec-table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: 0.86rem; }
2015+
.rec-table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: 0.82rem; line-height: 1.35; }
19092016
.rec-table th {
1910-
text-align: left; padding: 13px 12px; white-space: nowrap;
1911-
font-size: 0.71rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em;
2017+
text-align: left; padding: 7px 10px; white-space: nowrap;
2018+
font-size: 0.67rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em;
19122019
color: var(--ink-faint); border-bottom: 1px solid var(--border); background: var(--surface);
19132020
/* Stays on screen while the rows go past it. A column of dates with no
19142021
heading is a column of numbers. */
@@ -1918,14 +2025,14 @@ label.finance-toggle {
19182025
.rec-table th.is-sortable:hover { color: var(--ink); }
19192026
.rec-table th .ord { opacity: 0.4; margin-left: 4px; font-size: 0.8em; }
19202027
.rec-table th.is-sorted .ord { opacity: 1; color: var(--accent-deep); }
1921-
.rec-table td { padding: 14px 12px; border-bottom: 1px solid var(--border); vertical-align: middle; }
2028+
.rec-table td { padding: 5px 10px; border-bottom: 1px solid var(--border); vertical-align: middle; }
19222029
.rec-table tbody tr { cursor: pointer; }
19232030
.rec-table tbody tr:hover { background: var(--bg); }
19242031
.rec-table tbody tr:last-child td { border-bottom: 0; }
19252032
.rec-table .rec-sno { color: var(--ink-faint); width: 1%; }
19262033
.rec-table .rec-id { font-weight: 800; color: var(--accent-deep); white-space: nowrap; }
1927-
.rec-table .rec-who { font-weight: 700; color: var(--ink); }
1928-
.rec-table .rec-sub { color: var(--ink-dim); font-size: 0.82rem; }
2034+
.rec-table .rec-who { font-weight: 700; color: var(--ink); line-height: 1.25; }
2035+
.rec-table .rec-sub { color: var(--ink-dim); font-size: 0.74rem; line-height: 1.2; }
19292036
.rec-table .rec-money { font-weight: 700; white-space: nowrap; }
19302037
/* A date is one thing. Left to wrap it became "28 Sept" over "2026", which
19312038
cost the row a line for nothing. */

‎admin/dashboard.php‎

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,16 @@ function dial_select(string $id, string $selected = DIAL_DEFAULT): string
7373
<button class="btn btn-outline btn-sm" data-quick="finance">Finance</button>
7474
</div>
7575

76+
<!-- Everything below the search splits into two columns that each
77+
scroll on their own, so the overview is one screen however many
78+
alerts there are. It was a single narrow column of ten stacked
79+
sections, which on a wide monitor ran to twice the height of the
80+
screen with half the width unused. Counts on the left, because
81+
they are a fixed block you scan; what needs doing on the right,
82+
because it is the part that grows. -->
83+
<div class="dash-body">
84+
<div class="dash-col-main">
85+
7686
<!-- The shape of the day, before any of the totals. Three pickups is
7787
not a problem, it is a morning -- which is why these sit apart
7888
from "Needs attention". -->
@@ -89,14 +99,6 @@ function dial_select(string $id, string $selected = DIAL_DEFAULT): string
8999
</div>
90100
</section>
91101

92-
<!-- What needs doing, above the figures.
93-
A number tells you where the business stands; these tell you what
94-
is about to go wrong, and only one of the two has a deadline. -->
95-
<section class="alerts" id="alertsPanel" hidden>
96-
<h3 class="dashboard-subheading">Needs attention</h3>
97-
<ul class="alert-list" id="alertList"></ul>
98-
</section>
99-
100102
<div class="stat-grid">
101103
<div class="stat-card">
102104
<?= admin_icon3d('car', 34) ?>
@@ -154,10 +156,24 @@ function dial_select(string $id, string $selected = DIAL_DEFAULT): string
154156
<div class="stat-card stat-card-balance"><span class="stat-label">Net Revenue</span><span class="stat-value" id="statNetRevenue">₹0</span></div>
155157
</div>
156158

157-
<div class="dashboard-col">
158-
<h3>Active &amp; Upcoming Rentals</h3>
159-
<div id="activeUpcomingBookings"></div>
160-
</div>
159+
</div><!-- /dash-col-main -->
160+
161+
<div class="dash-col-side">
162+
<!-- What needs doing, above the figures.
163+
A number tells you where the business stands; these tell you
164+
what is about to go wrong, and only one of the two has a
165+
deadline. -->
166+
<section class="alerts" id="alertsPanel" hidden>
167+
<h3 class="dashboard-subheading">Needs attention</h3>
168+
<ul class="alert-list" id="alertList"></ul>
169+
</section>
170+
171+
<div class="dashboard-col">
172+
<h3>Active &amp; Upcoming Rentals</h3>
173+
<div id="activeUpcomingBookings"></div>
174+
</div>
175+
</div><!-- /dash-col-side -->
176+
</div><!-- /dash-body -->
161177
</section>
162178

163179
<!-- Bookings tab -->

‎build.json‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
2-
"publishedAt": "2026-10-15T08:00:27.860Z",
3-
"commit": "c6ec73a8",
4-
"commitAt": "2026-10-15T04:22:50+00:00",
5-
"sourceHash": "7bf1c69e6968ead1",
2+
"publishedAt": "2026-10-15T11:13:00.747Z",
3+
"commit": "05147656",
4+
"commitAt": "2026-10-15T08:00:46+00:00",
5+
"sourceHash": "d1a87e423f216b8e",
66
"assets": [
77
"About-DDYn5471.js",
88
"Bikes-1VLOLJDp.js",

0 commit comments

Comments
 (0)