@@ -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 : 24 px ; }
336+ . ns-content { flex : 1 ; width : 100% ; max-width : 1180px ; padding : 16 px 20 px ; }
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 : 12 px ;
361- margin-bottom : 14 px ;
383+ display : flex; justify-content : space-between; align-items : flex-end; flex-wrap : wrap; gap : 10 px ;
384+ margin-bottom : 9 px ;
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.15 rem ; margin-bottom : 0 ; }
370- . panel-header p { color : var ( --ink-dim ) ; font-size : 0.84 rem ; }
392+ . panel-header h2 { font-size : 1.05 rem ; margin-bottom : 0 ; }
393+ . panel-header p { color : var ( --ink-dim ) ; font-size : 0.78 rem ; }
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 ( 158 px , 1fr ) ) ;
609- gap : 10 px ; margin-bottom : 18 px ;
631+ display : grid; grid-template-columns : repeat ( auto-fill, minmax ( 118 px , 1fr ) ) ;
632+ gap : 7 px ; margin-bottom : 12 px ;
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 : 9 px ; right : 10 px ;
644+ position : absolute; top : 6 px ; right : 8 px ;
621645 transition : transform 0.22s cubic-bezier ( 0.22 , 1 , 0.36 , 1 ) ;
622646}
623- . stat-card : has ( . ns-ico3d ) { position : relative; padding-right : 52 px ; }
647+ . stat-card : has ( . ns-ico3d ) { position : relative; padding-right : 44 px ; }
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.72 rem ; color : var ( --ink-dim ) ; font-weight : 600 ; text-transform : uppercase; letter-spacing : 0.03 em ; }
662+ . stat-label { font-size : 0.66 rem ; color : var ( --ink-dim ) ; font-weight : 600 ; text-transform : uppercase; letter-spacing : 0.02 em ; }
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.15 rem ; font-weight : 800 ; color : var ( --ink ) ;
668+ font-size : 1 rem ; 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.95 rem ; margin : 2 px 0 10 px ; }
860+ . dashboard-subheading { font-size : 0.82 rem ; margin : 0 0 5 px ; }
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 ( 158 px , 1fr ) ) ; }
863+ . stat-grid-4 { grid-template-columns : repeat ( auto-fill, minmax ( 112 px , 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 : 14 px ; }
881+ . search-wrap { position : relative; margin-bottom : 10 px ; }
817882. search-box {
818- width : 100% ; padding : 11 px 14 px ; font-size : 0.95 rem ;
819- border : 1px solid var ( --border ) ; border-radius : 12 px ;
883+ width : 100% ; padding : 8 px 12 px ; font-size : 0.9 rem ;
884+ border : 1px solid var ( --border ) ; border-radius : 10 px ;
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 : 8 px ; margin-bottom : 18 px ; }
911+ . quick-actions { display : flex; flex-wrap : wrap; gap : 6 px ; margin-bottom : 10 px ; }
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 : 22 px ; }
915+ . today-ops { margin-bottom : 12 px ; }
851916. ops-grid {
852- display : grid; gap : 10 px ; margin-top : 8 px ;
853- grid-template-columns : repeat ( auto-fit, minmax ( 135 px , 1fr ) ) ;
917+ display : grid; gap : 7 px ; margin-top : 6 px ;
918+ grid-template-columns : repeat ( auto-fit, minmax ( 96 px , 1fr ) ) ;
854919}
855920. ops-card {
856- border : 1px solid var ( --border ) ; border-radius : 12 px ;
857- background : var ( --surface ) ; padding : 12 px 14 px ;
858- display : flex; flex-direction : column; gap : 2 px ;
921+ border : 1px solid var ( --border ) ; border-radius : 10 px ;
922+ background : var ( --surface ) ; padding : 7 px 10 px ;
923+ display : flex; flex-direction : column-reverse ; gap : 0 ;
859924}
860- . ops-value { font-size : 1.35 rem ; font-weight : 800 ; color : var ( --ink ) ; }
861- . ops-label { font-size : 0.78 rem ; color : var ( --ink-dim ) ; }
925+ . ops-value { font-size : 1.1 rem ; font-weight : 800 ; color : var ( --ink ) ; line-height : 1.25 ; }
926+ . ops-label { font-size : 0.7 rem ; 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 : 22 px ; }
869- . alert-list { list-style : none; margin : 8 px 0 0 ; padding : 0 ; display : flex; flex-direction : column; gap : 6 px ; }
933+ . alerts { margin-bottom : 12 px ; }
934+ . alert-list { list-style : none; margin : 6 px 0 0 ; padding : 0 ; display : flex; flex-direction : column; gap : 4 px ; }
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.86 rem ; }
2015+ . rec-table { width : 100% ; border-collapse : separate; border-spacing : 0 ; font-size : 0.82 rem ; line-height : 1.35 ; }
19092016. rec-table th {
1910- text-align : left; padding : 13 px 12 px ; white-space : nowrap;
1911- font-size : 0.71 rem ; font-weight : 700 ; text-transform : uppercase; letter-spacing : 0.04 em ;
2017+ text-align : left; padding : 7 px 10 px ; white-space : nowrap;
2018+ font-size : 0.67 rem ; font-weight : 700 ; text-transform : uppercase; letter-spacing : 0.03 em ;
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 : 14 px 12 px ; border-bottom : 1px solid var ( --border ) ; vertical-align : middle; }
2028+ . rec-table td { padding : 5 px 10 px ; 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.82 rem ; }
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.74 rem ; 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. */
0 commit comments