
        :root {
            /* Tell the browser which theme native controls (select popups, date
               pickers, scrollbars) should match. Without this the dropdown option list
               renders light-on-white in dark mode - invisible text. */
            color-scheme: light;
            /* THE NAV STANDS ON THE BRAND'S NAVY, not on a neutral near-black.
               #10233F is the same dark the marketing page's footer and the
               sign-in screen stand on, so the three surfaces a customer sees in
               order - the site, the door, the app - are one colour rather than
               three. It also gives the Fleet Cadence mark a ground it was drawn
               for: the mark is white and amber, and on #15181d it read as
               floating on a grey slab.

               Professional's rail is its own light grey, below. */
            --sidebar-bg: #10233F;
            --sidebar-bg-gradient: linear-gradient(180deg, #14294A 0%, #0D1D35 100%);
            --sidebar-bg-hover: #1B3155;
            --sidebar-text: #93A3BE;
            --sidebar-text-active: #FFFFFF;
            --sidebar-border: #24395C;
            /* The nav's own accent, deliberately NOT the app's indigo. Safety
               amber is the vernacular of the actual job (hi-vis, dock markings,
               van livery) and it matches the marketing site, so the product and
               the page that sells it read as one company. Scoped to the nav so
               changing it never disturbs buttons, focus rings or charts. */
            /* The nav accent FOLLOWS the colour theme. It was pinned to amber,
               so picking Terminal or Sandstone left an amber nav bolted to a
               cyan or teal app - the one thing a theme picker must not do.
               Lightened off --accent rather than used raw: every theme's sidebar
               ground is dark, and Sandstone's deep teal (#0f766e) is close to
               invisible on its own brown panel. 80% keeps the hue and guarantees
               it reads. Self-maintaining, so a ninth theme needs no nav edit. */
            /* THE NAV'S HUE IS ITS OWN TOKEN, because the page's accent is the
               wrong colour to lighten. --accent is picked to read on PAPER, so on
               Light it is the dark #9A5806 and on Professional a deep #1d4ed8 -
               and 80% of either on this navy measured 4.21:1 and 3.68:1, under
               AA on every section heading in the nav. The sidebar is the same
               navy in Light and Dark, so both take the brand's own #E8901B,
               which Dark was already using (7.61:1). Professional's rail is
               light grey, so it names its nav accent outright instead. */
            --nav-hue: #E8901B;
            /* A DSP'S OWN COLOUR (app/tools/dsp_brand.py) when it has one, set
               after the themes by _dsp_brand_style.html; the app's amber and
               its ink otherwise. The Home band, the Dashboard row and the
               public pages' band read these. */
            --dsp: #E8901B;
            --dsp-on: #14181F;
            --dsp-band: var(--dsp);
            --dsp-band-on: var(--dsp-on);
            --nav-accent: color-mix(in srgb, var(--nav-hue) 80%, #ffffff);
            --nav-accent-soft: color-mix(in srgb, var(--accent) 15%, transparent);
            /* Text on the current row, and the dark ink used on an accent plate. */
            --nav-accent-text: color-mix(in srgb, var(--accent) 45%, #ffffff);
            --nav-on-accent: color-mix(in srgb, var(--accent) 22%, #08090b);
            /* The section band: the sidebar ground, pushed darker. */
            --nav-band: color-mix(in srgb, var(--sidebar-bg) 82%, #000000);
            --nav-band-hover: color-mix(in srgb, var(--sidebar-bg) 94%, #000000);
            /* A solid fill for the Dense style's current row. Darkened off the
               accent so white text on it stays legible even on the themes whose
               accent is pale, where the raw accent would not carry it. */
            --nav-solid: color-mix(in srgb, var(--accent) 85%, #000000);
            /* The nav has its own face, held across every colour theme, because
               it is chrome rather than content. Condensed earns its place twice:
               it gives the panel a voice the system stack cannot, and it fits
               names like "Timecard compliance" on one line without shrinking
               the type to the point of being unreadable. */
            --font-nav: "Barlow Condensed", "Arial Narrow", var(--font-body);
            /* THE BRAND'S AMBER, AND IT TAKES TWO VALUES RATHER THAN ONE.
               "make the main theme Light match the home page too. Orange not
               purple."

               Indigo could be one token because it happened to work as both a
               text colour and a fill: #6366f1 measures 4.47:1 against white
               either way round. Amber cannot. The brand's #E8901B is 2.49:1 on
               white, so it is unreadable AS text, and white on it is the same
               2.49:1, so it is unreadable UNDER text. Painting the app with one
               amber would have produced links nobody can read on buttons nobody
               can read.

               So it splits the way the marketing page already splits it:

               --accent DOES BOTH JOBS, and its value is chosen so that it can.
               154 rules set it as TEXT and 45 set it as a BACKGROUND under
               white, so it has to clear 4.5:1 in both directions and on every
               ground the app paints. #9A5806 does: 5.57:1 as text on white,
               5.24:1 on the page, 4.91:1 on the zebra stripe, 4.95:1 on its own
               tint, and 5.57:1 under white text. Worst case 4.91:1, against the
               indigo it replaces, which was 4.47:1 at best and 4.13:1 on the
               zebra. #A55F07 was the first pick and was rejected here: it reads
               4.95:1 on white and looks fine, then lands at 4.37:1 on the zebra
               stripe, which is most of the tables in this app.

               --accent-gradient is the FILL amber, the marketing page's own
               #E8901B, and it carries the ink in --accent-gradient-on at 6.88:1. That is
               the same pairing as the home page's call to action and the Sign
               in button, and it is the thing that makes the two read as one
               company rather than as an indigo app with orange buttons.

               EVERY DERIVED TOKEN FOLLOWS --accent, not the fill: --nav-accent,
               --nav-solid, --nav-on-accent and --nav-accent-text are all
               color-mix off it, so the sidebar re-tints itself and no nav edit
               is needed here. */
            --accent: #9A5806;
            --accent-dark: #864C04;      /* 6.89:1 on white */
            --accent-darker: #7E4A05;    /* 7.32:1 on white */
            --accent-light: #FDF0DC;
            /* THE INK THAT SITS ON THE GRADIENT FILL. It exists because four
               places hard-coded `color: #fff` on top of --accent-gradient, and
               white on amber is the exact failure this palette has to avoid.
               Every theme defines it; Professional's is still white, because
               its fill is graphite.

               IT WAS CALLED --accent-on AND HAD TO BE RENAMED, because the
               plain -on suffix belongs to the plain token: --accent-dark-on is
               the ink on --accent-dark, so --accent-on has to be the ink on
               --accent. The gradient is a different fill and needs its own. */
            --accent-gradient-on: #2B1704;
            /* AND THE INK ON THE SOLID --accent, WHICH CANNOT BE ONE COLOUR.
               43 rules paint a solid --accent plate and set `color: #fff` on
               it, and the two themes need opposite inks: Light's #9A5806 wants
               white (5.57:1) and reads only 3.07:1 under the gradient's dark
               ink, while Dark's #E8901B wants that dark ink (6.88:1) and reads
               2.49:1 under white.

               NO SINGLE INK CAN SERVE BOTH, and it is worth writing down so
               nobody tries: white needs a ground darker than about L 0.14 and
               the dark ink needs one lighter than about L 0.37, and those two
               grounds are the two accents. So this is a token per theme, like
               --accent-dark-on beside it and for the same reason. */
            --accent-on: #ffffff;
            /* AND THE INK ON --accent-dark, WHICH IS A SEPARATE QUESTION. It is
               the pressed state of every button in the app, and the two themes
               disagree about which way it runs: on paper --accent-dark is the
               DARKER amber (#864C04) and wants white at 6.89:1, in dark it is
               the LIGHTER one (#F0A94A) and wants the ink at 8.54:1. Hard-coding
               #fff there measured 2.00:1 in the dark theme, which is a pressed
               button with no label on it - and it measured 1.99:1 before this
               repaint too, with the pale indigo that used to sit there, so this
               is an old bug being closed rather than a new one introduced. */
            --accent-dark-on: #ffffff;
            --accent-gradient: linear-gradient(135deg, #E8901B 0%, #D9820F 100%);
            --accent-gradient-hover: linear-gradient(135deg, #D9820F 0%, #C9760B 100%);
            --accent-glow: 0 0 0 3px rgba(232, 144, 27, 0.30);
            --gold: #92400e;
            --gold-on: #ffffff;          /* 7.09:1 on #92400e; see --red-on */
            --gold-light: #fde68a;
            --gold-bg: #fffbeb;
            /* White rides on this (.btn-gold): #d97706 was 3.19:1 at the light end. */
            --gold-gradient: linear-gradient(135deg, #b45309 0%, #92400e 100%);
            /* STATUS COLOURS READ AS 12px BOLD TEXT ON THEIR OWN TINTS, so they
               are held to 4.5:1 there, not just on white. #059669, #dc2626 and
               #b45309 managed 3.58, 4.41 and 4.38 on their -bg pills, and every
               "Signed", "Over 48h" and warning count in the app missed AA.
               Darker, each clears it on every tint the app paints them on - green
               the most, because it also sits on its own 16% wash (5.12:1 there);
               white on the solid fills goes up with them. */
            --green: #05654a;
            --green-on: #ffffff;         /* 7.08:1 on #05654a; see --red-on */
            --green-bg: #ecfdf5;
            /* THE STANDING COLOURS (2026-11-02: "Fantastic Plus should be Deep
               Blue, Fantastic blue, great green, anything below Red"), defined
               once for the scorecard pages. -X the fill (a ladder rung, the
               Fantastic Plus pill); -X-ink text on the card or on its own 14%
               wash, 4.5:1 or better; --tier-fplus-on the text on the solid
               Fantastic Plus pill. Fair and Poor are one red. Professional
               reads these Light values. The --ring-* four are the scorecard
               rings: muted orange, lavender, gold, mauve - none of them blue, green or
               red, so a ring is never mistaken for a standing. */
            --tier-fplus: #1B2E7B; --tier-fplus-ink: #1B2E7B; --tier-fplus-on: #FFFFFF;
            --tier-fantastic: #2F6FE0; --tier-fantastic-ink: #1D4ED8;
            --tier-great: #1E9A55; --tier-great-ink: #0B6B3A;
            --tier-red: #D6453D; --tier-red-ink: #B42318;
            --ring-safety: #EE9B5A; --ring-delivery: #9486C7; --ring-pickup: #E3BD5F; --ring-service: #C994A8;
            --red: #b91c1c;
            /* The ink on a SOLID --red (a count badge, a danger button on hover):
               white is 6.47:1 on #b91c1c. Dark overrides it - see there. */
            --red-on: #ffffff;
            --red-bg: #fef2f2;
            --orange: #c2410c;
            --orange-bg: #ffedd5;
            --yellow: #854d0e;
            --yellow-bg: #fef9c3;
            --gray: #5b6b80;             /* 4.97:1 on the grey pill; #64748b was 4.34 */
            --gray-bg: #f1f5f9;

            /* Tile palette - cycled across grid boxes (.dashboard-grid items,
               .stat-grid items) so a row/grid of cards reads as genuinely
               distinct boxes instead of the same indigo card repeated. */
            /* Tile 1 mirrors the accent by convention, so it moves with it -
               an indigo tile in an amber app is the one card that looks pasted in. */
            --tile1: #9A5806; --tile1-dark: #864C04; --tile1-bg: #FDF0DC;
            --tile2: #0d9488; --tile2-dark: #0f766e; --tile2-bg: #ecfdfb;
            --tile3: #d97706; --tile3-dark: #b45309; --tile3-bg: #fffbeb;
            --tile4: #7c3aed; --tile4-dark: #6d28d9; --tile4-bg: #f6f3ff;
            --tile5: #0284c7; --tile5-dark: #0369a1; --tile5-bg: #f0f9ff;
            /* THE HOME PAGE'S OWN GROUND AND INK, not slate. #10233F is the
               navy the marketing page sets every word in, and #F6F8FC is the
               cool paper it stands on - the blue in the paper is what makes the
               amber read as daybreak rather than as a warm editorial accent.
               --text-muted at #40506B is 8.4:1 on that paper, against the 4.8:1
               the slate it replaces managed. */
            --text: #10233F;
            --text-muted: #40506B;
            --border: #D8DEEA;
            --card-bg: #FFFFFF;
            --page-bg: #F6F8FC;
            --zebra: #EDF1F8;

            /* Aliases for names pages reached for that the palette never had.
               Every one of these was written with a hard-coded fallback after
               the comma, and an undefined token always renders that fallback -
               so they painted one light colour in every theme,
               including the dark ones. A page painting #fff7ed behind text
               coloured for a dark ground is not broken enough to throw; it just
               reads wrong, which is why they sat here.

               Defined as var(...) of a real token rather than copied values, so
               a theme that redefines the target redefines these with it. One
               place; no per-palette duplication of a token used twice. */
            --bg: var(--page-bg);
            --bg-subtle: var(--zebra);
            --surface-2: var(--card-bg);
            --card-bg-alt: var(--zebra);
            --chip-bg: var(--gray-bg);
            --code-bg: var(--gray-bg);
            --nav-muted: var(--sidebar-text);
            --ok: var(--green);
            --blue-bg: var(--tile2-bg);
            --amber: var(--gold);
            --amber-bg: var(--gold-bg);
            --amber-dark: var(--gold-light);
            /* THE "YOU ARE HERE" MARK OF A TAB ROW: the line under the current
               tab, the line under the current settings group, the focus ring
               on a tab. --gold in Light, Dusk and Dark, as it always was; a
               theme whose accent is not amber names its own (Professional:
               graphite), so no tab bar on any page has to know which theme it
               is in. */
            --tab-mark: var(--gold);
            /* THE PAGE TABS' TRACK AND THE RAISED TAB ON IT (.settings-tabs,
               the Settings groups). Mixed from the theme's own ink and page
               here, so Professional and any later light theme get a track in
               their own grey; Dusk and Dark name theirs, measured below. The
               muted ink reads 6.7:1 on Light's track. */
            --tab-track: color-mix(in srgb, var(--text) 7%, var(--page-bg));
            --tab-track-shade: color-mix(in srgb, var(--text) 6%, transparent);
            --tab-thumb: var(--card-bg);
            --tab-thumb-ring: color-mix(in srgb, var(--text) 8%, transparent);
            --tab-thumb-shadow: 0 1px 2px color-mix(in srgb, var(--text) 14%, transparent), 0 2px 6px color-mix(in srgb, var(--text) 6%, transparent);
            --tab-hover: color-mix(in srgb, var(--text) 5%, transparent);
            /* A font stack, not a colour - the same in every theme. */
            --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
            --input-bg: #ffffff;
            --radius: 10px;
            --radius-sm: 8px;
            --shadow: 0 1px 3px rgba(15, 23, 42, 0.06), 0 1px 2px rgba(15, 23, 42, 0.04);
            --shadow-lift: 0 14px 30px rgba(15, 23, 42, 0.13), 0 4px 10px rgba(76, 29, 149, 0.06);
            --transition: all 0.15s ease;
            --sidebar-width: 300px;
            /* Gap around the "floating" sidebar - it sits inset from all four
               viewport edges as a rounded card rather than flush to the left. */
            /* 0 = the nav sits flush against the viewport edge. The floating
               inset card (rounded corners, big drop shadow) is the single most
               recognisable AI-default chrome there is; being part of the window
               instead of hovering over it is most of the difference. */
            --sidebar-gap: 0rem;
            /* Type: themes swap these for a genuinely different look. Default is
               the native system UI stack (Light/Dark). */
            --font-body: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
            --font-display: var(--font-body);
            --heading-spacing: 0;

            /* THE PAGE HAS A GROUND, NOT A FLAT FILL.
               It was one radial at 6% in the top right, which is close enough
               to nothing that the page read as a slab of #f8fafc with cards on
               it. Three wide, slow blooms give it depth without becoming
               something you look AT: the cards are opaque, so this is only ever
               seen in the gutters and between blocks, which is exactly where a
               page wants some air.

               MIXED FROM THE THEME'S OWN TOKENS rather than written as literal
               colours, so it follows whichever palette is on - Modern's ground
               comes out violet and fuchsia, Sandstone's comes out warm - and a
               tenth theme needs no edit here. The percentages are the only
               thing tuned by hand, and they are tuned for a LIGHT ground;
               dark redefines them below, because a dark page swallows colour
               and the same numbers there are invisible. */
            /* LIGHT'S GROUND IS FLAT PAPER NOW, and that is the point of the
               repaint rather than a casualty of it. The wash mixed --accent
               with --tile2 (teal) and --tile4 (violet), which under indigo read
               as one cool haze and under amber reads as three arguing hues. The
               home page it is copying is flat #F6F8FC with nothing behind it.
               Dark went flat too, for the same reason - see its block below. */
            --page-ground: none;
        }

        /* ---------------- Harbor (the theme keyed "dark") ----------------
           "almost doesn't really fit the website", "something that's just
           darker than Dusk, but not so black". Four takes went up on real
           pages and the owner picked Harbor: Fleet Cadence's own navy, lifted
           well off black. The near-black slate it replaces had a page and a
           card of 5 and 10 on a 0-100 brightness scale; Harbor's page is
           #1E314D (20) and its card #253956 (24), still far darker than Dusk's
           83 and nothing like Light's 98.

           LIFTED ONCE MORE, A LITTLE: "Harbor is still just a little too dark".
           The page went from 17 to 20, a field and a card with it, the zebra a
           point and the grey pill not at all. --gray-bg is the worst ground for
           every ink below, so leaving it where it was is what let every ink,
           the amber included, stay exactly as it was.

           THE KEY STAYS "dark", and only the name a person reads is Harbor (My
           App's picker, the setup walk, the FAQ answers, What's new).
           A saved choice lives in localStorage as "dark", the boot script and
           the retired-theme map send old themes to that key, and the capture
           script and a dozen tests all say data-theme="dark". Renaming the key
           would drop everybody who picked it back onto their OS setting.

           THE SIDEBAR IS NOT PART OF THIS. It is still the brand's #10233F
           (the nav tokens above are not redefined here), a band darker than
           the #1E314D page beside it.

           Only the neutral/surface tokens and the light tinted "-bg" tokens
           flip; the accent hues carry over. Set via data-theme on <html> (see
           the head script) so there's no flash, and so it never depends on the
           OS setting once a user picks.

           FIVE GROUNDS carry text in this app: the page #1E314D, a card
           #253956, the zebra stripe #273B5D, the grey pill --gray-bg #2B3F5F
           and a form field #213552. --gray-bg is the lightest and so the worst
           ground for every ink below. Every text ink was re-measured on all
           five AND on the -bg tints it sits on (the hover row, the five status
           washes, the five tile washes, the gold plate), and each is 4.5:1 or
           better on every one of them. The worst ground is named beside each
           ink; tests/test_harbor_theme.py repeats the arithmetic. */
        :root[data-theme="dark"] {
            color-scheme: dark;
            /* HARBOR TAKES THE AMBER TOO. It never redefined the brand and
               does not start now: one brand across every theme is the point,
               and amber on navy is the home page's own pairing. The primary
               button's FILL and its ink are inherited unchanged - #2B1704 on
               #E8901B is 6.88:1 whatever is behind the button. */
            /* HARBOR IS FLAT. "i hate those random orange/blue hues on dark
               mode in the backgrounds." The page had three blooms behind it -
               amber, blue, amber - turned up because a dark page swallows
               colour. Light and Overcast dropped theirs for reading as arguing
               hues, and on a navy ground they read as stray light leaking in.
               The cards and the sidebar carry the depth on their own. */
            --page-ground: none;
            /* HARBOR CANNOT HAVE ITS CAKE, and this is worth writing down
               because the obvious fix is wrong. --accent is read as TEXT in 154
               rules and used as a BACKGROUND UNDER WHITE in 45, and on a light
               ground one amber can serve both: #9A5806 above is 5.57:1 either
               way. On #253956 the two requirements pull apart - a text amber
               has to be LIGHT to clear the ground, a fill under white has to be
               DARK - and the crossover where they meet is only 3.42:1.

               SO THE FILLS GAVE UP THE WHITE, AND THE TEXT GOT ITS COLOUR. The
               43 rules that painted white on a solid accent take --accent-on,
               which is the ink per theme, and that frees --accent to be the one
               thing it still has to be here: readable TEXT on a navy ground.

               THE BRAND'S OWN #E8901B ALMOST CLEARS IT, and the one ground it
               misses is the grey pill: 5.27:1 on the page, 4.69:1 on a card,
               4.51:1 on the zebra, but 4.26:1 on --gray-bg, which is a chip or
               a tag with amber words in it. So --accent is the same hue lifted
               a few points of lightness to #EA982B, and that is the whole
               change: 5.63:1 on the page, 5.02 on a card, 4.83 on the zebra,
               5.32 in a field and 4.56 on --gray-bg, the worst. The primary
               button does not move - it paints --accent-gradient, which this
               block does not touch - so the brand's #E8901B is still the fill
               of every call to action, and #2B1704 on the lifted plate is 7.35:1.

               THE SIDEBAR IS UNMOVED: its accent is --nav-hue, the brand's own
               #E8901B, on its own navy. The few nav tokens mixed off --accent
               (the current row's text, the ink on an accent plate) moved a hair
               with it, and the brand test measures them again.

               --accent-dark and --accent-darker go LIGHTER here, which is what
               those names have always meant in this block, and they carry the
               54 rules that set accent text on a tint. #F0A94A is 5.83:1 on a
               card and 5.30:1 on --gray-bg; #F5B25C 6.34 and 5.76. */
            --accent: #EA982B;
            --accent-on: #2B1704;        /* 7.35:1 on the plate above */
            --accent-light: #3E3420;     /* the hover row; --text on it 10.89:1, --accent 5.26:1 */
            --accent-dark: #F0A94A;      /* 5.30:1 on --gray-bg, 5.83:1 on a card */
            --accent-darker: #F5B25C;    /* 5.76:1 on --gray-bg, 6.34:1 on a card */
            /* Harbor's -dark variants are the LIGHT ones, so what sits on them
               is the ink: #2B1704 on #F0A94A is 8.54:1, where white is 2.00:1. */
            --accent-dark-on: #2B1704;
            /* Warning text. Light's dark amber #92400e is 1.77:1 on a Harbor
               card, so this theme names its own. */
            --gold: #f59e0b;             /* 4.94:1 on --gray-bg, 5.44:1 on a card */
            /* The fills under white needed an ink once --gold went light here:
               white on #f59e0b is 2.15:1, #2b1704 is 7.97:1. */
            --gold-on: #2b1704;
            --gold-gradient: linear-gradient(135deg, #b45309 0%, #92400e 100%);  /* white 5.02:1+ */
            --gold-light: #443A1A;       /* a plate, not an ink; --gold on it 5.24:1 */
            --gold-bg: #3A3218;          /* --gold on it 5.93:1 */
            --green: #34d399;            /* 5.52:1 on --gray-bg, 6.45:1 on --green-bg */
            /* White on #34d399 is 1.92:1 - an Approve button with no word on it.
               Same split as --red-on: #052e22 is 7.69:1. */
            --green-on: #052e22;
            --green-bg: #173A35;
            /* The standing colours and rings on the dark card (see :root). */
            --tier-fplus: #5068F0; --tier-fplus-ink: #A9B7FF;
            --tier-fantastic: #5AA9FF; --tier-fantastic-ink: #8DC2FF;
            --tier-great: #3FCB7E; --tier-great-ink: #6EE0A0;
            --tier-red: #FF7A70; --tier-red-ink: #FF9D95;
            --ring-safety: #F4B47F; --ring-delivery: #B0A4DD; --ring-pickup: #EBCC80; --ring-service: #DCAFC0;
            /* THE ONE STATUS INK THAT HAD TO MOVE. The palette's #f87171 reads
               4.74:1 on the page but 4.22 on a card, 4.06 on the zebra,
               3.84 on --gray-bg and 4.2 to 4.5 on seven of the eleven washes - every
               red count in a striped table was under AA. The same hue, lifted
               to #F98C8C: 5.72:1 on the page, 5.09 on a card, 4.90 on the
               zebra, 4.63 on --gray-bg (the worst) and 6.11 on --red-bg. */
            --red: #F98C8C;
            /* THE SAME SPLIT AS --accent-on. The lifted red is light so it
               reads as text on this ground, which leaves white on it at
               2.29:1 - every unread badge in the app failed. The fill keeps its
               colour and the ink goes dark: #2a0a0a is 7.99:1 on it. */
            --red-on: #2a0a0a;
            --red-bg: #3E2430;
            --orange: #fdba74;           /* 6.29:1 on --gray-bg, 7.50:1 on --orange-bg */
            --orange-bg: #40301E;
            --yellow: #fde047;           /* 8.05:1 on --gray-bg */
            --yellow-bg: #3D3818;
            --gray: #A9B6CA;             /* 5.17:1 on --gray-bg, 5.69:1 on a card */
            --gray-bg: #2B3F5F;
            /* THE TILE HUES TOO. Harbor never redefined them, so a chart line,
               a "Chat" chip or an avatar's initials painted Light's mid-tone
               hues on navy: 2 to 3.5:1 on a card. Each is the same hue lifted
               until it clears 4.5:1 on all five grounds and on every wash (the
               worst is --gray-bg, 4.61 to 4.64:1), and
               each -dark is four points lighter again, the way --accent-dark
               is lighter than --accent. Tile 1 mirrors the accent. Nothing
               paints white on a tile hue in this theme: the one rule that did,
               .dashboard-card .icon, has no markup. */
            --tile1: #EA982B; --tile1-dark: #F0A94A; --tile1-bg: #2A3555;
            --tile2: #11C0B0; --tile2-dark: #13D3C1; --tile2-bg: #1A3D44;
            --tile3: #F9921B; --tile3-dark: #FA9C2F; --tile3-bg: #3A3420;
            --tile4: #BD9BF6; --tile4-dark: #C9AEF8; --tile4-bg: #312B55;
            --tile5: #29B5FD; --tile5-dark: #3DBCFD; --tile5-bg: #1C3A58;
            --text: #EEF2F8;             /* 9.45:1 on --gray-bg, 10.39:1 on a card */
            --text-muted: #B3C0D4;       /* 5.76:1 on --gray-bg, 6.34:1 on a card */
            --border: #3D5579;           /* 1.54:1 against a card: an edge, not an ink */
            --card-bg: #253956;
            --page-bg: #1E314D;
            --zebra: #273B5D;
            --input-bg: #213552;
            /* The page tabs' track is a well below the page, the raised tab a
               step above a card. --text-muted 8.0:1 on the track, --text 8.1:1
               on the raised tab. */
            --tab-track: #172941;
            --tab-track-shade: rgba(0, 0, 0, 0.28);
            --tab-thumb: #30496D;
            --tab-thumb-ring: rgba(255, 255, 255, 0.08);
            --tab-thumb-shadow: 0 1px 2px rgba(0, 0, 0, 0.35), 0 2px 8px rgba(0, 0, 0, 0.2);
            --tab-hover: rgba(238, 242, 248, 0.07);
            --shadow: 0 1px 3px rgba(0, 0, 0, 0.35), 0 1px 2px rgba(0, 0, 0, 0.25);
            --shadow-lift: 0 14px 30px rgba(0, 0, 0, 0.4), 0 4px 10px rgba(0, 0, 0, 0.3);
        }
        /* Components with hardcoded light colors that tokens can't reach,
           written from Harbor's own tokens rather than as hex: the borders are
           the status ink mixed into the card, the gold and orange badges take
           their wash and their ink from the tokens measured above. */
        :root[data-theme="dark"] .badge-red { border-color: color-mix(in srgb, var(--red) 35%, var(--card-bg)); }
        :root[data-theme="dark"] .badge-green { border-color: color-mix(in srgb, var(--green) 35%, var(--card-bg)); }
        :root[data-theme="dark"] .badge-gold { background: var(--gold-bg); color: var(--gold); border-color: color-mix(in srgb, var(--gold) 35%, var(--card-bg)); }
        :root[data-theme="dark"] .badge-orange { background: var(--orange-bg); color: var(--orange); border-color: color-mix(in srgb, var(--orange) 35%, var(--card-bg)); }

        /* ---------------- Dusk theme ----------------
           "I also want another Color theme. We have Light, Dark. we need
           something inbetween this."

           IT WAS A DIMMED DARK, AND THAT WAS STILL TOO DARK. "I feel that our
           dusk theme is still too dark." The slate #273041 measured 20 on a
           0-100 brightness scale, with Dark at 5 and Light at 98: a dark theme,
           not an in-between one. Four lighter takes were put up side by side
           and the answer was "I like overcast".

           SO IT IS A DIMMED LIGHT: Light with the glare taken off. Soft blue-
           grey where Light has white (the page #c7cfdb, cards a step lighter at
           #d8dee7), Light's own navy text, and the brand amber taken darker
           until it reads on the grey. The middle of the scale, about 44 to 57,
           is out of bounds for any theme here: neither the app's off-white nor
           its navy reaches 4.5:1 there, so an in-between has to sit on one side
           of it, and this one sits on the light side.

           IT STILL DEFINES EVERY TOKEN DARK DEFINES. Light's values are tuned
           for white, and on this grey most of its inks slip under AA - Light's
           #9A5806 amber is 4.33:1 on a Dusk card, its red 4.27:1 on the grey
           pill. So the whole palette is restated rather than inherited.
           tests/test_the_dusk_theme.py diffs the two blocks.

           EVERY INK IS MEASURED on all five grounds this app paints - the page,
           a card, the zebra stripe, --gray-bg and an input - at 4.5:1 or
           better. --gray-bg is the darkest of them and the worst every time. */
        :root[data-theme="dusk"] {
            color-scheme: light;
            /* No blooms: Light has none, and on a grey ground they read as
               smudges rather than depth. */
            --page-ground: none;
            /* THE BRAND AMBER, TAKEN DARKER. Light's #9A5806 is 4.33:1 on this
               card. #7A4604 is the same hue: 4.80:1 on --gray-bg, 5.74:1 on a
               card, and white on it is 7.76:1, so a solid accent keeps white
               ink the way Light's does. */
            --accent: #7A4604;
            --accent-on: #ffffff;
            --accent-light: #e9dfcc;     /* the hover row; --text on it 11.91:1, --accent 5.87:1 */
            --accent-dark: #6A3C03;      /* DARKER, as in Light; 5.74:1 on --gray-bg */
            --accent-darker: #5E3503;    /* 6.53:1 */
            --accent-dark-on: #ffffff;   /* 9.29:1 on --accent-dark */
            --gold: #7a360b;             /* 5.50:1 on --gray-bg */
            --gold-on: #ffffff;
            --gold-gradient: linear-gradient(135deg, #b45309 0%, #92400e 100%);  /* white 5.02:1+ */
            --gold-light: #eadcb2;       /* a plate, not an ink */
            --gold-bg: #e6d9b4;
            --green: #04573f;            /* 5.32:1 on --gray-bg */
            --green-on: #ffffff;
            --green-bg: #bfdcd0;
            /* The standing colours and rings on the Dusk card (see :root). */
            --tier-fplus: #16276B; --tier-fplus-ink: #16276B;
            --tier-fantastic: #1E5BC6; --tier-fantastic-ink: #173F9E;
            --tier-great: #12783F; --tier-great-ink: #0A5A2E;
            --tier-red: #BF2F28; --tier-red-ink: #8F1A14;
            --ring-safety: #DC8848; --ring-delivery: #7F70B5; --ring-pickup: #D0AA4B; --ring-service: #B67F94;
            --red: #9b1818;              /* 5.11:1 on --gray-bg */
            --red-on: #ffffff;
            --red-bg: #e8c9c9;
            --orange: #8f3208;           /* 4.94:1 on --gray-bg */
            --orange-bg: #ecd3bf;
            --yellow: #664006;           /* 5.64:1 on --gray-bg */
            --yellow-bg: #e5dfb1;
            --gray: #3b4a61;             /* 5.55:1 on --gray-bg */
            --gray-bg: #c4ccd8;
            /* THE TILE HUES TOO. A stat card paints its number in its tile's
               gradient, so the hue is the text there, and Light's #d97706 or
               #0284c7 on this card is near 3:1. Each is the same hue taken
               darker until it clears 4.5:1 on a card and on its own -bg. */
            --tile1: #7A4604; --tile1-dark: #6A3C03; --tile1-bg: #e9dfcc;
            --tile2: #0b5a52; --tile2-dark: #084a43; --tile2-bg: #c5dedb;
            --tile3: #7c4a03; --tile3-dark: #6b3f02; --tile3-bg: #e8dec2;
            --tile4: #5b2bb5; --tile4-dark: #4c239a; --tile4-bg: #d8d2ec;
            --tile5: #0a4f82; --tile5-dark: #083f69; --tile5-bg: #c8daea;
            --text: #10233F;             /* Light's navy: 11.63:1 on a card */
            --text-muted: #34435c;       /* 7.38:1 on a card, 6.17:1 on --gray-bg */
            --border: #adb7c6;
            --card-bg: #d8dee7;
            --page-bg: #c7cfdb;
            --zebra: #ced5e0;
            --input-bg: #e2e7ee;
            /* The page tabs' track, a shade under the page, and a raised tab
               lighter than a card. --text-muted 5.6:1 on the track. */
            --tab-track: #BAC3CF;
            --tab-track-shade: rgba(16, 35, 63, 0.10);
            --tab-thumb: #E6EAF0;
            --tab-thumb-ring: rgba(16, 35, 63, 0.10);
            --tab-thumb-shadow: 0 1px 2px rgba(16, 35, 63, 0.18), 0 2px 6px rgba(16, 35, 63, 0.08);
            --tab-hover: rgba(16, 35, 63, 0.06);
            --shadow: 0 1px 3px rgba(16, 35, 63, 0.10), 0 1px 2px rgba(16, 35, 63, 0.07);
            --shadow-lift: 0 14px 30px rgba(16, 35, 63, 0.16), 0 4px 10px rgba(16, 35, 63, 0.08);
        }
        /* Dark's component overrides, written from Dusk's own tokens rather
           than copied as hex. Dark's are tuned for near-black and Light's
           defaults for white; routed through the tokens above, each of these
           components gets inks that were measured on Dusk's grey. */
        :root[data-theme="dusk"] .badge-red { border-color: color-mix(in srgb, var(--red) 35%, var(--card-bg)); }
        :root[data-theme="dusk"] .badge-green { border-color: color-mix(in srgb, var(--green) 35%, var(--card-bg)); }
        :root[data-theme="dusk"] .badge-gold { background: var(--gold-bg); color: var(--gold); border-color: color-mix(in srgb, var(--gold) 35%, var(--card-bg)); }
        :root[data-theme="dusk"] .badge-orange { background: var(--orange-bg); color: var(--orange); border-color: color-mix(in srgb, var(--orange) 35%, var(--card-bg)); }

        * { box-sizing: border-box; }

        body {
            font-family: var(--font-body);
            margin: 0; padding: 0; color: var(--text);
            /* LONGHANDS, NOT THE SHORTHAND, and this is load-bearing.
               `background: var(--page-bg) var(--page-ground) fixed` looks
               right and is invalid: --page-ground is THREE comma-separated
               layers, so the substitution puts a background-colour on the
               first of several layers, which the shorthand only permits on the
               last. An invalid shorthand is dropped whole, so the body ended up
               with no background at all - a white page, which is worse than the
               flat one this replaced. It rendered as if nothing had changed,
               which is how it nearly shipped. */
            background-color: var(--page-bg);
            background-image: var(--page-ground);
            background-attachment: fixed;
            line-height: 1.55; -webkit-font-smoothing: antialiased;
        }
        /* Headings pick up the theme's display face + tracking (a no-op in
           Light/Dark, where display == body). */
        h1, h2, h3, h4, h5, h6, .sidebar-brand-name {
            font-family: var(--font-display); letter-spacing: var(--heading-spacing);
        }

        a { color: var(--accent); text-decoration: none; transition: var(--transition); }
        a:hover { color: var(--accent-dark); }

        h1 { font-size: 1.5rem; font-weight: 700; color: var(--text); margin: 0 0 1rem; letter-spacing: -0.015em; }
        h2 { font-size: 1.05rem; font-weight: 700; color: var(--text); margin: 1.75rem 0 0.75rem; letter-spacing: -0.01em; }

        /* ================= Professional: graphite and white =================
           "I also want to experiment with another version of the professional
           theme, i want something different for this." Three candidates went
           up side by side - Ledger, Harbor and Graphite - and Graphite was
           picked. It replaced the navy-and-serif Professional UNDER THE SAME
           KEY, so everybody already on Professional got it with no setting
           change, and a device still holding one of the three preview keys is
           sent here by ZIPSTER_RETIRED_THEMES.

           MONOCHROME. Graphite ink for every control and mark, so the only
           colour on a screen is a status - a red count, a green tick, an amber
           warning - and it means something. The nav stands on the page's own
           grey with no panel of its own, white cards are lifted on soft
           shadows instead of outlined, and the faces are Schibsted Grotesk with
           Bitter for headings.

           THE ONE LIGHT SIDEBAR, so it restates every nav token the shell
           derives with color-mix: those lighten the hue into white for a dark
           rail, which on this grey would be close to invisible. Its component
           rules are at the END of this sheet ("Professional's components"),
           because several restate generic rules - button, th, table - at the
           same weight and only win by coming after them.

           EVERY TOKEN DARK AND DUSK DEFINE IS RESTATED, and every ink is
           measured on every ground: tests/test_the_professional_theme.py. */
        :root[data-theme="professional"] {
            color-scheme: light;
            /* The scorecard tiers and rings, Light's values: Professional is a
               light theme and these were picked for a white card. */
            --tier-fplus: #1B2E7B; --tier-fplus-ink: #1B2E7B; --tier-fplus-on: #FFFFFF;
            --tier-fantastic: #2F6FE0; --tier-fantastic-ink: #1D4ED8;
            --tier-great: #1E9A55; --tier-great-ink: #0B6B3A;
            --tier-red: #D6453D; --tier-red-ink: #B42318;
            --ring-safety: #EE9B5A; --ring-delivery: #9486C7; --ring-pickup: #E3BD5F; --ring-service: #C994A8;
            --font-body: "Schibsted Grotesk", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
            --font-display: "Bitter", Georgia, "Times New Roman", serif;
            --font-nav: "Schibsted Grotesk", -apple-system, "Segoe UI", sans-serif;
            --heading-spacing: -0.01em;
            --page-ground: none;
            /* The rail is the page's own grey. Sidebar text 7.01:1 on it,
               6.26:1 on a hovered row. */
            --sidebar-bg: #E9ECEF;
            --sidebar-bg-gradient: linear-gradient(180deg, #E9ECEF 0%, #E9ECEF 100%);
            --sidebar-bg-hover: #DCE0E4;
            --sidebar-text: #474F58;
            --sidebar-text-active: #14181C;
            --sidebar-border: #D3D8DD;
            /* Named outright rather than derived: graphite on the grey rail is
               10.94:1, and white on a graphite plate 12.98:1. */
            --nav-hue: #2B3239;
            --nav-accent: #2B3239;
            --nav-accent-soft: #D6DBE0;
            --nav-accent-text: #14181C;
            --nav-on-accent: #FFFFFF;
            --nav-band: #E2E6EA;
            --nav-band-hover: #DCE0E4;
            --nav-solid: #2B3239;
            /* Graphite is text and fill at once: 12.98:1 as text on a card,
               and white on it the same. */
            --accent: #2B3239;
            --accent-dark: #1E2329;
            --accent-darker: #161A1F;
            --accent-light: #E4E8EC;
            --accent-on: #FFFFFF;
            --accent-dark-on: #FFFFFF;   /* 15.82:1 on #1E2329 */
            --accent-gradient: linear-gradient(180deg, #2B3239 0%, #22282E 100%);
            --accent-gradient-hover: linear-gradient(180deg, #1E2329 0%, #161A1F 100%);
            /* The fill is graphite, so its ink is white: 12.98:1. */
            --accent-gradient-on: #FFFFFF;
            --accent-glow: 0 0 0 3px rgba(43, 50, 57, 0.2);
            --gold: #8A3C0D;
            /* Amber is a status here, not where you are: the current tab is
               marked in graphite, on every tab bar (--tab-mark, above). */
            --tab-mark: var(--accent);
            --gold-on: #FFFFFF;
            --gold-light: #FBE7B5;
            --gold-bg: #FDF6E3;
            --gold-gradient: linear-gradient(135deg, #b45309 0%, #92400e 100%);
            --green: #0B6239;
            --green-on: #FFFFFF;
            --green-bg: #E5F3EA;
            --red: #A61B1B;
            --red-on: #FFFFFF;
            --red-bg: #FCEDED;
            --orange: #A3380B;           /* the worst ink here: 5.66:1 on the page */
            --orange-bg: #FCEBDD;
            --yellow: #774508;
            --yellow-bg: #FBF4D2;
            --gray: #4B535B;
            --gray-bg: #EBEEF1;
            /* Calm, muted category hues: the tiles and the chart lines (the
               dashboard draws Received in tile 5 and Executed in tile 3). */
            --tile1: #2B3239; --tile1-dark: #1E2329; --tile1-bg: #E4E8EC;
            --tile2: #1F6267; --tile2-dark: #184F53; --tile2-bg: #E1EFF0;
            --tile3: #885000; --tile3-dark: #6F4100; --tile3-bg: #F8EEDD;
            --tile4: #634A86; --tile4-dark: #503B6D; --tile4-bg: #EEEAF4;
            --tile5: #2C5A88; --tile5-dark: #234970; --tile5-bg: #E4ECF4;
            --text: #14181C;             /* 15.04:1 on the page, 17.84:1 on a card */
            --text-muted: #4B535B;       /* 7.81:1 on a card, 6.71:1 on --gray-bg */
            --border: #D6DADF;
            --card-bg: #FFFFFF;
            --page-bg: #E9ECEF;
            --zebra: #F5F6F8;
            --input-bg: #FFFFFF;
            /* The page tabs: a grey track a step under the page and a white key
               on it, the raised tab. --text-muted 5.7:1 on the track. */
            --tab-track: #DADDE0;
            --tab-track-shade: rgba(20, 24, 28, 0.06);
            --tab-thumb: #FFFFFF;
            --tab-thumb-ring: rgba(20, 24, 28, 0.08);
            --tab-thumb-shadow: 0 1px 2px rgba(20, 24, 28, 0.12), 0 2px 6px rgba(20, 24, 28, 0.06);
            --tab-hover: rgba(20, 24, 28, 0.05);
            --radius: 9px;
            --radius-sm: 7px;
            --shadow: 0 1px 2px rgba(20, 24, 28, 0.06), 0 3px 10px rgba(20, 24, 28, 0.06);
            --shadow-lift: 0 12px 30px rgba(20, 24, 28, 0.14), 0 3px 8px rgba(20, 24, 28, 0.07);
        }
        /* The flat page, with no glow behind it. */
        :root[data-theme="professional"] body { background: var(--page-bg); }
        /* NOT ON A PHONE. backdrop-filter is per-element GPU work, and these
           are the screens with the least of it and the most to lose - a
           dispatcher's laptop can afford eleven blurred panes on a board, a
           driver's phone on a dock scanning parcels cannot. The colour, the
           rim and the ground all stay; only the blur is dropped, and the pane
           is thickened so the text has the same ground to sit on. */

        /* ================= The two nav layouts =================
           "I want a B and D toggle for top bar/side bar nav in the preferences."
           A preference (data-nav on <html>, set pre-paint in <head>),
           independent of the colour theme, and desktop only: a phone has the
           tab bar and the More sheet whichever is picked.

           BOTH SHOW ONE SECTION AT A TIME. The nav is ten sections, not forty
           rows, and what is on screen is one section's pages - the one holding
           this page, marked .is-shown on the server (app/tools/nav_sections.py),
           or the one somebody has just picked. Every section stays open, so
           the markup is a plain full list to anything without these rules.

           SIDE BAR ("side", the default): a 76px rail of section squares down
           the left, and the shown section's pages in a panel beside it. The
           panel pushes the page rather than covering it - it is where you
           work from, not a menu that gets in the way - and pressing the shown
           square folds it away (html.nav-collapsed, remembered).

           TOP BAR ("top"): the sections as pills across a bar, and the shown
           section's pages as a row of tabs under it. The page gets the full
           width.

           Tokens: the rail and the bar stand on --sidebar-bg, which is dark in
           every theme; the panel and the tab row stand on --card-bg, like the
           page, so a section's pages read as part of it. */
        /* THE WHOLE SCREEN SCALES TO THE MONITOR. "The side nav is too large on
           smaller monitors, it needs to scale. My zoom is on 100% on my work
           laptop." and "Same with the loadout board." Nearly everything is
           sized in rem, so one number scales the lot: text, controls, the
           space round them, and the board's container breakpoints (72rem is
           1008px at 14px, so a laptop gets the one-line board a big monitor
           gets). A laptop window is 1280 to 1536px wide and 600 to 750px
           tall; 15px is a browser at 94% and 14px one at 88%. Only where
           there is a mouse (a tablet's fingers keep their full-size targets)
           and only above the phone layout. */
        @media (min-width: 901px) and (max-width: 1600px) and (hover: hover) and (pointer: fine) { html { font-size: 15px; } }
        @media (min-width: 901px) and (max-width: 1440px) and (hover: hover) and (pointer: fine) { html { font-size: 14px; } }
        :root { --rail-w: 76px; --panel-w: 236px; --tabrow-h: 2.85rem; }
        .nav-search-btn, .nav-roxxy-btn, .nav-whatsnew-btn { display: inline-flex; align-items: center; gap: 0.4rem; }
        /* What's new's dot: amber on the menu's own ground, ringed in it so it
           reads on the icon at any size. */
        .nav-whatsnew-ico { position: relative; display: inline-block; line-height: 0; }
        .nav-whatsnew-dot {
            position: absolute; top: -3px; right: -4px; width: 9px; height: 9px; border-radius: 50%;
            background: var(--nav-accent); box-shadow: 0 0 0 2px var(--sidebar-bg); pointer-events: none;
        }
        .topbar-whatsnew { position: relative; }
        .topbar-whatsnew .nav-whatsnew-dot { top: 4px; right: 4px; background: var(--accent-gradient); box-shadow: 0 0 0 2px var(--accent-light); }
        .nav-whatsnew-btn { text-decoration: none; }
        @media (min-width: 901px) {
            /* ---- side bar: the rail ------------------------------------- */
            html:not([data-nav="top"]) { --sidebar-width: var(--rail-w); }
            html.nav-has-panel:not([data-nav="top"]):not(.nav-collapsed) {
                --sidebar-width: calc(var(--rail-w) + var(--panel-w));
            }
            /* The panel is position:fixed inside the rail, and a transform on
               the rail would make the rail its containing block - clipping
               the panel to 76px. The old collapse slid the whole aside away
               with one, so it is switched off here rather than trusted. */
            html:not([data-nav="top"]) .sidebar {
                width: var(--rail-w); transform: none !important;
                overflow-x: hidden; overflow-y: auto; scrollbar-width: none;
            }
            html:not([data-nav="top"]) .sidebar-head { border-bottom: none; padding: var(--rs-head, 0.7rem) 0 0.3rem; }
            html:not([data-nav="top"]) .sidebar-head-row { justify-content: center; }
            /* The mark alone: there is no room for the DSP's name at 76px, and
               the panel carries the section's name instead. */
            html:not([data-nav="top"]) .sidebar-brand {
                flex: 0 0 auto; justify-content: center; gap: 0; padding: 0.2rem;
            }
            html:not([data-nav="top"]) .sidebar-brand-text { display: none; }
            /* In the 76px rail there is room for the logo's height and not much more. */
            html:not([data-nav="top"]) .sidebar-brand-icon.brand-logo-own { max-width: 3.4rem; }
            /* THE SQUARES SCROLL, YOU STAY PUT. Scorecard became a section of
               its own and the vehicle pages two (Fleet and Repairs), which is
               two more squares - and the rail was already
               a hair too tall for 1280x720 with a Pinned square, so the whole
               rail scrolled and the signed-in block at its foot (Log out with
               it) slid off the bottom. Now only the squares scroll, between
               Search and Ask Roxxy above and you below, which both stay where
               they are. The scrollbar stays hidden like the rail's own; the
               edge that has more past it shows a shadow (.nav-more-above /
               .nav-more-below, set by the script after the nav). The min-height
               keeps two squares on a very short window, where the whole rail
               scrolls again as it did before. Most of the space above the first
               square is margin rather than padding, so a square scrolled up
               stops short of Ask Roxxy instead of running into it. */
            html:not([data-nav="top"]) .sidebar-nav {
                flex: 1 1 auto; min-height: 7.5rem; display: flex; flex-direction: column; gap: 0.15rem;
                margin-top: 0.3rem; padding: 0.05rem 0.4rem 0.6rem;
                overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain;
                scrollbar-width: none;
            }
            html:not([data-nav="top"]) .sidebar-nav::-webkit-scrollbar { display: none; }
            html:not([data-nav="top"]) .sidebar-nav > details.sidebar-section { flex: none; }
            html:not([data-nav="top"]) .sidebar.nav-more-above .sidebar-icon-actions {
                box-shadow: 0 10px 10px -10px rgba(0, 0, 0, 0.55);
            }
            html:not([data-nav="top"]) .sidebar.nav-more-below .sidebar-user {
                box-shadow: 0 -10px 10px -10px rgba(0, 0, 0, 0.55);
            }
            html:not([data-nav="top"]) details.sidebar-section { margin: 0; }
            /* One square per section: its icon over its name. The name stays -
               a rail you have to hover square by square to read is a worse
               sidebar than the one it replaced. */
            html:not([data-nav="top"]) details.sidebar-section > summary,
            html:not([data-nav="top"]) :is(.nav-search-btn, .nav-whatsnew-btn) {
                display: flex; flex-direction: column; align-items: center; justify-content: center;
                gap: 0.25rem; width: 100%; box-sizing: border-box;
                min-height: var(--rs-sq, 3.35rem); margin: 0; padding: var(--rs-pad, 0.45rem) 0.1rem;
                border: none; border-radius: 10px; background: transparent; box-shadow: none;
                font-family: var(--font-body); font-size: max(var(--rs-fs, 0.64rem), 9.5px); font-weight: 600;
                line-height: 1.15; letter-spacing: 0.01em; text-transform: none;
                text-align: center; overflow-wrap: anywhere; color: var(--sidebar-text);
            }
            html:not([data-nav="top"]) details.sidebar-section > summary::after { content: none; }
            html:not([data-nav="top"]) .sidebar-section-caret { display: none; }
            html:not([data-nav="top"]) .sidebar-section-ico { display: block; line-height: 0; position: relative; }
            html:not([data-nav="top"]) .sidebar-section-ico .ui-ico,
            html:not([data-nav="top"]) :is(.nav-search-btn, .nav-whatsnew-btn) .ui-ico {
                width: var(--rs-ico, 1.3rem); height: var(--rs-ico, 1.3rem); opacity: 0.8; vertical-align: 0;
            }
            html:not([data-nav="top"]) details.sidebar-section > summary:hover,
            html:not([data-nav="top"]) :is(.nav-search-btn, .nav-whatsnew-btn):hover {
                background: var(--sidebar-bg-hover); color: var(--sidebar-text-active);
                box-shadow: none; transform: none;
            }
            html:not([data-nav="top"]) :is(.nav-search-btn, .nav-whatsnew-btn):active { background: var(--sidebar-bg-hover); color: var(--sidebar-text-active); }
            html:not([data-nav="top"]) .nav-whatsnew-btn { margin-top: 0.25rem; }
            html:not([data-nav="top"]) .nav-whatsnew-btn.is-here { background: var(--sidebar-bg-hover); color: var(--sidebar-text-active); }
            html:not([data-nav="top"]) .nav-whatsnew-btn.is-here .ui-ico { color: var(--nav-accent); opacity: 1; }
            html:not([data-nav="top"]) details.sidebar-section.is-shown > summary {
                background: var(--sidebar-bg-hover); color: var(--sidebar-text-active);
                box-shadow: inset 0 0 0 1px var(--sidebar-border);
            }
            html:not([data-nav="top"]) details.sidebar-section.is-shown .sidebar-section-ico .ui-ico {
                color: var(--nav-accent); opacity: 1;
            }
            html:not([data-nav="top"]) .sidebar-icon-actions { display: block; margin: 0.15rem 0.4rem 0; }

            /* ---- side bar: the panel ------------------------------------ */
            html:not([data-nav="top"]) .sidebar-section-body { display: none; }
            html.nav-has-station:not([data-nav="top"]) { --panel-top: 2.6rem; }
            html:not([data-nav="top"]):not(.nav-collapsed) details.sidebar-section.is-shown > .sidebar-section-body {
                display: flex; flex-direction: column; gap: 0.1rem;
                position: fixed; top: var(--panel-top, 0rem); bottom: 0; left: var(--rail-w);
                width: var(--panel-w); box-sizing: border-box; padding: 0 0.7rem 1rem;
                overflow-y: auto; background: var(--card-bg);
                border-right: 1px solid var(--border); z-index: 29;
            }
            /* The section's name heads its panel. */
            html:not([data-nav="top"]) details.sidebar-section.is-shown > .sidebar-section-body::before {
                content: attr(data-title); display: block; flex: none;
                padding: 1.05rem 2.4rem 0.7rem 0.55rem;
                font-family: var(--font-display); font-size: 1.1rem; font-weight: 700;
                letter-spacing: -0.01em; color: var(--text);
            }
            html:not([data-nav="top"]) .sidebar-section-body a {
                flex: none; margin: 0; padding: 0.5rem 0.6rem; gap: 0.65rem;
                border: none; border-radius: 8px; color: var(--text);
                font-size: 0.93rem; letter-spacing: 0.01em;
            }
            html:not([data-nav="top"]) .sidebar-section-body a .ui-ico { color: var(--text-muted); opacity: 1; }
            html:not([data-nav="top"]) .sidebar-section-body a:hover { background: var(--gray-bg); color: var(--text); }
            html:not([data-nav="top"]) .sidebar-section-body a:hover .ui-ico { color: var(--accent-dark); }
            html:not([data-nav="top"]) .sidebar-section-body a.active {
                background: var(--accent-light); color: var(--accent-dark); font-weight: 700;
            }
            html:not([data-nav="top"]) .sidebar-section-body a.active .ui-ico { color: var(--accent-dark); }
            /* RICHER ROWS. "for the options, I like 3. we need different quick
               actions though. copy week doesnt make sense." The panel runs the
               window's full height, and a section of four pages left most of
               it blank. So each page is a row of two lines - its name, and
               under it what the page is for (.nav-desc) - with its icon on a
               small tile to the left and a count that is waiting at the right
               (the unread badge, the padlock, a pin's cross); and the section
               ends in a few Quick actions (.nav-quick). The name is still the
               row's first words, so it is still what a screen reader says
               first. Placed on a grid rather than wrapped in new markup, so the
               row is the same anchor every other reader of the nav reads: the
               name is the one thing not placed, and falls into the first free
               cell, beside the tile. A row with no line under it (a pin, a
               DSP's own page) keeps its tile on the one line it has. */
            html:not([data-nav="top"]):not(.nav-collapsed) details.sidebar-section.is-shown > .sidebar-section-body { gap: 0.15rem; }
            html:not([data-nav="top"]) .sidebar-section-body > a {
                display: grid; grid-template-columns: 2rem minmax(0, 1fr) auto;
                align-items: center; column-gap: 0.7rem; row-gap: 0.1rem;
                padding: 0.45rem 0.55rem; white-space: normal; line-height: 1.2; font-size: 0.92rem;
            }
            html:not([data-nav="top"]) .sidebar-section-body > a > :is(svg, .nav-messenger-ico) {
                grid-column: 1; grid-row: 1 / span 2;
                display: flex; align-items: center; justify-content: center;
                width: 2rem; height: 2rem; box-sizing: border-box; padding: 0.48rem;
                border-radius: 9px; background: var(--gray-bg); transition: background 0.12s ease, box-shadow 0.12s ease;
            }
            html:not([data-nav="top"]) .sidebar-section-body > a > .nav-messenger-ico { padding: 0; font-size: 1rem; }
            html:not([data-nav="top"]) .sidebar-section-body > a > .nav-messenger-ico .ui-ico { width: 1.05rem; height: 1.05rem; flex: none; vertical-align: 0; }
            html:not([data-nav="top"]) .sidebar-section-body > a:not(:has(> .nav-desc)) > :is(svg, .nav-messenger-ico) { grid-row: 1; }
            html:not([data-nav="top"]) .sidebar-section-body > a:hover > :is(svg, .nav-messenger-ico) { background: var(--card-bg); }
            html:not([data-nav="top"]) .sidebar-section-body > a.active > :is(svg, .nav-messenger-ico) {
                background: var(--card-bg); box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent) 30%, transparent);
            }
            html:not([data-nav="top"]) .sidebar-section-body > a > .nav-desc {
                display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
                grid-column: 2; grid-row: 2;
                font-family: var(--font-body); font-size: 0.74rem; font-weight: 400; line-height: 1.25;
                letter-spacing: 0; color: var(--text-muted);
            }
            html:not([data-nav="top"]) .sidebar-section-body > a.active > .nav-desc {
                color: color-mix(in srgb, var(--accent-dark) 78%, var(--text-muted));
            }
            html:not([data-nav="top"]) .sidebar-section-body > a > :is(.nav-chat-badge, .nav-lock) {
                grid-column: 3; grid-row: 1; justify-self: end; margin: 0;
            }
            /* The Quick actions: a rule, a small heading, then the buttons,
               quiet like the page's own secondary buttons - the first, the one
               that adds something, tinted. Each is as wide as its words with
               room either side, and a line fills out to the panel's edge.
               "new schedule needs to be moved a bit its too close to the right
               edge": two fixed columns squeezed the longer labels against their
               own borders and ran the right one to the panel's edge. */
            html:not([data-nav="top"]) .sidebar-section-body > .nav-quick {
                display: flex; flex-wrap: wrap; gap: 0.45rem; flex: none;
                margin: 1.1rem 0.6rem 0 0.15rem; padding-top: 1rem; border-top: 1px solid var(--border);
            }
            html:not([data-nav="top"]) .nav-quick-h {
                flex: 0 0 100%; margin: 0 0.3rem 0.2rem;
                font-family: var(--font-body); font-size: 0.7rem; font-weight: 700; line-height: 1.2;
                letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-muted);
            }
            /* A GROUP'S LABEL inside a section (Scorecard & Coaching: Scorecard,
               then Coaching), set like the Quick actions heading so the two read
               as one family. A label after rows gets room above it, so the
               groups part where one ends rather than with a rule. */
            html:not([data-nav="top"]) .sidebar-section-body > .nav-group-h {
                display: block; flex: none; margin: 0.35rem 0.55rem 0.2rem;
                font-family: var(--font-body); font-size: 0.7rem; font-weight: 700; line-height: 1.2;
                letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-muted);
            }
            html:not([data-nav="top"]) .sidebar-section-body > a ~ .nav-group-h { margin-top: 0.9rem; }
            html:not([data-nav="top"]) .sidebar-section-body .nav-quick a {
                display: flex; align-items: center; justify-content: center; gap: 0.4rem; min-width: 0;
                flex: 1 1 auto; max-width: 100%;
                margin: 0; padding: 0.5rem 0.75rem; box-sizing: border-box;
                border: 1px solid var(--border); border-radius: 8px; background: var(--card-bg); color: var(--text);
                font-family: var(--font-body); font-size: 0.76rem; font-weight: 600; line-height: 1.2;
                letter-spacing: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
            }
            html:not([data-nav="top"]) .sidebar-section-body .nav-quick a .ui-ico {
                flex: none; width: 0.95rem; height: 0.95rem; opacity: 1; vertical-align: 0; color: var(--text-muted);
            }
            html:not([data-nav="top"]) .sidebar-section-body .nav-quick a:hover {
                background: var(--gray-bg); color: var(--text); border-color: var(--border);
            }
            html:not([data-nav="top"]) .sidebar-section-body .nav-quick a.is-primary {
                background: var(--accent-light); color: var(--accent-dark);
                border-color: color-mix(in srgb, var(--accent) 35%, transparent);
            }
            html:not([data-nav="top"]) .sidebar-section-body .nav-quick a.is-primary:hover {
                background: color-mix(in srgb, var(--accent) 18%, var(--accent-light));
            }
            html:not([data-nav="top"]) .sidebar-section-body .nav-quick a:is(.is-primary, :hover) .ui-ico { color: var(--accent-dark); }
            html:not([data-nav="top"]) .sidebar-section-body .nav-quick a:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
            /* Where you are, on the top line of the panel - it is the one
               control in the header block. The one-station line goes when the
               panel does; any square brings both back.

               THE PICKER NO LONGER GOES WITH IT. Folding the panel used to
               leave no sign of which station the page was showing, so with no
               panel the pill becomes a square of the rail under the mark - the
               pin over the code, on the rail's own ground, like the section
               squares below it. 76px holds a code; the station's name is in
               the list it opens. The rail is the nav's ground, not paper, so
               the pill takes the nav's tokens here (and on the top bar). */
            html:not([data-nav="top"]) .sidebar-head .station-static { display: none; }
            html:not([data-nav="top"]) .sidebar-head .station-switch { margin: 0.35rem 0.4rem 0; }
            html:not([data-nav="top"]):is(:not(.nav-has-panel), .nav-collapsed) .sidebar-head .station-switch-pill {
                --stn-bg: var(--sidebar-bg-hover); --stn-ink: var(--sidebar-text-active); --stn-ico: var(--nav-accent);
                --stn-rim: color-mix(in srgb, var(--nav-accent) 55%, transparent); --stn-rim-on: var(--nav-accent);
                flex-wrap: wrap; justify-content: center; gap: 0.2rem 0.15rem;
                width: 100%; height: auto; padding: var(--rs-pad, 0.45rem) 0.15rem; border-radius: 10px;
                font-size: 0.8rem; white-space: normal; text-align: center;
            }
            /* The chevron rides beside the pin and the words get the line
               under them, so the longest label still takes one line. */
            html:not([data-nav="top"]):is(:not(.nav-has-panel), .nav-collapsed) .sidebar-head .station-switch-ico {
                width: var(--rs-ico, 1.3rem); height: var(--rs-ico, 1.3rem);
            }
            html:not([data-nav="top"]):is(:not(.nav-has-panel), .nav-collapsed) .sidebar-head .station-switch-now {
                order: 1; flex-basis: 100%;
            }
            html:not([data-nav="top"]):is(:not(.nav-has-panel), .nav-collapsed) .sidebar-head .station-switch-name { display: none; }
            html:not([data-nav="top"]):is(:not(.nav-has-panel), .nav-collapsed) .sidebar-head .station-switch-caret {
                width: 0.75rem; height: 0.75rem;
            }
            html.nav-has-panel:not([data-nav="top"]):not(.nav-collapsed) .sidebar-head :is(.station-switch, .station-static) {
                display: flex; align-items: center; position: fixed; z-index: 31;
                top: 0; left: var(--rail-w); width: var(--panel-w); height: var(--panel-top);
                box-sizing: border-box; margin: 0; padding: 0.35rem 2.6rem 0 1.25rem;
                background: var(--card-bg); border-right: 1px solid var(--border);
                color: var(--text-muted);
            }
            /* The pill's edge lines up with the panel's rows under it. */
            html.nav-has-panel:not([data-nav="top"]):not(.nav-collapsed) .sidebar-head .station-switch { padding-left: 0.7rem; }
            /* The fold arrow sits on the panel it folds. */
            html:not([data-nav="top"]) .sidebar-head .sidebar-collapse { display: none; }
            html.nav-has-panel:not([data-nav="top"]):not(.nav-collapsed) .sidebar-head .sidebar-collapse {
                display: inline-flex; position: fixed; z-index: 32; margin: 0;
                top: 0.55rem; left: calc(var(--rail-w) + var(--panel-w) - 2.5rem);
                color: var(--text-muted); background: transparent; border: none; opacity: 1;
            }
            html:not([data-nav="top"]) .sidebar-head .sidebar-collapse:hover { color: var(--accent-dark); background: var(--gray-bg); }

            /* ---- side bar: you, at the foot of the rail ----------------- */
            html:not([data-nav="top"]) .sidebar-user {
                flex-direction: column; align-items: stretch; gap: var(--rs-gap, 0.3rem);
                padding: var(--rs-user, 0.55rem) 0.4rem var(--rs-user, 0.7rem);
            }
            html:not([data-nav="top"]) .sidebar-user > span { display: none; }
            /* YOU, AS A SQUARE OF THE RAIL: the face over the first name and
               role, set like the section squares above (a face where they have
               an icon), because a rail you have to hover to read is the thing
               those squares were built not to be. 76px holds a first name and
               a role at this size; anything longer is cut with an ellipsis
               and is whole in the tooltip and the label. */
            html:not([data-nav="top"]) .nav-me {
                flex-direction: column; justify-content: center; gap: var(--rs-gap, 0.3rem);
                padding: var(--rs-pad, 0.45rem) 0.1rem var(--rs-pad, 0.4rem); text-align: center;
            }
            html:not([data-nav="top"]) .nav-me-text { align-items: center; width: 100%; }
            html:not([data-nav="top"]) .nav-me-av { width: var(--rs-av, 2.1rem); height: var(--rs-av, 2.1rem); }
            html:not([data-nav="top"]) .nav-me-name { max-width: 100%; font-size: max(var(--rs-name, 0.68rem), 9.5px); }
            html:not([data-nav="top"]) .nav-me-role { max-width: 100%; font-size: max(0.6rem, 9.5px); }
            html:not([data-nav="top"]) .nav-me-as {
                flex-direction: column; align-items: center; max-width: 100%;
                box-sizing: border-box; padding: 0.15rem 0.2rem; font-size: 0.58rem;
            }
            html:not([data-nav="top"]) .nav-me-as-v {
                display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
                overflow: hidden; max-width: 100%;
            }
            html:not([data-nav="top"]) .sidebar-user-acts { flex-direction: column; align-items: stretch; gap: var(--rs-gap, 0.3rem); }
            html:not([data-nav="top"]) .sidebar-user-app,
            html:not([data-nav="top"]) .sidebar-user button {
                width: 100%; box-sizing: border-box; padding: var(--rs-btn, 0.4rem) 0.1rem;
                font-size: max(var(--rs-fs, 0.64rem), 9.5px); text-align: center;
            }
            html:not([data-nav="top"]) .sidebar-footer { display: none; }

            /* ---- top bar ------------------------------------------------ */
            html[data-nav="top"] .app-shell { flex-direction: column; }
            /* Sticky: a bar you have to scroll back up to reach is worse than
               a sidebar, and the boards this layout is for are the long ones.
               `inset` clears the sidebar's offsets and would reset `top` too,
               so `top: 0` comes after it. */
            html[data-nav="top"] .sidebar {
                position: sticky; inset: auto; top: 0; width: 100%; height: auto; align-self: stretch;
                flex-direction: row; flex-wrap: wrap; align-items: center; gap: 0.35rem 0.5rem;
                padding: 0.45rem 1rem 0.45rem 0.9rem; box-sizing: border-box;
                border-right: none; border-bottom: 1px solid var(--sidebar-border);
                overflow: visible; transform: none !important; z-index: 30;
            }
            html[data-nav="top"].nav-has-panel .sidebar {
                padding-bottom: calc(0.45rem + var(--tabrow-h));
                border-bottom-color: var(--border);
            }
            /* An anchor lands below this bar, not under it (see the phone's
               value beside .topbar): 3.2rem of bar, plus the row of tabs
               when a section is open, and a little air. */
            html[data-nav="top"] { scroll-padding-top: 4.5rem; }
            html[data-nav="top"].nav-has-panel { scroll-padding-top: calc(4.5rem + var(--tabrow-h)); }
            html[data-nav="top"] .app-main { margin-left: 0; }
            html[data-nav="top"] .sidebar-head {
                order: 1; flex-direction: row; align-items: center; gap: 0.7rem;
                border-bottom: none; padding: 0; margin-right: 0.4rem;
            }
            html[data-nav="top"] .sidebar-brand { padding: 0.2rem 0; font-size: 0.95rem; }
            html[data-nav="top"] .sidebar-collapse { display: none; }
            html[data-nav="top"] :is(.station-switch, .station-static) { margin: 0; padding-left: 0; }
            /* Beside the DSP's name, the height of the section pills, and
               on the bar's ground like the rail's square. */
            html[data-nav="top"] .sidebar-head .station-switch-pill {
                --stn-bg: var(--sidebar-bg-hover); --stn-ink: var(--sidebar-text-active); --stn-ico: var(--nav-accent);
                --stn-rim: color-mix(in srgb, var(--nav-accent) 55%, transparent); --stn-rim-on: var(--nav-accent);
                height: 2.3rem; padding: 0 0.85rem 0 0.7rem; font-size: 0.95rem;
            }
            html[data-nav="top"] .sidebar-nav {
                order: 2; flex: 1 1 auto; min-width: 0; padding: 0;
                display: flex; flex-wrap: wrap; align-items: center; gap: 0.2rem;
            }
            html[data-nav="top"] details.sidebar-section { margin: 0; }
            /* One pill per section. */
            html[data-nav="top"] details.sidebar-section > summary {
                display: inline-flex; align-items: center; gap: 0.45rem;
                height: 2.3rem; padding: 0 0.85rem; border-radius: 999px;
                font-family: var(--font-body); font-size: 0.86rem; font-weight: 600;
                letter-spacing: 0; text-transform: none; white-space: nowrap;
                color: var(--sidebar-text);
            }
            html[data-nav="top"] details.sidebar-section > summary::after { content: none; }
            html[data-nav="top"] .sidebar-section-caret { display: none; }
            html[data-nav="top"] .sidebar-section-ico { display: inline-flex; position: relative; }
            html[data-nav="top"] .sidebar-section-ico .ui-ico { width: 1.05rem; height: 1.05rem; opacity: 0.8; vertical-align: 0; }
            html[data-nav="top"] details.sidebar-section > summary:hover {
                background: var(--sidebar-bg-hover); color: var(--sidebar-text-active);
            }
            html[data-nav="top"] details.sidebar-section.is-shown > summary {
                background: var(--nav-accent); color: var(--nav-on-accent); font-weight: 700;
            }
            html[data-nav="top"] details.sidebar-section.is-shown > summary .ui-ico { opacity: 1; }
            /* The shown section's pages: a row of tabs along the foot of the
               bar, on the page's own ground. */
            html[data-nav="top"] .sidebar-section-body { display: none; }
            html[data-nav="top"] details.sidebar-section.is-shown > .sidebar-section-body {
                display: flex; align-items: stretch; gap: 0.1rem;
                position: absolute; left: 0; right: 0; bottom: 0; height: var(--tabrow-h);
                box-sizing: border-box; padding: 0 1rem;
                overflow-x: auto; overflow-y: hidden; scrollbar-width: thin;
                background: var(--card-bg); border-bottom: 1px solid var(--border);
            }
            html[data-nav="top"] .sidebar-section-body a {
                flex: 0 0 auto; margin: 0; padding: 0 0.85rem; gap: 0.4rem;
                border: none; border-radius: 0; background: transparent;
                color: var(--text-muted); font-family: var(--font-body);
                font-size: 0.88rem; font-weight: 600; letter-spacing: 0;
            }
            html[data-nav="top"] .sidebar-section-body a :is(.ui-ico, svg) { display: none; }
            /* A tab is a name: no line under it, and no Quick actions in a row
               of pages. */
            html[data-nav="top"] .sidebar-section-body :is(.nav-desc, .nav-quick) { display: none; }
            /* ...but not the padlock on a locked row, which is not the row's icon. */
            html[data-nav="top"] .sidebar-section-body a .nav-lock .nav-lock-ico { display: inline-block; }
            html[data-nav="top"] .sidebar-section-body a:hover { color: var(--text); background: var(--gray-bg); }
            html[data-nav="top"] .sidebar-section-body a.active {
                color: var(--accent-dark); background: transparent; font-weight: 700;
                box-shadow: inset 0 -3px 0 var(--accent);
            }
            html[data-nav="top"] .sidebar-icon-actions { order: 3; margin: 0 0 0 auto; }
            html[data-nav="top"] :is(.nav-search-btn, .nav-whatsnew-btn) {
                height: 2.3rem; min-height: 0; padding: 0 0.9rem; border-radius: 999px;
                background: transparent; border: 1px solid var(--sidebar-border); box-shadow: none;
                color: var(--sidebar-text); font-size: 0.84rem; font-weight: 600;
            }
            html[data-nav="top"] :is(.nav-search-btn, .nav-whatsnew-btn):is(:hover, :active, .is-here) {
                background: var(--sidebar-bg-hover); color: var(--sidebar-text-active);
                border-color: var(--sidebar-border); box-shadow: none; transform: none;
            }
            html[data-nav="top"] .sidebar-user {
                order: 4; border-top: none; border-left: 1px solid var(--sidebar-border);
                padding: 0 0 0 0.75rem; gap: 0.7rem;
            }
            html[data-nav="top"] .sidebar-user > span { font-size: 0.75rem; }
            /* On the bar, you sit beside My App: face, then first name over
               role, then - only while viewing as somebody - who that is. */
            html[data-nav="top"] .nav-me { padding: 0.15rem 0.5rem 0.15rem 0.2rem; gap: 0.55rem; }
            html[data-nav="top"] .nav-me-av { width: 1.9rem; height: 1.9rem; font-size: 0.74rem; }
            html[data-nav="top"] .nav-me-text { max-width: 9rem; }
            html[data-nav="top"] .nav-me-as { flex-wrap: nowrap; max-width: 13rem; white-space: nowrap; }
            html[data-nav="top"] .nav-me-as-v { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
            html[data-nav="top"] .sidebar-footer { display: none; }
            /* Switch account's menu drops from the bar's right-hand end (the
               script at the foot of this file pins it to the button). */
            html[data-nav="top"] .acct-menu { left: auto; bottom: auto; top: 3.75rem; right: 1rem; }

            /* ---- both: Ask Roxxy, beside Search ------------------------- */
            /* THE ONE BUTTON IN THE CHROME THAT IS LIT. Search is a quiet
               square because every page has a way to get somewhere; this is
               the thing that was "pretty hidden", so it carries the nav's own
               accent - a soft plate, an accent rim and the sparkles in full
               colour - without being the filled pill that means "the section
               you are in". All nav tokens, so it is right on every theme's
               dark rail and bar. */
            html:not([data-nav="top"]) .nav-roxxy-btn {
                display: flex; flex-direction: column; align-items: center; justify-content: center;
                gap: 0.25rem; width: 100%; box-sizing: border-box;
                min-height: var(--rs-sq, 3.35rem); margin: 0.25rem 0 0; padding: var(--rs-pad, 0.45rem) 0.1rem;
                border: none; border-radius: 10px; background: var(--nav-accent-soft);
                box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--nav-accent) 45%, transparent);
                font-family: var(--font-body); font-size: max(var(--rs-fs, 0.64rem), 9.5px); font-weight: 700;
                line-height: 1.15; letter-spacing: 0.01em; text-transform: none;
                text-align: center; overflow-wrap: anywhere; color: var(--sidebar-text-active);
            }
            html:not([data-nav="top"]) .nav-roxxy-btn .ui-ico {
                width: var(--rs-ico, 1.3rem); height: var(--rs-ico, 1.3rem); color: var(--nav-accent); vertical-align: 0;
            }
            html:not([data-nav="top"]) .nav-roxxy-btn:is(:hover, :active, [aria-expanded="true"]) {
                background: var(--sidebar-bg-hover); color: var(--sidebar-text-active);
                box-shadow: inset 0 0 0 1px var(--nav-accent); transform: none;
            }
            html[data-nav="top"] .nav-roxxy-btn {
                height: 2.3rem; min-height: 0; padding: 0 0.95rem; border-radius: 999px;
                background: var(--nav-accent-soft); border: 1px solid var(--nav-accent); box-shadow: none;
                color: var(--sidebar-text-active); font-size: 0.84rem; font-weight: 700; white-space: nowrap;
            }
            html[data-nav="top"] .nav-roxxy-btn .ui-ico {
                width: 1.05rem; height: 1.05rem; color: var(--nav-accent); vertical-align: 0;
            }
            html[data-nav="top"] .nav-roxxy-btn:is(:hover, :active, [aria-expanded="true"]) {
                background: var(--sidebar-bg-hover); color: var(--sidebar-text-active);
                border-color: var(--nav-accent); box-shadow: none; transform: none;
            }
            .nav-roxxy-btn:focus-visible { outline: 2px solid var(--nav-accent); outline-offset: 2px; }

            /* ---- both: an unread count shows on its section ------------- */
            details.sidebar-section:has(.nav-chat-badge) > summary .sidebar-section-ico::after {
                content: ""; position: absolute; top: -3px; right: -5px;
                width: 8px; height: 8px; border-radius: 50%;
                background: var(--red); box-shadow: 0 0 0 2px var(--sidebar-bg);
            }
        }

        /* ================= The star beside a menu row =================
           "I don't like how intrusive the pin button is." A page carries no Pin
           button now; pinning is a small star beside each page in the menu, and
           pinned pages sit in a Pinned group at the top.

           THE STAR'S FORM IS THE ELEMENT BEFORE ITS ROW (nav_sections.add_stars),
           because a form cannot live inside a link. It takes no height of its
           own: the star hangs off the top of the row that follows, which is the
           one edge of a row that is the same whether the row is one line or two.
           The flex gap it would add is taken back with a negative margin.

           SHOWN WHEN THE ROW IS POINTED AT OR FOCUSED, and always when the page
           is pinned (a filled star). Only where there are rows to point at: the
           side bar's panel and the icon rail's flyout. The rail's own icons never
           carry one. The phone's sheet and the top bar's tabs draw none and take
           a long press instead (see the script after the nav). */
        .nav-star-form { display: none; margin: 0; padding: 0; }
        .nav-star {
            appearance: none; box-sizing: border-box; flex: none;
            display: inline-flex; align-items: center; justify-content: center;
            width: 1.7rem; height: 1.7rem; min-height: 0; min-width: 0; margin: 0; padding: 0;
            border: 0; border-radius: 7px; background: transparent; box-shadow: none;
            color: var(--text-muted); cursor: pointer; line-height: 1;
        }
        .nav-star-ico { width: 1rem; height: 1rem; flex: none; fill: none; }
        .nav-star.is-on .nav-star-ico { fill: currentColor; }
        .nav-star.is-on { color: var(--accent-dark); }
        .nav-star:hover { color: var(--accent-dark); background: color-mix(in srgb, var(--text) 10%, transparent); }
        .nav-star:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
        @media (min-width: 901px) {
            html:not([data-nav="top"]) .sidebar-section-body > .nav-star-form {
                display: block; position: relative; height: 0; overflow: visible; z-index: 1;
                margin-bottom: calc(-1 * var(--nav-gap, 0.1rem));
            }
            html:not([data-nav="top"]):not([data-nav="iconrail"]):not(.nav-collapsed) details.sidebar-section.is-shown > .sidebar-section-body { --nav-gap: 0.15rem; }
            html:not([data-nav="top"]) .sidebar-section-body > .nav-star-form .nav-star {
                position: absolute; top: var(--nav-star-top, 0.5rem); right: 0.4rem; opacity: 0;
                transition: opacity 0.12s ease, background 0.12s ease, color 0.12s ease;
            }
            :root[data-nav="iconrail"] .sidebar-section-body { --nav-star-top: 0.4rem; }
            html:not([data-nav="top"]) .sidebar-section-body > .nav-star-form:hover .nav-star,
            html:not([data-nav="top"]) .sidebar-section-body > .nav-star-form:has(+ a:is(:hover, :focus-visible)) .nav-star,
            html:not([data-nav="top"]) .sidebar-section-body > .nav-star-form .nav-star:focus-visible,
            html:not([data-nav="top"]) .sidebar-section-body > .nav-star-form .nav-star.is-on { opacity: 1; }
            html:not([data-nav="top"]) .sidebar-section-body > .nav-star-form:hover + a { background: var(--gray-bg); color: var(--text); }
            /* Room for the star at the row's right edge, so a long name never runs under it. */
            html:not([data-nav="top"]) .sidebar-section-body > .nav-star-form + a { padding-right: 2.3rem; }
        }
        /* A touch screen wide enough for the panel has no hover to find the star
           by, so it is drawn quietly all the time. */
        @media (min-width: 901px) and (hover: none) {
            html:not([data-nav="top"]) .sidebar-section-body > .nav-star-form .nav-star { opacity: 0.7; }
        }
        @media (prefers-reduced-motion: reduce) {
            .nav-star { transition: none !important; }
        }
        /* THE LONG PRESS CHOICE. The phone's sheet (and the top bar's tabs) draw no
           star; about half a second on a page opens this beside it. */
        .nav-pop {
            position: fixed; z-index: 60; min-width: 10.5rem; box-sizing: border-box; padding: 0.25rem;
            border-radius: 12px; background: var(--text); color: var(--card-bg);
            box-shadow: 0 10px 30px rgba(16, 35, 63, 0.35);
            font-family: var(--font-body); font-size: 0.95rem; font-weight: 600;
        }
        .nav-pop-pin, .nav-pop-open {
            display: flex; align-items: center; gap: 0.6rem; width: 100%; box-sizing: border-box;
            min-height: 2.75rem; margin: 0; padding: 0.55rem 0.8rem; border: 0; border-radius: 9px;
            background: transparent; box-shadow: none; color: inherit; font: inherit; text-align: left;
            text-decoration: none; cursor: pointer;
        }
        .nav-pop-pin .nav-star-ico { width: 1.05rem; height: 1.05rem; fill: none; color: var(--accent); }
        .nav-pop-pin:is(:hover, :focus-visible), .nav-pop-open:is(:hover, :focus-visible) {
            background: color-mix(in srgb, var(--card-bg) 16%, transparent); color: inherit; outline: none;
        }
        @media (max-width: 900px) {
            /* A finger held on a row is a press, not a text selection or the browser's link menu. */
            .sidebar-nav .sidebar-section-body > a { -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; }
            .sidebar-nav .sidebar-section-body > a.is-pressed { box-shadow: inset 0 0 0 2px var(--accent); }
            /* A page you have pinned wears a small filled star in the sheet. */
            .sidebar-nav .sidebar-section-body > .nav-star-form:has(.nav-star.is-on) + a::after {
                content: "\2605"; position: absolute; top: 0.3rem; left: 0.4rem;
                color: var(--accent); font-size: 0.8rem; line-height: 1;
            }
            .sidebar-nav details[data-section=pinned] .sidebar-section-body > .nav-star-form + a::after { content: none; }
        }
        .sidebar-section[hidden] { display: none !important; }

        /* ================= The icon rail =================
           "Main menu mockup B": a slim rail of icons that opens to labels, with
           search on top. The third layout (data-nav="iconrail"), and the one
           anybody who has never picked a layout gets. A computer only, like
           the other two: a phone has its tab bar whatever is stored, so every
           rule of this layout sits inside this one desktop query.

           IT BUILDS ON THE SIDE BAR'S RULES, NOT BESIDE THEM. The side bar's
           selectors say "not the top bar", so they still match here; what is
           written below only changes what differs. The rail is 64px of icons
           (the names stay in the markup for a screen reader and head the
           flyout), and a section's pages are a flyout over the page rather
           than a panel that pushes it: the page keeps its width, and the rail
           stays 64px whichever section is open.

           THE FLYOUT is the section's own list, opened by hovering its icon,
           by tapping it, or by tabbing to it (.is-fly, set by the script after
           the nav). It stands on the nav's ground, so each theme's rail and
           flyout match; the side bar's rows are written for a light card, so
           the colour tokens they read are re-pointed at the nav's own inside
           the flyout instead of every rule being said twice. Rows here are a
           name, an icon and a count: the line under each name and the Quick
           actions belong to the side bar's roomier panel.

           The selector starts at the root element so that it outranks the side
           bar's, which it shares every property with. */
        .nav-fly-search { display: none; }
        @media (min-width: 901px) {
            :root[data-nav="iconrail"][data-nav][data-nav] {
                --rail-w: 64px; --sidebar-width: 64px; --panel-top: 0rem;
            }
            :root[data-nav="iconrail"] .sidebar-brand-icon.brand-logo-own { max-width: 2.9rem; }
            /* One icon per section, centred in the rail. */
            :root[data-nav="iconrail"] details.sidebar-section > summary,
            :root[data-nav="iconrail"] .nav-search-btn,
            :root[data-nav="iconrail"] .nav-roxxy-btn,
            :root[data-nav="iconrail"] .nav-whatsnew-btn {
                width: 2.8rem; min-height: 2.8rem; margin-left: auto; margin-right: auto;
                padding: 0; gap: 0; font-size: 0; border-radius: 12px;
            }
            :root[data-nav="iconrail"] details.sidebar-section.is-fly > summary {
                background: var(--sidebar-bg-hover); color: var(--sidebar-text-active);
            }
            :root[data-nav="iconrail"] details.sidebar-section.is-fly .sidebar-section-ico .ui-ico { opacity: 1; }
            /* The section holding this page keeps its mark when its flyout is shut. */
            :root[data-nav="iconrail"] details.sidebar-section.is-shown > summary {
                background: var(--nav-accent-soft);
                box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--nav-accent) 40%, transparent);
            }
            :root[data-nav="iconrail"] details.sidebar-section > summary:focus-visible,
            :root[data-nav="iconrail"] .nav-search-btn:focus-visible,
            :root[data-nav="iconrail"] .nav-roxxy-btn:focus-visible,
            :root[data-nav="iconrail"] .nav-whatsnew-btn:focus-visible,
            :root[data-nav="iconrail"] .nav-me:focus-visible { outline: 2px solid var(--nav-accent); outline-offset: 2px; }

            /* ---- the flyout --------------------------------------------- */
            :root[data-nav="iconrail"][data-nav] details.sidebar-section.is-shown > .sidebar-section-body { display: none; }
            :root[data-nav="iconrail"][data-nav] details.sidebar-section.is-fly > .sidebar-section-body {
                display: flex; flex-direction: column; gap: 0.1rem;
                position: fixed; top: 0; bottom: 0; left: var(--rail-w); width: var(--panel-w);
                box-sizing: border-box; padding: 4.1rem 0.7rem 1rem;
                overflow-y: auto; overscroll-behavior: contain; z-index: 29;
                --text: var(--sidebar-text-active); --text-muted: var(--sidebar-text); --card-bg: var(--sidebar-bg);
                --gray-bg: var(--sidebar-bg-hover); --border: var(--sidebar-border);
                --accent-light: var(--nav-accent-soft); --accent-dark: var(--nav-accent);
                background: var(--sidebar-bg); border-right: 1px solid var(--sidebar-border);
                box-shadow: 14px 0 32px rgba(0, 0, 0, 0.28);
            }
            :root[data-nav="iconrail"][data-nav] details.sidebar-section.is-fly > .sidebar-section-body::before {
                content: attr(data-title); display: block; flex: none; padding: 0.2rem 0.6rem 0.5rem;
                font-family: var(--font-body); font-size: 0.7rem; font-weight: 700;
                letter-spacing: 0.08em; text-transform: uppercase; color: var(--sidebar-text);
            }
            :root[data-nav="iconrail"] .sidebar-section-body > a {
                grid-template-columns: 1.5rem minmax(0, 1fr) auto; column-gap: 0.7rem;
                padding: 0.5rem 0.6rem; font-size: 0.92rem; font-weight: 600; border-radius: 8px;
            }
            :root[data-nav="iconrail"] .sidebar-section-body > a > :is(svg, .nav-messenger-ico) {
                grid-row: 1; width: 1.5rem; height: 1.5rem; padding: 0.2rem;
                background: transparent; border-radius: 0; box-shadow: none;
            }
            :root[data-nav="iconrail"] .sidebar-section-body > a > .nav-messenger-ico { padding: 0; }
            :root[data-nav="iconrail"] .sidebar-section-body > a:is(:hover, .active) > :is(svg, .nav-messenger-ico) {
                background: transparent; box-shadow: none;
            }
            :root[data-nav="iconrail"] .sidebar-section-body > a > .nav-desc { display: none; }
            :root[data-nav="iconrail"] .sidebar-section-body a:focus-visible { outline: 2px solid var(--nav-accent); outline-offset: -2px; }
            /* Search, on top of the flyout. It is the rail's Search button
               again, drawn as a field: pressing it opens the same jump box. */
            :root[data-nav="iconrail"].rail-fly .nav-fly-search {
                display: flex; align-items: center; gap: 0.5rem;
                position: fixed; top: 0.8rem; left: calc(var(--rail-w) + 0.7rem); width: calc(var(--panel-w) - 1.4rem);
                height: 2.5rem; box-sizing: border-box; z-index: 31; padding: 0 0.7rem; margin: 0;
                border: 1px solid var(--sidebar-border); border-radius: 10px;
                background: var(--sidebar-bg-hover); color: var(--sidebar-text);
                font-family: var(--font-body); font-size: 0.88rem; font-weight: 500; text-align: left; cursor: text;
            }
            :root[data-nav="iconrail"].rail-fly .nav-fly-search:hover { border-color: var(--nav-accent); color: var(--sidebar-text-active); }
            :root[data-nav="iconrail"] .nav-fly-search .ui-ico { width: 1.05rem; height: 1.05rem; flex: none; }
            :root[data-nav="iconrail"] .nav-fly-search kbd {
                margin-left: auto; font: inherit; font-size: 0.7rem; opacity: 0.8; white-space: nowrap;
            }
            @media (prefers-reduced-motion: no-preference) {
                :root[data-nav="iconrail"][data-nav] details.sidebar-section.is-fly > .sidebar-section-body {
                    animation: fc-rail-fly 0.14s ease-out;
                }
                @keyframes fc-rail-fly { from { opacity: 0; transform: translateX(-6px); } to { opacity: 1; transform: none; } }
            }

            /* ---- you, at the foot of the rail ----------------------------
               The face alone. The name, My App, Switch and Sign out open
               beside it on hover or focus, and the face is still a link to My
               App. "Viewing as" stays under the face: it is the one piece of
               state in here that must never be hidden. */
            :root[data-nav="iconrail"] .sidebar-user { position: relative; align-items: center; padding: 0.5rem 0.4rem 0.7rem; }
            :root[data-nav="iconrail"] .nav-me { width: 2.8rem; margin: 0 auto; padding: 0.25rem 0; }
            :root[data-nav="iconrail"] .nav-me.is-viewing { width: 100%; }
            :root[data-nav="iconrail"] .nav-me-text { display: none; }
            :root[data-nav="iconrail"] .sidebar-user-acts {
                display: none; position: fixed; left: var(--rail-w); bottom: 0.5rem; z-index: 34;
                width: 12.5rem; box-sizing: border-box; padding: 0.4rem;
                flex-direction: column; align-items: stretch; gap: 0.1rem;
                background: var(--sidebar-bg); border: 1px solid var(--sidebar-border); border-left: none;
                border-radius: 0 12px 12px 0; box-shadow: 14px 0 32px rgba(0, 0, 0, 0.28);
            }
            :root[data-nav="iconrail"] .sidebar-user:is(:hover, :focus-within) .sidebar-user-acts { display: flex; }
            :root[data-nav="iconrail"] .sidebar-user-acts :is(.sidebar-user-app, button) {
                display: block; width: 100%; box-sizing: border-box; min-height: 2.4rem; padding: 0.5rem 0.7rem;
                border: none; border-radius: 8px; background: transparent; color: var(--sidebar-text-active);
                font-size: 0.9rem; font-weight: 600; text-align: left;
            }
            :root[data-nav="iconrail"] .sidebar-user-acts :is(.sidebar-user-app, button):is(:hover, .is-active) {
                background: var(--sidebar-bg-hover); color: var(--sidebar-text-active); border: none;
            }
            :root[data-nav="iconrail"] .sidebar-user-acts :is(.sidebar-user-app, button):focus-visible {
                outline: 2px solid var(--nav-accent); outline-offset: -2px;
            }
        }

        /* THE RAIL SCALES TO THE WINDOW. Everything above is drawn for a tall
           window, and on a work laptop at 100% zoom the window is 600 to 750px
           tall: the signed-in block at the foot (face, name, role, My App,
           Switch, Log out) took about 260px and the squares were left a
           four-square slot to scroll in. So the sizes the rail is made of are
           variables, and these steps shrink them as the window gets shorter
           (the section squares, their icons, the foot) and the panel narrows
           as it gets narrower. Names stay at 0.6rem or more, and every square
           keeps its icon over its name; only the room around them is spent.
           The rail itself stays 76px: "Scorecard" does not fit in less. */
        @media (min-width: 901px) and (max-height: 860px) {
            html { --rs-sq: 3rem; --rs-pad: 0.34rem; --rs-ico: 1.2rem; --rs-head: 0.5rem;
                   --rs-av: 1.9rem; --rs-btn: 0.3rem; --rs-user: 0.4rem; }
        }
        @media (min-width: 901px) and (max-height: 740px) {
            html { --rs-sq: 2.6rem; --rs-pad: 0.24rem; --rs-ico: 1.1rem; --rs-fs: 0.6rem; --rs-head: 0.35rem;
                   --rs-av: 1.65rem; --rs-btn: 0.22rem; --rs-user: 0.3rem; --rs-gap: 0.2rem; --rs-name: 0.64rem; }
        }
        @media (min-width: 901px) and (max-height: 640px) {
            html { --rs-sq: 2.35rem; --rs-pad: 0.18rem; --rs-ico: 1rem; --rs-head: 0.25rem;
                   --rs-av: 1.45rem; --rs-btn: 0.16rem; --rs-user: 0.22rem; --rs-gap: 0.14rem; }
            html:not([data-nav="top"]) .nav-me-role { display: none; }
        }
        @media (min-width: 901px) and (max-width: 1400px) { :root { --panel-w: 216px; } }
        @media (min-width: 901px) and (max-width: 1180px) { :root { --panel-w: 200px; } }

    /* ---- Dropdown menus (details.menu-dropdown + .menu-panel) ------------
       LIVED ON ONE PAGE AND WAS USED BY FOUR. The Repair Tickets page defined
       these, _sync_buttons.html and the VTO page used the same class names,
       and on a page that had not copied the CSS the panel laid out IN FLOW: it
       carries a min-width, so the disclosure grew to it and its summary row
       stretched across the page as a bar. Reported on VTO as "the settings
       icon bar gets stretched".

       The trigger used to be a real button element nested inside the summary -
       looked right, but a button inside a summary silently breaks the
       browser's native toggle (confirmed live: clicking it did nothing, the
       open flag never flipped). The summary row is already clickable, so it is
       styled directly with .btn instead.

       No element names in angle brackets in here: base.html's styles are
       inlined into every page, so the literal would land in the HTML of pages
       that have no such element - see
       tests/test_the_native_controls_are_ours.py. */
    .menu-dropdown { position: relative; display: inline-block; }
    .menu-dropdown summary { list-style: none; cursor: pointer; display: inline-block; }
    .menu-dropdown summary::-webkit-details-marker { display: none; }
    .menu-dropdown summary::marker { content: ""; }
    .menu-panel {
        position: absolute; top: calc(100% + 4px); left: 0; z-index: 40; min-width: 220px;
        background: var(--card-bg); border: 1px solid var(--border); border-radius: var(--radius-sm);
        box-shadow: var(--shadow-lift); padding: 0.4rem 0;
    }
    /* ANCHORED TO THE OTHER EDGE. .menu-panel hangs its left edge off the
       trigger, which is right for a button on the left of a row and wrong for
       one pushed to the right by margin-left:auto - there the panel's own
       width carries it off the side of the screen. Measured on the VTO gear:
       a 320px panel opening at x=1318 on a 1400px viewport, so 237px of it
       was past the edge and the setting looked like it did nothing. */
    .menu-panel-right { left: auto; right: 0; }
    .menu-panel hr { border: none; border-top: 1px solid var(--border); margin: 0.3rem 0; }
    .menu-item {
        display: block; width: 100%; text-align: left; background: transparent; border: none;
        color: var(--text); padding: 0.45rem 0.9rem; font-size: 0.85rem; font-weight: 400;
        border-radius: 0; min-height: auto; cursor: pointer;
    }
    .menu-item:hover { background: var(--accent-light); color: var(--text); }
    label.menu-item { display: flex; align-items: center; gap: 0.5rem; cursor: pointer; }
    label.menu-item input { width: auto; margin: 0; }

        /* ---------------- App shell: fixed sidebar + main content ---------------- */
        .app-shell { display: flex; min-height: 100vh; min-height: 100dvh; }

        .sidebar {
            width: var(--sidebar-width); flex-shrink: 0;
            /* Solid, not lit. The glow and the gradient wash read as slick;
               structure is what this needs instead - see the banded section
               headers below, which do the same job without the sheen. */
            background: var(--sidebar-bg);
            box-shadow: inset -1px 0 0 rgba(255, 255, 255, 0.03);
            display: flex; flex-direction: column; position: fixed;
            top: 0; left: 0; bottom: 0;
            border-radius: 0; border: none; border-right: 1px solid var(--sidebar-border);
            overflow-y: auto; overflow-x: hidden; z-index: 30; transition: transform 0.2s ease;
            /* Thin, subtle scrollbar so it doesn't sit as a chunky gray bar
               against the floating card's rounded corner (Firefox). */
            scrollbar-width: thin; scrollbar-color: rgba(255, 255, 255, 0.18) transparent;
        }
        /* WebKit/Blink: a slim, rounded, translucent thumb over a transparent
           track - only really visible while hovering the sidebar. */
        .sidebar::-webkit-scrollbar { width: 8px; }
        .sidebar::-webkit-scrollbar-track { background: transparent; }
        .sidebar::-webkit-scrollbar-thumb {
            background: transparent; border-radius: 999px; border: 2px solid transparent; background-clip: content-box;
        }
        .sidebar:hover::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.18); background-clip: content-box; }
        .sidebar::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, 0.32); background-clip: content-box; }
        /* No gradient wash, no glowing gradient tile. A squared plate and a
           rule underneath - the header of a panel, not a logo lockup floating
           on a card. */
        .sidebar-brand {
            color: var(--sidebar-text-active); font-family: var(--font-nav);
            font-weight: 700; font-size: 1.2rem; text-transform: uppercase;
            /* Full width: the collapse button moved to the sidebar's edge, so
               there is no longer a column to reserve here and a longer DSP name
               gets the room back. */
            padding: 0.85rem 0.9rem 0.75rem 0.9rem; letter-spacing: 0.06em; line-height: 1.05;
            display: flex; align-items: center; gap: 0.6rem;
            overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
            border-bottom: none; background: transparent;
        }
        /* The name, so it can be truncated on its own rather than truncating
           the mark beside it. */
        .sidebar-brand-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
        /* AN IDENTITY BLOCK, not a title bar: a brand row and, under it, where
           this person currently is. Column rather than row for that reason.

           THE RULE UNDERNEATH IS A HAIRLINE. It was --nav-accent, the most
           saturated colour in the whole nav, which made the divider the
           brightest thing in the header - a separator outshouting what it
           separates. Dense style had already quietly corrected this to
           --sidebar-border for itself; this is the same correction everywhere.  */
        .sidebar-head {
            display: flex; flex-direction: column;
            border-bottom: 1px solid var(--sidebar-border);
            padding-bottom: 0.1rem;
        }
        .sidebar-head-row { display: flex; align-items: center; }
        .sidebar-head .sidebar-brand { flex: 1 1 auto; min-width: 0; border-bottom: none; }
        /* A BARE GLYPH, NOT A BOXED BUTTON. It sat in a bordered 2rem square at
           the far end of a mostly empty row, which gave one chevron the same
           visual weight as the company's name. */
        .sidebar-head .sidebar-collapse {
            flex: 0 0 auto; align-self: center; margin: 0 0.5rem 0 0.2rem;
            border: none; background: transparent; opacity: 0.55;
        }
        .sidebar-head .sidebar-collapse:hover {
            opacity: 1; background: transparent; border: none;
        }
        .sidebar-brand-icon {
            font-size: 1.05rem; width: 2.15rem; height: 2.15rem; display: inline-flex;
            align-items: center; justify-content: center; border-radius: 2px;
            background: var(--nav-accent); color: var(--nav-on-accent); box-shadow: none;
            flex-shrink: 0;
        }
        /* The Fleet Cadence mark - no plate, just the artwork. */
        .sidebar-brand-icon.brand-logo, .topbar-brand-logo {
            width: 1.85rem; height: 1.85rem; border-radius: 2px; object-fit: contain;
            background: transparent; box-shadow: none;
        }
        .topbar-brand-logo { width: 1.6rem; height: 1.6rem; vertical-align: middle; margin-right: 0.4rem; }
        /* A DSP's own logo is whatever shape they uploaded, not a square mark: keep its
           height and let it be wider, up to a limit, instead of shrinking a wide logo
           into a square it cannot fill. */
        .sidebar-brand-icon.brand-logo-own { width: auto; max-width: 6rem; height: 1.85rem; object-fit: contain;
            background: transparent; box-shadow: none; border-radius: 2px; flex-shrink: 0; }
        .topbar-brand-logo[src^="/org-logo"] { width: auto; max-width: 5rem; height: 1.6rem; }
        /* A WIDE LOGO SHOWS ALONE (org_logo.WIDE_KEY): it is a wordmark, so the
           name beside it would say the company twice. It gets the room the name
           had; the name stays in the page for screen readers. */
        .sidebar-brand-icon.brand-logo-own.brand-logo-wide { max-width: 11rem; background: #FFFFFF; padding: 0.2rem 0.4rem;
            border-radius: 6px; box-sizing: border-box; }
        .topbar-brand-logo.brand-logo-wide[src^="/org-logo"] { max-width: 9rem; margin-right: 0; }
        .brand-name-in-logo { position: absolute !important; width: 1px; height: 1px; overflow: hidden;
            clip: rect(0 0 0 0); white-space: nowrap; margin: 0; padding: 0; border: 0; }
        /* WHICH CUT THE TOP BAR SHOWS. Aurora and Professional put the bar on
           white, Harbor puts it on #253956, and the mark is mostly white - so the
           ink cut is the default and the white one only appears on the dark
           theme. Exactly one is ever displayed. Dusk's bar is its card,
           #d8dee7, a light grey, so it keeps the ink cut like Light. */
        .topbar-mark-white { display: none; }
        :root[data-theme="dark"] .topbar-mark-ink { display: none; }
        :root[data-theme="dark"] .topbar-mark-white { display: inline-block; }
        /* Install + Enable-notifications are icon-only buttons (tooltip carries
           the label) so they stay compact - a small row in the sidebar
           (desktop) and grouped on the right of the topbar (mobile). Each row
           collapses to nothing when both buttons are hidden. */
        .sidebar-icon-actions { display: flex; gap: 0.4rem; margin: 0.6rem 0.75rem 0; }
        /* Station picker - see station_switch() above the sidebar. THE SECOND
           LINE OF THE IDENTITY BLOCK, so it reads as "where you are" - and now
           as something you can press. It was the select stripped to a line of
           small text with a caret, and "the station selector in the nav needs
           to be much more noticeable". A pill in the accent: the pin, the
           station's code in bold, a chevron.

           THE SELECT IS THE WHOLE PILL, see-through and laid on top, so a
           click or one tap anywhere on it opens the browser's own list. It is
           still a real select element (keyboard, no-JS and the mobile wheel
           all keep working); what you see under it is drawn beside it.

           ITS COLOURS ARE FIVE LOCAL TOKENS, because the pill stands on two
           grounds. On paper (the panel, the More sheet, the phone's bar) it
           takes the accent's tint and ink, the pair the panel's tinted Quick
           action uses; on the nav's own ground (the rail, the top bar) it takes
           the nav's tokens, which every theme sets for that ground - navy in
           Light, Dusk and Dark, grey in Professional. A place sets the five and
           these rules draw the pill once. */
        .station-switch { margin: 0 0.9rem 0.55rem; display: flex; align-items: center; gap: 0.35rem; min-width: 0; }
        .station-switch-label {
            font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.06em;
            color: var(--nav-muted, var(--text-muted)); margin: 0; white-space: nowrap;
            /* The word "Station" is what the line itself now says. Kept in the
               markup for the label/control pairing screen readers need. */
            position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%);
        }
        .station-switch-pill {
            --stn-bg: var(--accent-light); --stn-ink: var(--accent-dark); --stn-ico: var(--accent-dark);
            --stn-rim: color-mix(in srgb, var(--accent) 40%, transparent); --stn-rim-on: var(--accent);
            position: relative; display: inline-flex; align-items: center; gap: 0.4rem;
            min-width: 0; max-width: 100%; height: 1.95rem; box-sizing: border-box; padding: 0 0.65rem 0 0.55rem;
            border: 1px solid var(--stn-rim); border-radius: 999px; background: var(--stn-bg); color: var(--stn-ink);
            font-family: var(--font-nav); font-size: 0.9rem; line-height: 1.1; white-space: nowrap; cursor: pointer;
            transition: border-color 0.12s ease, box-shadow 0.12s ease;
        }
        .station-switch-pill .ui-ico { flex: none; width: 1rem; height: 1rem; vertical-align: 0; color: var(--stn-ico); }
        .station-switch-pill .station-switch-caret { width: 0.85rem; height: 0.85rem; stroke-width: 2.5; }
        .station-switch-now { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
        .station-switch-now b { font-weight: 700; letter-spacing: 0.04em; }
        .station-switch-name { font-weight: 500; }
        .station-switch-pill select {
            position: absolute; inset: 0; z-index: 1; display: block; width: 100%; height: 100%;
            min-width: 0; min-height: 0; margin: 0; padding: 0; border: 0; border-radius: inherit;
            opacity: 0; cursor: pointer;
        }
        .station-switch-pill:hover { border-color: var(--stn-rim-on); box-shadow: inset 0 0 0 1px var(--stn-rim-on); }
        .station-switch-pill:has(select:focus-visible) { outline: 2px solid var(--stn-rim-on); outline-offset: 2px; }
        /* One station, so nothing to switch - the same line, stated. */
        .station-static {
            margin: 0 0.9rem 0.55rem; padding-left: 2.75rem;
            font-family: var(--font-nav); font-size: 0.78rem; font-weight: 600;
            letter-spacing: 0.03em; color: var(--sidebar-text);
            overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
        }
        .sidebar-icon-actions:not(:has(> :not([hidden]))) { display: none; }
        .sidebar-icon-btn {
            display: inline-flex; align-items: center; justify-content: center;
            width: 2rem; height: 2rem; padding: 0; font-size: 1rem; line-height: 1;
            background: var(--sidebar-bg-hover); border: 1px solid var(--sidebar-border); border-radius: var(--radius-sm);
            color: var(--sidebar-text); cursor: pointer; box-shadow: none; min-height: auto;
        }
        .sidebar-icon-btn:hover { color: var(--nav-accent); border-color: var(--nav-accent); background: var(--nav-accent-soft); }
        .topbar-actions { margin-left: auto; display: flex; gap: 0.4rem; }
        .topbar-icon-btn {
            display: inline-flex; align-items: center; justify-content: center;
            width: 2rem; height: 2rem; padding: 0; font-size: 1rem; line-height: 1;
            border-radius: var(--radius-sm); border: 1px solid var(--accent);
            background: var(--accent-light); color: var(--accent-dark); cursor: pointer; box-shadow: none; min-height: auto;
        }
        .topbar-icon-btn:hover { background: var(--accent-light); border-color: var(--accent-dark); }
        /* BACK, FOR AN INSTALLED APP. Hidden everywhere until the app runs with no
           browser chrome (display-mode: standalone, or the fc-standalone class
           static/js/app-back.js sets for iOS). 44px square, icon only, the text
           colour of the bar so every theme reads it; the bar's own safe-area
           padding keeps it out of a landscape notch. */
        .app-back {
            display: none; align-items: center; justify-content: center; flex: none;
            width: 2.75rem; height: 2.75rem; min-height: 2.75rem; padding: 0; margin: -0.3rem 0 -0.3rem -0.5rem;
            border: 0; border-radius: 50%; background: transparent; color: var(--text); cursor: pointer; box-shadow: none;
            -webkit-tap-highlight-color: transparent; touch-action: manipulation;
            transition: background-color 150ms ease, transform 150ms ease;
        }
        .app-back .ui-ico { width: 1.35rem; height: 1.35rem; }
        .app-back:hover { background: var(--accent-light); color: var(--accent-dark); }
        .app-back:active { transform: scale(0.92); }
        .app-back:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
        html.fc-standalone .app-back { display: inline-flex; }
        @media (display-mode: standalone), (display-mode: window-controls-overlay), (display-mode: minimal-ui) { .app-back { display: inline-flex; } }
        @media (prefers-reduced-motion: reduce) { .app-back { transition: none; } .app-back:active { transform: none; } }
        .sidebar-nav { flex: 1; padding: 0.85rem 0.85rem 1rem; }
        /* A section header carries an icon FOR THE ICON RAIL, which has nothing
           else to show on a 76px square. Hidden by default so the sidebar and
           the top bar read exactly as they did before it existed. */
        .sidebar-section-ico { display: none; }

        /* Each nav category is a disclosure so it's individually collapsible -
           state (open/closed) persists per-section in localStorage (see the
           wireSidebarSections script), matching this app's established
           localStorage-preference pattern (e.g. Scheduler's "Hide previous
           days"). Defaults open (unchanged from before this was
           collapsible) - collapsing is something the user opts into per
           section, not a new default that would hide nav items on first
           load. */
        details.sidebar-section { margin: 0.5rem 0 0; border-top: none; }
        /* A hairline running out to the panel edge, the way a section breaks on
           a printed manifest - rather than the uppercase gold chip that every
           generated dashboard uses. */
        /* A solid header band per category. Banded headers are how a parts list
           or a run sheet separates its sections: you see the groups before you
           read any of the items, which is the whole point of having them. */
        /* ONE DEVICE, NOT THREE. This was a full-width band, plus a rule above
           it, plus a rule below it, plus a 3px accent bar down its left edge -
           four ways of saying "this is a heading", on four headings, which was
           most of why the nav ran past the fold.

           What says it now is the accent colour and the condensed caps, which
           nothing else in the nav uses. Dropping the left bar matters as much
           as dropping the band: every ROW carries a marker channel down its own
           left edge, and that channel means "you are here" - a heading wearing
           the same bar was the one piece of the nav's grammar that said two
           things at once. */
        details.sidebar-section summary {
            list-style: none; cursor: pointer; display: flex; align-items: center; gap: 0.5rem;
            color: var(--nav-accent); font-family: var(--font-nav);
            font-size: 0.75rem; font-weight: 700; text-transform: uppercase;
            letter-spacing: 0.15em; padding: 0.7rem 0.9rem 0.3rem 0.78rem; border-radius: 0;
            background: transparent; border: none; box-shadow: none;
            transition: color 0.15s ease;
        }
        details.sidebar-section summary::after {
            content: ""; flex: 1;
        }
        details.sidebar-section summary::-webkit-details-marker { display: none; }
        details.sidebar-section summary:hover {
            color: var(--nav-accent-text); background: transparent;
        }
        /* Rows sit inside the band's group, indented off it. */
        .sidebar-section-body { padding: 0.1rem 0 0.25rem; }
        .sidebar-section-caret {
            font-size: 0.55rem; color: var(--sidebar-text); opacity: 0.6; transition: transform 0.15s ease;
            transform: rotate(90deg); order: 3;
        }
        details.sidebar-section:not([open]) .sidebar-section-caret { transform: rotate(0deg); }
        details.sidebar-section:not([open]) summary { padding-bottom: 0.42rem; }
        /* Rows on a board, not a stack of pills. Each one carries a 3px marker
           channel down its left edge: empty while idle, amber on the current
           page. Everything sits on one strict left grid, which is what makes a
           long nav scan like an operational list instead of a toy. */
        /* TIGHTER THAN BANDED WAS, LOUDER THAN DENSE WAS. The row keeps the
           condensed face and the marker channel; what came off is the padding
           and about a tenth of the type size. Four sections and a dozen rows
           makes that the difference between a nav that fits a 900px window and
           one that does not - which is what the two styles were really
           disagreeing about. */
        .sidebar-nav a {
            display: flex; align-items: center; gap: 0.55rem; min-width: 0;
            color: var(--sidebar-text); padding: 0.36rem 0.85rem 0.36rem 0.75rem;
            border-radius: 2px; border-left: 3px solid transparent;
            font-family: var(--font-nav); font-size: 0.97rem; font-weight: 500;
            letter-spacing: 0.02em; white-space: nowrap; margin-bottom: 0.03rem;
            overflow: hidden; text-overflow: ellipsis;
            transition: color 0.12s ease, background 0.12s ease, border-color 0.12s ease;
        }
        /* A LOCKED NAV ENTRY IS STILL A NAV ENTRY. Dimmed, because it is not
           somewhere you can go yet; kept in place and clickable, because the
           point is to say the feature exists and what it costs. Hiding it
           outright is how a customer never learns they could buy it. */
        .sidebar-nav a.nav-locked { opacity: 0.55; }
        .sidebar-nav a.nav-locked:hover { opacity: 0.8; }
        .nav-lock { margin-left: auto; font-size: 0.78rem; flex: none; }
        .nav-lock .nav-lock-ico { display: inline-block; width: 1.15em; height: 1.15em; vertical-align: -0.15em; }
        /* WHAT A PAGE IS FOR, AND THE QUICK ACTIONS, are the side bar panel's
           alone (RICHER ROWS, above): hidden here for every other way the nav
           is drawn - the top bar's tabs, the phone's sheet and chips - and
           shown only by the panel's own rules. */
        .nav-desc, .nav-quick, .nav-group-h { display: none; }
        .sidebar-nav a:hover {
            color: var(--sidebar-text-active); background: rgba(255, 255, 255, 0.05);
            border-left-color: color-mix(in srgb, var(--nav-accent) 45%, transparent);
        }
        .sidebar-nav a:hover .ui-ico { color: var(--nav-accent); opacity: 1; }
        /* Current page: the marker lights, the row takes an amber ground that
           falls away to the right, and the icon lights with it. The row is doing
           the work rather than a lone hairline - which is what made the first
           pass read as plain. */
        .sidebar-nav a.active {
            color: var(--nav-accent-text); font-weight: 600;
            background: var(--nav-accent-soft);
            border-left-color: var(--nav-accent);
            box-shadow: none; transform: none;
        }
        .sidebar-nav a.active:hover { color: var(--nav-accent-text); }
        .sidebar-nav a.active .ui-ico { color: var(--nav-accent); opacity: 1; }
        /* Icons on a fixed column so every label starts at the same x, and held
           back a shade so the labels lead. */
        .sidebar-nav a .ui-ico, .sidebar-nav a > svg { flex: 0 0 1.15rem; opacity: 0.62; transition: opacity 0.12s ease, color 0.12s ease; }
        /* Section-level hub links (Reports, Setup & Integrations) aren't
           collapsible groups, so give them the same hairline separator + top
           spacing the disclosure sections use - the sidebar's major entries then
           share one consistent rhythm instead of a lone pill between sections. */
        .sidebar-nav a.sidebar-hub {
            margin-top: 0.55rem; padding-top: 0.85rem; border-top: 1px solid var(--sidebar-border);
        }
        .sidebar-user {
            padding: 0.9rem 1.1rem; border-top: 1px solid var(--sidebar-border);
            display: flex; align-items: center; justify-content: space-between; gap: 0.5rem;
        }
        /* The plain-text fallback, drawn only if nav_me could not be built.
           A direct child, so it never reaches into the nav_me link's spans. */
        .sidebar-user > span {
            color: var(--sidebar-text); font-family: var(--font-nav); font-size: 0.92rem;
            line-height: 1.25; letter-spacing: 0.03em; text-transform: uppercase;
        }
        .sidebar-user form { margin: 0; }
        .sidebar-user-acts { display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap;
            justify-content: flex-end; }
        /* Styled like the Log out button beside it, because they are the same
           kind of thing: the two actions that belong to the person rather than
           to the fleet. */
        .sidebar-user-app {
            background: transparent; border: 1px solid var(--sidebar-border); color: var(--sidebar-text);
            padding: 0.35rem 0.7rem; border-radius: var(--radius-sm); font-size: 0.78rem;
            text-decoration: none; white-space: nowrap;
        }
        .sidebar-user-app:hover, .sidebar-user-app.is-active {
            border-color: var(--nav-accent); color: var(--nav-accent);
        }
        .sidebar-user button {
            background: transparent; border: 1px solid var(--sidebar-border); color: var(--sidebar-text);
            padding: 0.35rem 0.7rem; border-radius: var(--radius-sm); font-size: 0.78rem; min-height: auto;
        }
        .sidebar-user button:hover { border-color: var(--nav-accent); color: var(--nav-accent); background: transparent; }
        /* WHO IS SIGNED IN (the nav_me_link macro, app/tools/nav_me.py). On
           the nav's own dark ground by default - the rail and the top bar -
           and restyled onto the card for the phone's More sheet further down.
           Nav tokens only, so it follows every theme's panel. */
        .nav-me {
            display: flex; align-items: center; gap: 0.6rem; min-width: 0;
            padding: 0.3rem; border-radius: 10px; text-decoration: none;
            color: var(--sidebar-text-active);
        }
        .nav-me:hover, .nav-me.is-active { background: var(--sidebar-bg-hover); color: var(--sidebar-text-active); }
        .nav-me:focus-visible { outline: 2px solid var(--nav-accent); outline-offset: 2px; }
        .nav-me-av {
            position: relative; flex: none; width: 2.1rem; height: 2.1rem; border-radius: 50%;
            display: inline-flex; align-items: center; justify-content: center;
            background: var(--nav-accent); color: var(--nav-on-accent);
            font-family: var(--font-body); font-size: 0.8rem; font-weight: 700;
            letter-spacing: 0.02em; line-height: 1;
        }
        .nav-me-av img { display: block; width: 100%; height: 100%; border-radius: 50%; object-fit: cover; }
        /* Viewing as somebody else: a ring round the face and an eye on it, on
           top of the words - the state is said three ways because forgetting
           you are in it is the failure. */
        .nav-me.is-viewing .nav-me-av { box-shadow: 0 0 0 2px var(--sidebar-bg), 0 0 0 4px var(--nav-accent); }
        .nav-me-eye {
            position: absolute; right: -0.35rem; bottom: -0.3rem;
            width: 1.1rem; height: 1.1rem; border-radius: 50%;
            display: inline-flex; align-items: center; justify-content: center;
            background: var(--sidebar-bg); color: var(--nav-accent);
        }
        .nav-me-eye .ui-ico { width: 0.75rem; height: 0.75rem; vertical-align: 0; }
        .nav-me-text { display: flex; flex-direction: column; min-width: 0; line-height: 1.2; }
        .nav-me-name, .nav-me-role { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
        .nav-me-name { font-family: var(--font-body); font-size: 0.86rem; font-weight: 700; color: var(--sidebar-text-active); }
        .nav-me-role { font-family: var(--font-body); font-size: 0.72rem; font-weight: 600; color: var(--sidebar-text); }
        .nav-me-as {
            display: inline-flex; flex-wrap: wrap; align-items: baseline; gap: 0 0.25rem; min-width: 0;
            padding: 0.15rem 0.45rem; border-radius: 6px;
            background: var(--nav-accent-soft); color: var(--sidebar-text-active);
            box-shadow: inset 0 0 0 1px var(--nav-accent);
            font-family: var(--font-body); font-size: 0.7rem; line-height: 1.25;
        }
        .nav-me-as-k { color: var(--nav-accent-text); font-weight: 600; }
        .nav-me-as-v { font-weight: 700; overflow-wrap: anywhere; }
        /* The sheet's own copy says "My App" out loud; the rail and the bar
           have the My App button right beside it. */
        .nav-me-go { display: none; }
        .sidebar-me-sheet { display: none; }
        /* The More sheet's Page tour and Help (base.html, THE PAGE'S TOUR
           AND HELP): a phone only, below. */
        .sheet-acts { display: none; }
        /* MY APP AS A PANEL. On a computer, My App opens as a large dialog
           over the page you are on instead of taking you to a page of its own.
           The link keeps its real address, so a phone, a new tab and a browser
           with no script all still go to the page. The rows inside are the
           ones /app-settings draws, brought in with their own styles. */
        .myapp-panel { padding: 0; border: 1px solid var(--border); border-radius: 14px;
            background: var(--page-bg); color: var(--text); box-shadow: var(--shadow-lift);
            width: min(900px, calc(100vw - 2rem)); max-width: none; max-height: 90vh;
            overflow: hidden; }
        .myapp-panel[open] { display: flex; flex-direction: column; }
        .myapp-panel::backdrop { background: rgba(15, 23, 42, 0.55); }
        .myapp-panel-bar { flex: none; display: flex; align-items: center; gap: 0.75rem;
            padding: 0.6rem 0.7rem 0.6rem 1.25rem; background: var(--card-bg);
            border-bottom: 1px solid var(--border); }
        .myapp-panel-bar h2 { margin: 0; font-size: 1.1rem; }
        .myapp-panel-status { flex: 1 1 auto; min-width: 0; font-size: 0.85rem;
            font-weight: 700; color: var(--green); }
        .myapp-panel-status.myapp-panel-bad { color: var(--red); }
        .myapp-panel-x { flex: none; display: grid; place-items: center; width: 2.5rem;
            height: 2.5rem; padding: 0; border-radius: 10px; border: 1px solid var(--border);
            background: var(--card-bg-alt); color: var(--text); cursor: pointer; }
        .myapp-panel-body { flex: 1 1 auto; min-height: 0; overflow-y: auto;
            overscroll-behavior: contain; padding: 1.1rem 1.25rem 1.5rem; }
        .myapp-panel-body:focus { outline: none; }
        .myapp-panel-body .ap-wrap { max-width: none; }
        .myapp-panel-body .ap-wrap > h1 { display: none; }
        html:has(.myapp-panel[open]) { overflow: hidden; }
        /* Privacy/Terms pinned at the very bottom of the sidebar card, out of
           the page content flow so it never disrupts full-height pages (chat). */
        .sidebar-footer {
            padding: 0.6rem 1.1rem 0.9rem; text-align: center; font-size: 0.75rem;
            color: var(--sidebar-text); opacity: 0.6; line-height: 1.4;
        }
        .sidebar-footer a { color: inherit; text-decoration: none; }
        /* SWITCH ACCOUNT (app/tools/account_switch.py): the menu, its two
           openers, and where the menu sits on each layout. The menu is a card
           over the page wherever it opens, so it wears card tokens, never the
           nav's - light, dark and overcast all repaint it.

           DISPLAY IS SPELLED OUT, NOT LEFT TO THE BROWSER. A browser without
           popovers would otherwise draw the menu inline under the nav, open
           for good; with these three rules it stays shut there until the
           fallback at the foot of this file adds .is-open. Three rules rather
           than one, because a selector a browser does not know drops the
           whole rule it is in. */
        .acct-menu { display: none; }
        .acct-menu:popover-open { display: block; }
        .acct-menu.is-open { display: block; z-index: 2147483000; }
        .acct-menu {
            position: fixed; inset: auto; margin: 0;
            left: calc(var(--rail-w) + 0.5rem); bottom: 0.75rem;
            width: min(22rem, calc(100vw - 1.5rem)); max-height: min(36rem, calc(100vh - 1.5rem));
            overflow-y: auto; overscroll-behavior: contain; box-sizing: border-box;
            padding: 0.45rem 0.55rem 0.55rem; border: 1px solid var(--border); border-radius: 14px;
            background: var(--card-bg); color: var(--text); box-shadow: var(--shadow-lift);
            font-family: var(--font-body); text-align: left;
        }
        .acct-menu-head {
            display: flex; align-items: center; justify-content: space-between; gap: 0.5rem;
            padding: 0.15rem 0.1rem 0.4rem 0.6rem;
        }
        .acct-menu-title {
            margin: 0; font-family: var(--font-body); font-size: 0.75rem; font-weight: 700;
            letter-spacing: 0.07em; text-transform: uppercase; color: var(--text-muted);
        }
        .acct-menu button { font-family: var(--font-body); }
        .acct-menu-close, .acct-x button {
            display: inline-flex; align-items: center; justify-content: center; flex: none;
            width: 2.25rem; height: 2.25rem; min-height: 0; padding: 0;
            border: none; border-radius: 8px; background: transparent; color: var(--text-muted); box-shadow: none;
        }
        .acct-menu-close:hover, .acct-menu-close:active { background: var(--gray-bg); color: var(--text); box-shadow: none; transform: none; }
        .acct-x button:hover, .acct-x button:active { background: var(--red-bg); color: var(--red); box-shadow: none; transform: none; }
        .acct-row {
            display: flex; align-items: center; gap: 0.7rem; width: 100%; min-height: 3.3rem;
            box-sizing: border-box; margin: 0; padding: 0.5rem 0.6rem; border: none; border-radius: 10px;
            background: transparent; color: var(--text); box-shadow: none; text-align: left;
            text-decoration: none; font-size: 0.93rem; font-weight: 400;
        }
        button.acct-row:hover, a.acct-row:hover, button.acct-row:active {
            background: var(--gray-bg); color: var(--text); box-shadow: none; transform: none;
        }
        .acct-row-me { background: var(--page-bg); margin-bottom: 0.3rem; }
        .acct-av {
            flex: none; width: 2.5rem; height: 2.5rem; border-radius: 50%; overflow: hidden;
            display: inline-flex; align-items: center; justify-content: center;
            background: var(--accent); color: var(--accent-on);
            font-size: 0.86rem; font-weight: 700; letter-spacing: 0.02em; line-height: 1;
        }
        .acct-av img { display: block; width: 100%; height: 100%; object-fit: cover; }
        .acct-li .acct-av { background: var(--accent-light); color: var(--accent-dark); }
        .acct-av-plus { background: transparent; color: var(--accent-dark); box-shadow: inset 0 0 0 1.5px var(--border); }
        .acct-av .ui-ico { width: 1.15rem; height: 1.15rem; vertical-align: 0; }
        .acct-txt { display: flex; flex-direction: column; flex: 1; min-width: 0; line-height: 1.25; }
        .acct-name, .acct-sub { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
        .acct-name { font-weight: 700; color: var(--text); }
        .acct-sub { font-size: 0.8rem; color: var(--text-muted); }
        .acct-here {
            flex: none; display: inline-flex; align-items: center; gap: 0.25rem;
            font-size: 0.74rem; font-weight: 700; color: var(--green);
        }
        .acct-here .ui-ico { width: 0.9rem; height: 0.9rem; vertical-align: 0; }
        .acct-list { list-style: none; margin: 0; padding: 0; }
        .acct-li { display: flex; align-items: center; gap: 0.15rem; margin: 0; }
        .acct-li form { margin: 0; }
        .acct-go { flex: 1; min-width: 0; }
        .acct-hint {
            display: inline-flex; align-items: center; gap: 0.25rem; margin-top: 0.1rem;
            font-size: 0.74rem; font-weight: 600; color: var(--text-muted); white-space: nowrap;
        }
        .acct-hint .ui-ico { width: 0.85rem; height: 0.85rem; vertical-align: 0; }
        .acct-full { margin: 0.35rem 0.6rem 0.2rem; font-size: 0.82rem; line-height: 1.4; color: var(--text-muted); }
        .acct-foot {
            display: flex; flex-direction: column; gap: 0.1rem;
            margin-top: 0.4rem; padding-top: 0.4rem; border-top: 1px solid var(--border);
        }
        .acct-foot form { margin: 0; }
        .acct-out {
            display: flex; align-items: center; gap: 0.6rem; width: 100%; min-height: 2.6rem;
            margin: 0; padding: 0.45rem 0.6rem; border: none; border-radius: 8px;
            background: transparent; color: var(--text); box-shadow: none;
            font-size: 0.9rem; font-weight: 600; text-align: left;
        }
        .acct-out:hover, .acct-out:active { background: var(--gray-bg); color: var(--text); box-shadow: none; transform: none; }
        .acct-out .ui-ico { width: 1.05rem; height: 1.05rem; color: var(--text-muted); vertical-align: 0; }
        .acct-menu :focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
        /* The phone's opener lives in the More sheet, which a computer never
           shows; the computer's is the Switch button beside My App. */
        .acct-sheet-btn { display: none; }
        @media (max-width: 900px) {
            /* A sheet from the bottom of the screen, over the More sheet. */
            .acct-menu {
                left: 0; right: 0; bottom: 0; top: auto; width: 100%; max-height: 88vh;
                border-width: 1px 0 0; border-radius: 18px 18px 0 0;
                padding: 0.35rem 0.85rem calc(0.9rem + env(safe-area-inset-bottom, 0px));
            }
            .acct-menu::backdrop { background: rgba(9, 14, 24, 0.45); }
            .acct-row { min-height: 3.6rem; }
            .acct-out { min-height: 3rem; font-size: 0.95rem; }
            .sidebar-user .sidebar-user-switch { display: none; }
            .acct-sheet-btn {
                display: flex; align-items: center; gap: 0.75rem; width: 100%; min-height: 3.25rem;
                box-sizing: border-box; margin: 0.45rem 0 0; padding: 0.55rem 0.85rem;
                border: 1px solid var(--border); border-radius: 14px;
                background: var(--card-bg); color: var(--text); box-shadow: none;
                font-size: 0.95rem; font-weight: 600; text-align: left;
            }
            .acct-sheet-btn:hover, .acct-sheet-btn:active { background: var(--gray-bg); color: var(--text); box-shadow: none; transform: none; }
            .acct-sheet-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
            .acct-sheet-ico {
                flex: none; width: 2.75rem; display: inline-flex; justify-content: center; color: var(--accent-dark);
            }
            .acct-sheet-ico .ui-ico { width: 1.3rem; height: 1.3rem; vertical-align: 0; }
            .acct-sheet-txt { display: flex; flex-direction: column; flex: 1; min-width: 0; line-height: 1.25; }
            .acct-sheet-k { font-weight: 700; }
            .acct-sheet-v { font-size: 0.82rem; font-weight: 500; color: var(--text-muted); }
            .acct-sheet-go { flex: none; display: inline-flex; color: var(--text-muted); }
            .acct-sheet-go .ui-ico { vertical-align: 0; }
        }
        .sidebar-footer a:hover { text-decoration: underline; opacity: 1; }
        .theme-toggle {
            display: block; width: 100%; text-align: left; margin: 0.5rem 0 0.1rem;
            background: transparent; border: 1px solid var(--sidebar-border); color: var(--sidebar-text);
            padding: 0.5rem 0.6rem; border-radius: var(--radius-sm); font-size: 0.85rem; font-weight: 500;
            cursor: pointer; min-height: auto; box-shadow: none;
        }
        .theme-toggle:hover { color: var(--nav-accent); border-color: var(--nav-accent); background: var(--nav-accent-soft); }

        /* THE APPEARANCE PICKERS draw themselves: _appearance_controls.html
           carries its own styles (radio groups on the shared .seg track and
           theme circles), for My App and the setup walk alike. The sidebar
           swatch row and its light-card remap that stood here had nothing
           left to style. *//* Classic and Modern each have their own options: only the picked
           look's are shown, on My App and in the setup walk. */
        :root:not([data-look~="modern"]) .look-modern-only,
        :root[data-look~="modern"] .look-classic-only { display: none !important; }
        /* CLASSIC'S TYPE IS SOURCE SANS 3, menu and page alike. "I really do
           not like the font the Classic version uses" - it was Barlow
           Condensed in the menu over the computer's own system font, so the
           look changed with the machine. Picked B on the font mockup. The
           face is self-hosted (vendor/fonts). Modern and Professional keep
           their own. */
        :root:not([data-look~="modern"]):not([data-theme="professional"]) {
            --font-body: "Source Sans 3", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif;
            --font-display: var(--font-body);
            --font-nav: var(--font-body);
        }

        .app-main {
            flex: 1; margin-left: calc(var(--sidebar-width) + var(--sidebar-gap) * 2);
            min-width: 0; display: flex; flex-direction: column;
        }
        .topbar {
            display: none; align-items: center; gap: 0.75rem; padding: 0.75rem 1.25rem;
            background: var(--card-bg); border-bottom: 1px solid var(--border); position: sticky; top: 0; z-index: 20;
            box-shadow: var(--shadow);
        }
        .topbar-brand { font-weight: 700; color: var(--text); font-size: 1rem; }
        /* A PAGE THAT COMES BACK TO AN ANCHOR LANDS BELOW THE STICKY BAR.
           The driver's Home forms and the photo in My App reload to #vto,
           #cycle0, #late or #photo, and the browser put that spot at the very
           top of the screen: under the phone's title bar, so the notice saying
           it worked was hidden or cut to its last word. The bar is about
           3.5rem on a phone (3.4rem with a touch screen's bigger buttons); the
           side bar layout has nothing across the top, and the top bar layout
           has its own value in the desktop block. Each leaves a little air. */
        html { scroll-padding-top: 1rem; }
        @media (max-width: 900px) { html { scroll-padding-top: 4.5rem; } }
        .sidebar-overlay { display: none; position: fixed; inset: 0; background: rgba(15, 23, 42, 0.4); z-index: 25; }
        /* THE FORCED SET A NEW PASSWORD SCREEN SHOWS ONLY ITSELF. Every row of
           the nav, the More sheet, the page and station chips and the My App and Search
           buttons on the top bar would only send somebody back to it, so on a
           gate screen they are not shown and the page takes the full width.
           The tab bar is not drawn there at all. */
        html.gate-screen .sidebar, html.gate-screen .sidebar-overlay,
        html.gate-screen .topbar-actions, html.gate-screen .topbar-chips,
        html.gate-screen .station-chip { display: none !important; }
        html.gate-screen .app-main { margin-left: 0 !important; }

        /* THE PAGE ONLY SLIDES WHEN SOMEBODY ASKS IT TO. The fold arrow, a rail
           square and the Side/Top switch add .nav-animating for a moment (see
           zipsterNavAnimate in <head>). It was animating always, so anything
           that moved the margin on its own - the panel's width arriving a beat
           after the page was first drawn - turned into a slide across the
           screen, with the Messenger window on it. */
        .app-main { transition: none; }
        html.nav-animating .app-main { transition: margin-left 0.2s ease; }
        /* THE FOLD ARROW FOLDS THE PANEL NOW, NOT THE NAV. It used to slide
           the whole sidebar off the screen and leave a floating button to bring
           it back; with the rail that is 76px of nav you would lose for nothing.
           html.nav-collapsed is still the one flag, set pre-paint and
           remembered, and the side bar's rules read it to hide the panel. The
           top bar has nothing to fold. */
        .sidebar-collapse { margin-left: auto; font-size: 0.95rem; }
        .sidebar-collapse:hover { color: var(--nav-accent); border-color: var(--nav-accent); background: var(--nav-accent-soft); }

        main { max-width: 1560px; margin: 0 auto; padding: 2rem 1.75rem 4rem; width: 100%; }

        /* ---------------- Forms ---------------- */
        label { display: block; font-weight: 600; font-size: 0.85rem; color: var(--text); margin: 1rem 0 0.3rem; }
        label:first-of-type { margin-top: 0; }
        /* A tick-box option is a label too, so the stacked-form 1rem top margin
           landed on every option after the first: in a row that pushed the
           second option a whole line below its neighbour (the misaligned
           sub-options on the Form Builder), and in a column it made the gaps
           uneven. The margin still separates the first option from whatever
           field sits above it - option-to-option spacing is the group's gap. */
        label:is(:has(> input[type="checkbox"]), :has(> input[type="radio"]))
            + label:is(:has(> input[type="checkbox"]), :has(> input[type="radio"])) { margin-top: 0; }

        /* A horizontal or stacked group of tick-box options. Sizing the box
           explicitly is what lets a nested group indent to exactly the parent
           option's text (--opt-indent) instead of guessing at the browser's
           checkbox width. */
        .opt-group { --opt-box: 1rem; --opt-gap: 0.5rem; --opt-indent: calc(var(--opt-box) + var(--opt-gap));
            display: flex; flex-direction: column; gap: 0.45rem; }
        .opt-group.is-row { flex-direction: row; flex-wrap: wrap; gap: 0.45rem 1.4rem; }
        .opt-group > label { display: flex; align-items: center; gap: var(--opt-gap); margin: 0; font-weight: 400; }
        .opt-group > label > input[type="checkbox"],
        .opt-group > label > input[type="radio"] {
            flex: 0 0 auto; width: var(--opt-box); height: var(--opt-box); margin: 0;
        }
        .opt-group .opt-note { color: var(--text-muted); font-weight: 400; font-size: 0.82rem; }
        .opt-group .opt-sub { margin-left: var(--opt-indent); }
        input, select, textarea {
            display: block; width: 100%; padding: 0.6rem 0.75rem; box-sizing: border-box;
            font-family: inherit; font-size: 1rem; color: var(--text);
            border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--input-bg);
            transition: var(--transition);
        }
        input:focus, select:focus, textarea:focus {
            outline: none; border-color: var(--accent); box-shadow: var(--accent-glow);
        }
        /* Explicitly theme the dropdown option list too (belt-and-suspenders with
           color-scheme) so it's never light-on-white in dark mode. */
        select option, select optgroup { background: var(--card-bg); color: var(--text); }

        /* ---------------- The native controls ----------------

           THE LOUDEST SIGNAL THAT A PAGE IS OLD is a control the operating
           system drew. A file field renders as "Choose File | No file chosen"
           in the browser's own grey; a dropdown carries the OS triangle; a tick
           box comes out in whatever colour the browser feels like, which is
           nothing like this app's accent in any of the eight themes.

           There are 35 file fields, 143 dropdowns and 129 tick boxes across
           the templates. Styling them HERE lifts every one of them at once,
           including the pages nobody thought to go and look at, and keeps them
           in step with every theme - which a fix pasted per page cannot do and
           has repeatedly failed to do. */

        /* One declaration, and every native tick box, radio, slider and
           progress bar in the app takes the theme's accent instead of the
           browser's idea of one. */
        :root { accent-color: var(--accent); }

        /* A tick box is a tick box wherever it sits. The `input` rule above
           sets width:100%, which a tick box must never take, and only the ones
           inside a label element were being rescued from it - so a bare one stretched
           the width of its column and templates worked around that with
           style="width:auto" one at a time. Sized here, for all of them.
           (.opt-group's own rule is more specific and still wins, so the
           indent arithmetic that keys off --opt-box is untouched.) */
        input[type="checkbox"], input[type="radio"],
        label input[type="checkbox"], label input[type="radio"] {
            display: inline-block; width: 1rem; height: 1rem; min-height: 0;
            margin: 0 0.4rem 0 0; vertical-align: middle; cursor: pointer;
        }
        input[type="checkbox"]:disabled, input[type="radio"]:disabled {
            cursor: not-allowed; opacity: 0.55;
        }

        
        input:is([type="checkbox"], [type="radio"]):where(:not(.switch, [role="switch"])),
        label input:is([type="checkbox"], [type="radio"]):where(:not(.switch, [role="switch"])) {
            -webkit-appearance: none; appearance: none; flex: none; box-sizing: border-box;
            display: inline-grid; place-items: center; padding: 0;
            border: 1px solid color-mix(in srgb, var(--text-muted) 55%, var(--card-bg)); border-radius: 4px;
            background: var(--card-bg); box-shadow: 0 1px 1px color-mix(in srgb, var(--text) 6%, transparent);
            transition: background-color 150ms ease, border-color 150ms ease;
        }input[type="radio"]:where(:not(.switch, [role="switch"])),
        label input[type="radio"]:where(:not(.switch, [role="switch"])) { border-radius: 50%; }
        input:is([type="checkbox"], [type="radio"]):where(:not(.switch, [role="switch"]))::before {
            content: ""; width: 62.5%; height: 62.5%; background: var(--accent-gradient-on);
        }
        input[type="checkbox"]:where(:not(.switch, [role="switch"]))::before {
            -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/svg%3E") center / contain no-repeat;
                    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/svg%3E") center / contain no-repeat;
            clip-path: inset(0 100% 0 0);
        }
        input[type="checkbox"]:where(:not(.switch, [role="switch"])):indeterminate::before {
            -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3.5' stroke-linecap='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'/%3E%3C/svg%3E");
                    mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3.5' stroke-linecap='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'/%3E%3C/svg%3E");
        }input:is([type="checkbox"], [type="radio"]):where(:not(.switch, [role="switch"])):is(:checked, :where([type="checkbox"]):indeterminate) {
            background: linear-gradient(180deg, rgba(255, 255, 255, 0.18), rgba(255, 255, 255, 0) 70%), var(--accent-gradient);
            border-color: color-mix(in srgb, var(--accent) 55%, transparent);
            box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.35), 0 1px 1px color-mix(in srgb, var(--text) 8%, transparent);
        }
        input[type="checkbox"]:where(:not(.switch, [role="switch"])):is(:checked, :indeterminate)::before {
            clip-path: inset(0 0 0 0); transition: clip-path 180ms cubic-bezier(0.2, 0.8, 0.2, 1);
        }
        input[type="radio"]:where(:not(.switch, [role="switch"]))::before {
            width: 37.5%; height: 37.5%; border-radius: 50%; opacity: 0; transform: scale(0.6);
        }
        input[type="radio"]:where(:not(.switch, [role="switch"])):checked::before {
            opacity: 1; transform: none; transition: opacity 150ms ease-out, transform 150ms ease-out;
        }
        input:is([type="checkbox"], [type="radio"]):where(:not(.switch, [role="switch"])):disabled { background: var(--bg-subtle); box-shadow: none; }
        input:is([type="checkbox"], [type="radio"]):where(:not(.switch, [role="switch"])):focus { outline: none; }
        input:is([type="checkbox"], [type="radio"]):where(:not(.switch, [role="switch"])):focus-visible {
            outline: 2px solid var(--accent); outline-offset: 2px;
        }
        @media (prefers-reduced-motion: reduce) {
            input:is([type="checkbox"], [type="radio"]):where(:not(.switch, [role="switch"])),
            input:is([type="checkbox"], [type="radio"]):where(:not(.switch, [role="switch"]))::before { transition: none !important; }
        }

        /* THE FILE FIELD. The row is ours - border, radius, the theme's input
           background. The button inside it is drawn as a quiet secondary
           button so it matches every other "do a thing" control on the page
           rather than the OS. ::file-selector-button is the standard name;
           Chrome and Safari still answer to the -webkit- one, and a browser
           that knows neither keeps its own button inside a field that at least
           belongs to this app. */
        input[type="file"] { padding: 0.4rem 0.6rem; cursor: pointer; font-size: 0.88rem; }
        /* A file box marked hidden stays hidden. The form rules give inputs a
           display, which beats the browser's own [hidden] rule, so My App's
           Profile photo showed a "Choose File / No file chosen" box over its
           title on a phone, beside the Change photo button that opens it. */
        input[type="file"][hidden] { display: none !important; }
        input[type="file"]::file-selector-button,
        input[type="file"]::-webkit-file-upload-button {
            margin-right: 0.75rem; padding: 0.4rem 0.8rem; cursor: pointer;
            font-family: inherit; font-size: 0.82rem; font-weight: 600;
            border: 1px solid var(--accent); border-radius: var(--radius-sm);
            background: var(--accent-light); color: var(--accent-dark);
            transition: var(--transition);
        }
        input[type="file"]:hover::file-selector-button,
        input[type="file"]:hover::-webkit-file-upload-button {
            background: var(--accent); border-color: var(--accent-dark); color: var(--accent-on, #fff);
        }

        /* THE DROPDOWN. appearance:none drops the OS triangle; the two
           gradients draw the replacement out of currentColor, which is the
           theme's own text colour - so it is right in all eight themes with no
           per-theme override and no SVG carrying a colour baked into it. The
           45deg wedge sits left of the 135deg one, and together they read as a
           chevron. A multi-select or a sized list is a scrolling box and not a
           dropdown at all, so neither gets an arrow or the room for one. */
        select:not([multiple]):not([size]) {
            -webkit-appearance: none; appearance: none; cursor: pointer;
            padding-right: 2.1rem;
            background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                              linear-gradient(135deg, currentColor 50%, transparent 50%);
            background-size: 0.3rem 0.3rem, 0.3rem 0.3rem;
            background-position: right 1.15rem center, right 0.85rem center;
            background-repeat: no-repeat;
        }
        select:disabled { cursor: not-allowed; }

        /* THE DATE AND TIME PICKERS. The calendar glyph belongs to the browser
           and color-scheme (set by every theme) already keeps it legible. What
           it lacks is a target worth hitting and any sign that it is one. */
        input[type="date"]::-webkit-calendar-picker-indicator,
        input[type="time"]::-webkit-calendar-picker-indicator,
        input[type="datetime-local"]::-webkit-calendar-picker-indicator,
        input[type="month"]::-webkit-calendar-picker-indicator,
        input[type="week"]::-webkit-calendar-picker-indicator {
            cursor: pointer; padding: 0.2rem; border-radius: 4px;
            opacity: 0.6; transition: var(--transition);
        }
        input[type="date"]:hover::-webkit-calendar-picker-indicator,
        input[type="time"]:hover::-webkit-calendar-picker-indicator,
        input[type="datetime-local"]:hover::-webkit-calendar-picker-indicator,
        input[type="month"]:hover::-webkit-calendar-picker-indicator,
        input[type="week"]:hover::-webkit-calendar-picker-indicator { opacity: 1; }

        /* "DATE PICKERS ON MOBILE STRETCH OUT THE SCREEN." Two iPhone habits,
           both about the date field and neither visible in a desktop browser.

           WIDER THAN ITS BOX. Safari on iPhone draws a date field in its own
           native style with a floor on its width, so the field ignores the
           width the page gives it and pushes the page sideways. Dropping the
           native look is what makes Safari honour the width, and min-width: 0
           takes the floor away so a grid or flex column can shrink it like
           any other field.

           ZOOMED IN ON TAP. Safari zooms the whole page into any field whose
           text is under 16px and leaves it zoomed after the picker closes.
           Seven pages shrink their date fields below that, so on iPhone they
           are held at 16px.

           The @supports test matches Safari on iPhone and iPad only (and the
           other iPhone browsers, which are Safari underneath). A desktop and
           Android already fit, and nothing about them changes. */
        @supports (-webkit-touch-callout: none) {
            input[type="date"], input[type="time"], input[type="datetime-local"],
            input[type="month"], input[type="week"] {
                -webkit-appearance: none; appearance: none;
                min-width: 0; max-width: 100%;
                font-size: 16px !important;
            }
            /* Without the native look an empty field would collapse to a
               sliver, and Safari centres the date where every other field
               starts on the left. */
            input::-webkit-date-and-time-value { min-height: 1.4em; margin: 0; text-align: left; }

            /* ZOOMED IN ON TAP, EVERY BOX AND NOT ONLY THE DATE ONES. The same
               Safari habit as above: any field whose text is under 16px zooms
               the page on tap and leaves it zoomed. Pages set their fields to 13
               to 15px (the Loadout's 116 boxes at 0.88rem, the Rentals filters,
               Shift Types, Service Type Compliance, the Employees search ...), so
               a dispatcher on an iPhone pinched the page back out after every
               tap. Every text box, list and text area is held at 16px here.
               Ticks, radios, sliders, colours, files and buttons are not text
               boxes and keep their size. */
            input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="color"]):not([type="file"]):not([type="button"]):not([type="submit"]):not([type="reset"]):not([type="image"]):not([type="hidden"]),
            select, textarea { font-size: 16px !important; }
        }

        /* A COLOUR is a swatch, not a text field with a colour sitting in it. */
        input[type="color"] { width: 3.25rem; height: 2.35rem; padding: 0.2rem; cursor: pointer; }

        /* PROGRESS, so the next page that needs a bar does not write a third
           private track-and-fill in a <style> block of its own. */
        progress {
            -webkit-appearance: none; appearance: none;
            display: block; width: 100%; height: 0.6rem; border: 0;
            border-radius: 999px; overflow: hidden; background: var(--gray-bg);
        }
        progress::-webkit-progress-bar { background: var(--gray-bg); border-radius: 999px; }
        progress::-webkit-progress-value { background: var(--accent); border-radius: 999px; }
        progress::-moz-progress-bar { background: var(--accent); border-radius: 999px; }

        .pw-field { position: relative; }
        .pw-field input { padding-right: 3.5rem; }
        .pw-toggle {
            position: absolute; right: 0.5rem; top: 50%; transform: translateY(-50%);
            background: none; border: none; box-shadow: none; padding: 0.2rem 0.3rem; margin: 0;
            width: auto; color: var(--text-muted); font-size: 0.8rem; font-weight: 600; cursor: pointer;
        }
        .pw-toggle:hover { color: var(--accent); background: none; box-shadow: none; transform: translateY(-50%); }

        /* Quiet by default - most buttons on a page are secondary actions
           (Edit, Refresh, Cancel, a toggle inside a card), and giving every
           one of them the same bold gradient pill reads as a wall of noise
           the moment more than a couple appear together (confirmed live:
           the Scheduler board, with several buttons per shift card times a
           week of cards, looked "pretty bad"). Reserve the bold look for
           .btn-primary - the one or two genuinely primary actions on a
           page (a form's Save/Submit, Log in) - so it actually stands out
           instead of competing with everything else.

           OUTLINED, NOT TINTED. Picked in One Look: "B, amber main, outlined
           others". Every other button was an amber-tinted box with an amber
           edge, so a row of Edit, Refresh and Cancel looked like a row of
           second Saves and the one amber button that mattered had company.
           Now the rest stand on the card's own ground with a hairline in
           --border and the page's ink; hover greys the ground and firms the
           outline, pressing firms it again. No lift and no glow: those belong
           to the primary button. Tokens only, so all four themes get it with
           no patch of their own (--text on --card-bg and on --gray-bg is
           9:1 or better in every one). */
        button, .btn, .btn-nav {
            padding: 0.5rem 1rem; cursor: pointer; font-family: inherit; font-size: 0.88rem; font-weight: 600;
            border-radius: var(--radius-sm); border: 1px solid var(--border); background: var(--card-bg); color: var(--text);
            transition: var(--transition); min-height: 2.35rem; box-shadow: none;
        }
        button:hover, .btn:hover, .btn-nav:hover:not(:disabled) {
            background: var(--gray-bg); border-color: var(--text-muted); color: var(--text);
            box-shadow: none; transform: none;
        }
        button:active, .btn:active, .btn-nav:active:not(:disabled) {
            background: var(--gray-bg); border-color: var(--text); color: var(--text);
            box-shadow: inset 0 1px 2px color-mix(in srgb, var(--text) 14%, transparent); transform: none;
        }
        /* A LINK THAT IS A BUTTON IS THE SAME BOX. A link with the btn class
           was an inline box, and min-height does nothing to one: on a touch
           screen a button was 2.75rem tall and the link beside it about
           2.4rem, with its words riding high. Pages are moving their buttons
           wrapped in links to links with the class, so that gap would have
           cost every one of them its thumb-sized target. As an inline flex
           box it takes the height and centres its words. The gap stands in for
           the typed space between an icon and its label, which a flex box
           drops, and the line height is a button's rather than the body's, so
           on a desk the two are one height too. The toolbar names it as well,
           because .toolbar a sets every link in a toolbar to inline-block. */
        a.btn, a.btn-secondary, a.btn-ghost, a.btn-nav, .toolbar a.btn {
            display: inline-flex; align-items: center; justify-content: center; gap: 0.35em;
            box-sizing: border-box; line-height: 1.25; text-decoration: none;
        }
        
        :where(button:not([hidden]):has(> .ui-ico)) {
            display: inline-flex; align-items: center; justify-content: center; gap: 0.35em;
            vertical-align: middle;
        }
        button:disabled, .btn:disabled {
            background: var(--gray-bg); border-color: var(--border); color: var(--text-muted);
            box-shadow: none; cursor: not-allowed; transform: none;
        }
        
        :where(span.btn) {
            display: inline-flex; align-items: center; justify-content: center; gap: 0.35em;
            box-sizing: border-box; line-height: 1.25;
        }
        .btn:is(.is-off, [aria-disabled="true"]),
        .btn:is(.is-off, [aria-disabled="true"]):is(:hover, :active) {
            background: var(--gray-bg); border-color: var(--border); color: var(--text-muted);
            box-shadow: none; cursor: not-allowed; transform: none;
        }
        a[href].btn:is(.is-off, [aria-disabled="true"]) { pointer-events: none; }
        /* A SYNC NOW WAITING OUT ITS FIVE MINUTES ("Sync again in 4:12"). The
           disabled look above is already the calm one, in the theme's own muted
           tokens; this only keeps the countdown from twitching as its digits
           change, and does not call a wait "not allowed". */
        button.is-sync-held, button[data-sync-wait-left] {
            font-variant-numeric: tabular-nums; cursor: default;
        }
        /* --accent-gradient-on RATHER THAN #fff, and this is the whole reason that token
           exists. The fill is the brand's #E8901B: white on it is 2.49:1 and the
           label disappears, where the dark ink is 6.88:1. Professional still
           resolves --accent-gradient-on to white, because its fill is graphite. */
        .btn-primary {
            border-color: var(--accent-dark); background: var(--accent-gradient); color: var(--accent-gradient-on);
            box-shadow: 0 1px 2px rgba(154, 88, 6, 0.28);
        }
        .btn-primary:hover {
            background: var(--accent-gradient-hover); border-color: var(--accent-darker); color: var(--accent-gradient-on);
            box-shadow: 0 8px 18px -8px rgba(232, 144, 27, 0.85); transform: translateY(-1px);
        }
        .btn-primary:active { background: var(--accent-gradient-hover); transform: translateY(0); box-shadow: 0 1px 2px rgba(154, 88, 6, 0.28); }
        .btn-primary:disabled {
            background: var(--gray-bg); border-color: var(--border); color: var(--text-muted);
            box-shadow: none; cursor: not-allowed; transform: none;
        }
        form.inline { display: inline; }
        /* THE DELETE IS THE SAME OUTLINE IN RED INK. Picked in One Look with
           the buttons: a Delete stands on the card's ground with the same
           hairline as every other button and says what it does in --red;
           hover washes it in --red-bg and turns the outline red. --red on
           --red-bg measures 5.3:1 or better in all four themes. */
        form.inline button {
            background: var(--card-bg); color: var(--red); border: 1px solid var(--border);
            padding: 0.35rem 0.7rem; font-size: 0.82rem; min-height: auto; box-shadow: none;
        }
        form.inline button:hover { background: var(--red-bg); border-color: var(--red); color: var(--red); transform: none; box-shadow: none; }
        /* .btn-success overrides form.inline's default red (delete-style) look for
           completion actions like "Mark Fixed"/"Mark Returned", which aren't destructive. */
        form.inline button.btn-success {
            background: var(--card-bg); color: var(--green); border: 1px solid var(--border);
        }
        form.inline button.btn-success:hover { background: var(--green-bg); border-color: var(--green); color: var(--green); }
        /* Same idea for .btn-primary. form.inline button is (0,1,2) and
           .btn-primary is (0,1,0), so a button that ASKED to be primary lost to
           the delete-style default and came out looking destructive - which is
           how "Sign" on a driver's own write-up, and both "Sync from ADP"
           buttons, have been rendering. A form being inline says where it sits,
           not that its action is dangerous. */
        form.inline button.btn-primary {
            background: var(--accent-gradient); color: var(--accent-gradient-on); border-color: var(--accent-dark);
            box-shadow: 0 1px 2px rgba(154, 88, 6, 0.28);
        }
        form.inline button.btn-primary:hover {
            background: var(--accent-gradient-hover); border-color: var(--accent-darker); color: var(--accent-gradient-on);
        }
        /* AN INLINE FORM SAYS WHERE A BUTTON SITS, NOT THAT IT IS DANGEROUS.

           form.inline's default is the red delete look, which is right for the
           overwhelming majority of one-button forms in this app - they are
           deletes. It is wrong for the sixteen that are not, and it was quietly
           telling somebody that "Print Truck Log", "Export Week", "Remind" and
           "Check ADP scope" would destroy something.

           .btn-success and .btn-primary already existed as escapes for the two
           loud intents. This is the missing third: give me back the ORDINARY
           secondary button, for an action that is neither destructive nor the
           point of the page. Most of the sixteen want exactly that - which,
           since One Look, is the outlined button in the page's own ink. */
        form.inline button.btn-plain {
            background: var(--card-bg); color: var(--text); border: 1px solid var(--border);
        }
        form.inline button.btn-plain:hover:not(:disabled) {
            background: var(--gray-bg); border-color: var(--text-muted); color: var(--text);
        }
        /* A DISABLED BUTTON IS DISABLED WHATEVER ITS INTENT.

           button:disabled is (0,1,1). Every intent class above beats it -
           form.inline button.btn-success is (0,2,2) - so a greyed-out action
           kept its green or indigo ink and its live border, and read as
           something you could still press. PAVE's "Mark Quarter Complete" sat
           there in green while the quarter had 23 vehicles left in it. */
        form.inline button:disabled,
        form.inline button.btn-plain:disabled,
        form.inline button.btn-success:disabled,
        form.inline button.btn-primary:disabled,
        .btn-primary:disabled, .btn-gold:disabled, .btn-danger:disabled, .btn-nav:disabled {
            background: var(--gray-bg); border-color: var(--border); color: var(--text-muted);
            box-shadow: none; cursor: not-allowed; transform: none;
        }
        .btn-gold { background: var(--gold-gradient); border-color: var(--gold); color: #fff; box-shadow: 0 1px 2px rgba(180, 83, 9, 0.25); }
        .btn-gold:hover { background: linear-gradient(135deg, #b45309, #92400e); border-color: #92400e; box-shadow: 0 4px 12px rgba(180, 83, 9, 0.35); }
        /* Same red destructive-action look as form.inline's default button,
           made available as an explicit class so any button, or a link to a
           permanent-removal confirmation page, can read as destructive. The
           outline is the neutral hairline every button has; the red is the
           ink, and on hover the wash and the edge. */
        a > button.btn-danger, .btn-danger {
            background: var(--card-bg); border: 1px solid var(--border); color: var(--red);
            box-shadow: none;
        }
        a:hover > button.btn-danger, .btn-danger:hover:not(:disabled) {
            background: var(--red-bg); border-color: var(--red); color: var(--red); box-shadow: none;
        }
        /* BUTTON TIERS. "Some things should stand out more" (2026-11-07): on
           any group of actions ONE is the main one and the rest step back, by
           how often and how much each is used, not the order it was built.

             primary    .btn-primary       the filled accent, one per group
             secondary  .btn / .btn-secondary
                                           the outlined button on the card's
                                           ground (One Look's "outlined others")
             tertiary   .btn-ghost         no edge, the page's ink, a soft tint
                        .btn-text          under the pointer; .btn-text is the
                                           same with less side room, for a
                                           link-like action in a line of words
             quiet destructive
                        .btn-danger-quiet  muted ink and no edge until hovered
                                           or focused, then the red wash: a
                                           Reset or a Delete that never looks
                                           like the main action
             More       _more_menu.html    three or more tertiary actions in a
                                           bar fold into the kit's dropdown
                                           (icon, bold name, short note)

           A bar whose buttons are genuinely equal choices (a segmented choice,
           a row of filters) says so with data-btn-equal, and the guard in
           tests/test_one_main_action_per_button_group.py leaves it be.

           Two sizes only: the default and .btn-sm. Words stay at 600. The box
           sits in :where() so it weighs nothing: a button already gets it from
           the rule above, and a page that sizes its own quiet button still
           wins. Everything is a token, so Light, Dusk, Dark and Professional
           need no patch of their own (Professional's graphite keys skip these
           names, below). */
        :where(.btn-secondary, .btn-ghost, .btn-text, .btn-danger-quiet) {
            padding: 0.5rem 1rem; cursor: pointer; font-family: inherit; font-size: 0.88rem; font-weight: 600;
            border-radius: var(--radius-sm); min-height: 2.35rem; transition: var(--transition);
        }
        .btn-secondary, .btn-ghost { border: 1px solid var(--border); color: var(--text); box-shadow: none; }
        .btn-secondary { background: var(--card-bg); }
        .btn-ghost { background: transparent; }
        .btn-ghost, .btn-text, .btn-danger-quiet {
            background: transparent; border: 1px solid transparent; color: var(--text); box-shadow: none;
        }
        :where(.btn-text) { padding-left: 0.55rem; padding-right: 0.55rem; }
        .btn-danger-quiet { color: var(--text-muted); }
        :where(a.btn-text, a.btn-danger-quiet, span.btn-text) {
            display: inline-flex; align-items: center; justify-content: center; gap: 0.35em;
            box-sizing: border-box; line-height: 1.25; text-decoration: none;
        }
        .btn-secondary:is(:hover, :active):where(:not(:disabled)) {
            background: var(--gray-bg); border-color: var(--text-muted); color: var(--text);
            box-shadow: none; transform: none;
        }
        :is(.btn-ghost, .btn-text):is(:hover, :active):where(:not(:disabled, [aria-disabled="true"], .is-off)) {
            background: color-mix(in srgb, var(--text) 7%, transparent); border-color: transparent; color: var(--text);
            box-shadow: none; transform: none;
        }
        .btn-danger-quiet:is(:hover, :active, :focus-visible):where(:not(:disabled)) {
            background: var(--red-bg); border-color: transparent; color: var(--red); box-shadow: none; transform: none;
        }
        /* An inline form's red delete look is for a bare button; a button that
           names its tier keeps it. */
        form.inline button:is(.btn-ghost, .btn-text) { background: transparent; border-color: transparent; color: var(--text); }
        form.inline button.btn-danger-quiet { background: transparent; border-color: transparent; color: var(--text-muted); }
        form.inline button:is(.btn-ghost, .btn-text):hover:not(:disabled) {
            background: color-mix(in srgb, var(--text) 7%, transparent); border-color: transparent; color: var(--text);
        }
        form.inline button.btn-danger-quiet:is(:hover, :focus-visible):not(:disabled) {
            background: var(--red-bg); border-color: transparent; color: var(--red);
        }
        :is(.btn-ghost, .btn-text, .btn-danger-quiet):disabled {
            background: transparent; border-color: transparent; color: var(--text-muted); opacity: 0.6; cursor: not-allowed;
        }
        /* The ring every tier keeps under the keyboard, in the accent. */
        :where(.btn-primary, .btn-secondary, .btn-ghost, .btn-text, .btn-danger-quiet, .btn-more > summary):focus-visible {
            outline: 2px solid var(--accent); outline-offset: 2px;
        }
        /* MORE: the overflow for a bar's rarely-used actions. The kit's
           dropdown (.menu-dropdown + .menu-panel, keys and roles from kit.js)
           under a ghost "More" trigger; each row an icon, a bold name and a
           short note. It opens toward the left, from the bar's right end. */
        .btn-more > summary { display: inline-flex; align-items: center; gap: 0.35em; white-space: nowrap; }
        .btn-more > .menu-panel { min-width: 15.5rem; text-align: left; white-space: normal; }
        .btn-more .menu-name > b { font-weight: 600; }
        .btn-more .menu-item { justify-content: flex-start; font-weight: 400; }
        .btn-more form { margin: 0; }
        .btn-more form > .menu-item { width: 100%; }
        @media (prefers-reduced-motion: reduce) {
            :is(.btn-secondary, .btn-ghost, .btn-text, .btn-danger-quiet) { transition: none; }
        }
        /* Quill's class-based Size/Color/Background/Align formats (see
           app/tools/html_sanitize.py) - defined globally, not just inside
           the editor's own stylesheet, so a saved write-up/coaching body
           still renders with the chosen formatting wherever it's displayed
           (profile, review, my-write-ups), not just inside the editor. */
        .ql-size-small { font-size: 0.75em; }
        .ql-size-large { font-size: 1.5em; }
        .ql-size-huge { font-size: 2.5em; }
        .ql-color-000000 { color: #000000; }
        .ql-color-dc2626 { color: #dc2626; }
        .ql-color-d97706 { color: #d97706; }
        .ql-color-059669 { color: #059669; }
        .ql-color-2563eb { color: #2563eb; }
        .ql-color-7c3aed { color: #7c3aed; }
        .ql-background-000000 { background-color: #000000; color: #fff; }
        .ql-background-dc2626 { background-color: #dc2626; color: #fff; }
        .ql-background-d97706 { background-color: #d97706; color: #fff; }
        .ql-background-059669 { background-color: #059669; color: #fff; }
        .ql-background-2563eb { background-color: #2563eb; color: #fff; }
        .ql-background-7c3aed { background-color: #7c3aed; color: #fff; }
        .ql-align-center { text-align: center; }
        .ql-align-right { text-align: right; }
        .ql-align-justify { text-align: justify; }
        /* Dark mode for the Quill rich-text EDITOR (write-ups, coaching
           templates). Quill's snow theme hard-codes a white editor + toolbar
           with dark icon strokes, which looked like a bright white box in dark
           mode. Repaint the container, toolbar, text, placeholder, icon
           strokes/fills, and the picker dropdowns to the app's dark tokens.
           Dusk needs it too, the other way round: Quill's white editor is a
           glaring slab on its grey page, and the same token rules put it on
           Dusk's own input ground. One selector names both, so nothing drifts. */
        :root:is([data-theme="dark"], [data-theme="dusk"]) .quill-editor,
        :root:is([data-theme="dark"], [data-theme="dusk"]) .ql-container.ql-snow,
        :root:is([data-theme="dark"], [data-theme="dusk"]) .ql-editor { background: var(--input-bg); color: var(--text); }
        :root:is([data-theme="dark"], [data-theme="dusk"]) .ql-toolbar.ql-snow { background: var(--card-bg); }
        :root:is([data-theme="dark"], [data-theme="dusk"]) .ql-toolbar.ql-snow,
        :root:is([data-theme="dark"], [data-theme="dusk"]) .ql-container.ql-snow { border-color: var(--border); }
        :root:is([data-theme="dark"], [data-theme="dusk"]) .ql-editor.ql-blank::before { color: var(--text-muted); }
        :root:is([data-theme="dark"], [data-theme="dusk"]) .ql-snow .ql-stroke { stroke: var(--text); }
        :root:is([data-theme="dark"], [data-theme="dusk"]) .ql-snow .ql-fill,
        :root:is([data-theme="dark"], [data-theme="dusk"]) .ql-snow .ql-stroke.ql-fill { fill: var(--text); }
        :root:is([data-theme="dark"], [data-theme="dusk"]) .ql-snow .ql-picker { color: var(--text); }
        :root:is([data-theme="dark"], [data-theme="dusk"]) .ql-snow .ql-picker-options { background: var(--card-bg); border-color: var(--border); }
        :root:is([data-theme="dark"], [data-theme="dusk"]) .ql-snow.ql-toolbar button:hover .ql-stroke,
        :root:is([data-theme="dark"], [data-theme="dusk"]) .ql-snow .ql-toolbar button.ql-active .ql-stroke,
        :root:is([data-theme="dark"], [data-theme="dusk"]) .ql-snow .ql-picker-label:hover,
        :root:is([data-theme="dark"], [data-theme="dusk"]) .ql-snow .ql-picker-item:hover { color: var(--accent); stroke: var(--accent); }
        /* Reusable pill-shaped search box (Scheduler's driver search,
           Employees, Disciplinary Log) - promoted here so any list page can use
           it without duplicating the style block. Border color previously
           matched the fill exactly (var(--accent-light) on both), so the
           box had no visible edge and effectively didn't read as an input
           until focused (confirmed live: "doesn't even show up until you
           start typing") - now a real --accent border + shadow at rest. */
        .search-box { position: relative; max-width: 18rem; flex: 1; min-width: 12rem; }
        /* The magnifier: the "search" line icon from _icons.html, drawn in the
           muted ink as a mask (a colour emoji magnifier used to sit here). */
        .search-box::before {
            content: ""; position: absolute; left: 0.8rem; top: 50%; transform: translateY(-50%);
            width: 0.9rem; height: 0.9rem; background: var(--text-muted); pointer-events: none;
            -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'/%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'/%3E%3C/svg%3E") center / contain no-repeat;
                    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'/%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'/%3E%3C/svg%3E") center / contain no-repeat;
        }
        .search-box input {
            margin: 0; padding: 0.5rem 0.85rem 0.5rem 2.15rem; border-radius: 999px;
            background: var(--accent-light); border: 1.5px solid var(--accent); font-size: 0.86rem;
            box-shadow: 0 1px 3px rgba(154, 88, 6, 0.16);
        }
        .search-box input::placeholder { color: var(--accent-dark); opacity: 0.7; }
        .search-box input:focus {
            background: var(--input-bg); border-color: var(--accent-dark); box-shadow: var(--accent-glow);
        }
        main blockquote {
            border-left: 3px solid var(--border); margin: 0.5rem 0; padding: 0.4rem 0 0.4rem 0.8rem;
            color: var(--text-muted); font-style: italic;
        }
        main pre {
            background: var(--gray-bg); border-radius: var(--radius-sm); padding: 0.6rem 0.8rem;
            overflow-x: auto; font-family: ui-monospace, "SFMono-Regular", Consolas, monospace; font-size: 0.85rem;
        }
        .btn-block { width: 100%; margin-top: 1.25rem; }
        /* The signed-out pages' footer links: 15px of text, now 44px to a finger. */
        .signed-out-foot a { display: inline-flex; align-items: center; min-height: 44px; }
        /* Multiple actions in one table cell (e.g. Edit + Mark Returned) - keeps
           them on one line with a consistent gap instead of wrapping/stacking
           whenever a link-wrapped button sits next to a form-wrapped one. */
        .row-actions { display: flex; align-items: center; gap: 0.5rem; flex-wrap: nowrap; }
        .row-actions form.inline { display: inline-flex; }
        .row-actions button, .row-actions a { white-space: nowrap; }
        /* BUT A TABLE CELL STAYS A TABLE CELL. "Line disconnected": a td made
           display:flex stops being a cell, sizes to its buttons, and the row's
           lines above and below it no longer meet the other cells' lines. The
           buttons sit on one line with the same gap, from margins instead. */
        td.row-actions { display: table-cell; white-space: nowrap; vertical-align: middle; }
        td.row-actions > * { vertical-align: middle; }
        td.row-actions > * + * { margin-left: 0.5rem; }

        /* NAV BUTTONS ARE PLAIN BUTTONS NOW. "Loadout", "Schedules", "Previous
           week", "Back to X": links to another page drawn as a button. They were
           a white pill with an amber outline, wrapped round a button inside a
           link, so that by SHAPE they read apart from the tinted form buttons.

           NOT A PILL ANY MORE. Picked in One Look: "B, amber main, outlined
           others" - one shape for every button. The pill was a second kind of
           button that did the same job as a link with the btn class, so a row
           could hold a rectangle and a pill side by side and nobody could say
           which was which. A link wrapped round a button now gets the plain
           button above with no rule of its own, and .btn-nav (the class for a
           disabled "Next week" with nowhere further to go) is listed with
           button and .btn there; only its disabled look is left here. */
        .btn-nav:disabled {
            box-shadow: none; background: var(--gray-bg);
            border-color: var(--border); color: var(--text-muted);
        }

        /* Week/day stepper - a single mounted, centered card (prev arrow / label /
           next arrow) replacing three separate loose elements in a left-aligned
           row - confirmed live as the actual ask ("something more mounted and
           centered into the page"), plus a Week NN number alongside the date
           range so it's not just a date span. Reuses .week-stepper-arrow instead
           of the pill nav-button shape above (circular, not pill) since these sit
           inside their own already-bordered card rather than floating loose. */
        .week-stepper {
            display: flex; align-items: center; justify-content: center; gap: 1rem;
            max-width: 26rem; margin: 0 auto 1.25rem; padding: 0.6rem 1rem;
            background: var(--card-bg); border: 1px solid var(--border); border-radius: 999px;
            box-shadow: var(--shadow);
        }
        .week-stepper-label { flex: 1; text-align: center; min-width: 0; }
        .week-stepper-label .week-stepper-num { font-weight: 700; font-size: 0.98rem; color: var(--text); }
        .week-stepper-label .week-stepper-range { font-size: 0.78rem; color: var(--text-muted); margin-top: 0.1rem; }
        .week-stepper-arrow {
            width: 2.3rem; height: 2.3rem; padding: 0; flex-shrink: 0;
            border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 1rem;
            background: transparent; border: 1px solid transparent; color: var(--accent-dark); box-shadow: none;
        }
        .week-stepper-arrow:hover:not(:disabled) {
            background: var(--accent-light); border-color: var(--accent); color: var(--accent-dark);
            box-shadow: none; transform: none;
        }
        .week-stepper-arrow:disabled { background: transparent; border-color: transparent; box-shadow: none; }

        
        .pager {
            container-type: inline-size; display: flex; align-items: center; justify-content: space-between;
            gap: 0.5rem; width: 100%; margin: 1rem 0 0; font-size: 0.85rem;
        }
        .pager-step { flex: none; white-space: nowrap; }
        .pager-cells { display: flex; align-items: center; justify-content: center; gap: 2px; min-width: 0; }
        .pager-cell {
            display: inline-grid; place-items: center; min-width: 2rem; height: 2rem; padding: 0 0.4rem;
            border: 1px solid transparent; border-radius: var(--radius-sm); color: var(--text);
            font-weight: 500; font-variant-numeric: tabular-nums; line-height: 1; text-decoration: none;
        }
        @media (hover: hover) {
            a.pager-cell:hover { background: color-mix(in srgb, var(--text) 6%, transparent); color: var(--text); }
        }
        a.pager-cell:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
        .pager-cell[aria-current="page"] {
            background: var(--card-bg); border-color: var(--border); font-weight: 600;
            box-shadow: 0 1px 2px color-mix(in srgb, var(--text) 8%, transparent);
        }
        .pager-gap { min-width: 1.5rem; text-align: center; color: var(--text-muted); }
        .pager-of { display: none; color: var(--text-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
        
        @container (max-width: 30rem) {
            .pager:not([data-short]) .pager-cells { display: none; }
            .pager:not([data-short]) .pager-of { display: inline; }
        }
        @container (max-width: 21rem) {
            .pager-cells { display: none; }
            .pager-of { display: inline; }
        }

        
        .divider {
            display: flex; align-items: center; gap: 0.75rem; margin: 1rem 0; padding: 0;
            border: 0; color: var(--text-muted); font-size: 0.75rem; font-weight: 600; line-height: 1.3;
        }
        hr.divider { display: block; height: 1px; background: var(--border); }
        .divider::before, .divider::after { content: ""; flex: 1 1 auto; height: 1px; background: var(--border); }
        hr.divider::before, hr.divider::after { content: none; }
        .divider > * { flex: none; min-width: 0; }
        .divider.is-framed {
            display: block; text-align: center; padding: 0.4rem 0;
            border-top: 1px solid var(--border); border-bottom: 1px solid var(--border);
        }
        .divider.is-strip {
            display: block; text-align: center; padding: 0.45rem 0.75rem;
            background: var(--bg-subtle); border-radius: var(--radius-sm);
        }
        .divider:is(.is-framed, .is-strip)::before, .divider:is(.is-framed, .is-strip)::after { content: none; }
        .divider:is(.is-framed, .is-strip) > span { padding: 0; border: 0; border-radius: 0; background: none; }

        
        .copy-field { display: flex; align-items: center; gap: 0.5rem; min-width: 0; }
        .copy-field-box { position: relative; flex: 1 1 auto; min-width: 0; }
        .copy-field-box > .ui-ico {
            position: absolute; left: 0.7rem; top: 50%; transform: translateY(-50%);
            color: var(--text-muted); pointer-events: none;
        }
        .copy-field-box > input { margin: 0; padding-left: 2.15rem; background: var(--bg-subtle); text-overflow: ellipsis; }
        .copy-field > .btn { flex: none; }
        .copy-field > .btn.is-copied { color: var(--green); border-color: var(--green); }

        .vh {
            position: absolute !important; left: 0; width: 1px; height: 1px; margin: -1px; padding: 0; border: 0;
            overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
        }

        
        .kit-spin, .sync-spinner {
            --s: 1rem; --w: 2px;
            position: relative; display: inline-block; width: var(--s); height: var(--s); flex: none;
            border: 0; border-radius: 50%; color: var(--accent); vertical-align: middle; animation: none;
            box-shadow: inset 0 0 0 var(--w) color-mix(in srgb, currentColor 18%, transparent);
        }
        .kit-spin::before, .sync-spinner::before {
            content: ""; position: absolute; inset: 0; border-radius: 50%;
            background: conic-gradient(from 0deg, transparent 0 45%, currentColor 100%);
            -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - var(--w)), #000 calc(100% - var(--w)));
                    mask: radial-gradient(farthest-side, transparent calc(100% - var(--w)), #000 calc(100% - var(--w)));
            animation: ui-spin 0.9s linear infinite;
        }
        .kit-spin::after, .sync-spinner::after {
            content: ""; position: absolute; left: 50%; top: 0; width: var(--w); height: var(--w);
            margin-left: calc(var(--w) / -2); border-radius: 50%; background: currentColor;
            box-shadow: 0 0 4px 1.5px currentColor; transform-origin: 50% calc(var(--s) / 2);
            animation: ui-spin 0.9s linear infinite;
        }
        .kit-spin.is-sm { --s: 0.875rem; --w: 1.5px; }
        .kit-spin.is-md { --s: 1.75rem; --w: 2.5px; }
        .kit-spin.is-lg { --s: 2.25rem; --w: 3px; }
        .kit-spin-tile {
            display: inline-grid; place-items: center; width: 2.75rem; height: 2.75rem; flex: none; border-radius: 13px;
            background: var(--accent-gradient); color: var(--accent-gradient-on);
            box-shadow: 0 6px 16px color-mix(in srgb, var(--accent) 35%, transparent);
        }
        .kit-spin-tile > .kit-spin { --s: 1.25rem; --w: 2px; color: currentColor; }
        @keyframes ui-spin { to { transform: rotate(360deg); } }
        :is(button, .btn)[aria-busy="true"] { cursor: progress; }
        :is(button, .btn):is([aria-busy="true"], .is-busy)::before,
        :is(.kit-loading, .dl-status.dl-busy, button.dl-busy)::before {
            content: ""; display: inline-block; flex: none; width: 1em; height: 1em; margin-right: 0.45em;
            vertical-align: -0.15em; border-radius: 50%; color: inherit;
            background: conic-gradient(from 0deg, transparent 0 40%, currentColor 100%);
            box-shadow: inset 0 0 0 2px color-mix(in srgb, currentColor 18%, transparent);
            -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 2px), #000 calc(100% - 2px));
                    mask: radial-gradient(farthest-side, transparent calc(100% - 2px), #000 calc(100% - 2px));
            animation: ui-spin 0.9s linear infinite;
        }
        :is(button, .btn)[aria-busy="true"]:has(.kit-spin, .sync-spinner)::before { content: none; }
        .kit-loading { color: var(--text-muted); }
        .kit-loading::before { color: var(--accent); }

        
        .file-drop { display: flex; flex-direction: column; gap: 0.6rem; min-width: 0; margin: 0 0 0.75rem; }
        .file-drop-zone {
            position: relative; display: flex; flex-direction: column; align-items: center; justify-content: center;
            gap: 0.55rem; min-height: 9rem; margin: 0; padding: 1.25rem 1rem;
            border: 1.5px dashed color-mix(in srgb, var(--text-muted) 50%, var(--card-bg)); border-radius: var(--radius-in, var(--radius));
            background: var(--card-bg); color: var(--text-muted); text-align: center; cursor: pointer;
            font-weight: 400; text-transform: none; letter-spacing: normal;
            transition: border-color 150ms ease, background-color 150ms ease;
        }
        .file-drop .file-drop-zone > input[type="file"] {
            position: absolute; width: 1px; height: 1px; margin: 0; padding: 0; border: 0; opacity: 0;
            overflow: hidden; pointer-events: none;
        }
        .file-drop-zone:has(> input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 3px; }
        @media (hover: hover) {
            .file-drop-zone:hover { border-color: color-mix(in srgb, var(--accent) 60%, var(--card-bg)); }
        }
        .file-drop-zone:active { background: color-mix(in srgb, var(--accent) 5%, var(--card-bg)); }
        :is(.file-drop-zone, .att-x, .file-chip-x, .dt-density > button, .menu-item, details:is(.menu-dropdown, .pop) > summary) {
            touch-action: manipulation; -webkit-user-select: none; user-select: none; -webkit-tap-highlight-color: transparent;
        }
        .file-drop.is-over .file-drop-zone { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 7%, var(--card-bg)); }
        .file-drop.is-bad .file-drop-zone { border-color: color-mix(in srgb, var(--red) 70%, var(--card-bg)); }
        .file-drop.is-busy .file-drop-zone { border-color: var(--border); cursor: progress; }
        .file-drop-ico {
            display: grid; place-items: center; width: 2.75rem; height: 2.75rem; flex: none; border-radius: 999px;
            background: var(--bg-subtle); color: var(--text); box-shadow: inset 0 0 0 1px var(--border);
            transition: transform 200ms cubic-bezier(0.2, 0.8, 0.2, 1);
        }
        .file-drop-ico .ui-ico { width: 1.15rem; height: 1.15rem; }
        .file-drop.is-over .file-drop-ico { transform: translateY(-3px); background: var(--accent-gradient); color: var(--accent-gradient-on); box-shadow: none; }
        .file-drop-words { display: flex; flex-direction: column; align-items: center; gap: 0.2rem; min-width: 0; }
        .file-drop-t { color: var(--text); font-weight: 500; font-size: 0.92rem; }
        .file-drop-link { color: var(--accent); text-decoration: underline; text-underline-offset: 2px; }
        .file-drop-s { font-size: 0.78rem; }
        .file-drop-trace {
            position: absolute; left: -1.5px; top: -1.5px; width: calc(100% + 3px); height: calc(100% + 3px);
            overflow: visible; pointer-events: none; opacity: 0; transition: opacity 200ms ease;
        }
        .file-drop-trace rect {
            x: 1.25px; y: 1.25px; width: calc(100% - 2.5px); height: calc(100% - 2.5px);
            rx: var(--radius-in, var(--radius)); fill: none; stroke: var(--accent); stroke-width: 2.5;
            stroke-linecap: round; stroke-dasharray: 100; stroke-dashoffset: 100;
        }
        .file-drop.is-busy .file-drop-trace, .file-drop.is-done .file-drop-trace { opacity: 1; }
        .file-drop-err { display: flex; align-items: flex-start; gap: 0.4rem; margin: 0; color: var(--red); font-size: 0.82rem; }
        .file-drop-err .ui-ico { width: 1rem; height: 1rem; flex: none; margin-top: 0.05rem; }
        :is(.file-drop-err, .file-drop-files)[hidden] { display: none; }
        .file-drop-files { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.4rem; }
        .file-chip {
            display: flex; align-items: center; gap: 0.65rem; min-width: 0; padding: 0.5rem 0.5rem 0.5rem 0.65rem;
            border-radius: var(--radius-sm); background: var(--card-bg); box-shadow: inset 0 0 0 1px var(--border);
            animation: ui-in 200ms cubic-bezier(0.2, 0.8, 0.2, 1);
        }
        .file-chip-ico {
            display: grid; place-items: center; width: 2.25rem; height: 2.25rem; flex: none; border-radius: 10px;
            background: var(--green-bg); color: var(--green);
        }
        .file-chip-ico .ui-ico { width: 1.05rem; height: 1.05rem; }
        .file-chip-meta { flex: 1 1 auto; display: flex; flex-direction: column; min-width: 0; }
        .file-chip-meta b { font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
        .file-chip-sub { font-size: 0.78rem; color: var(--text-muted); font-variant-numeric: tabular-nums; }
        .file-chip-x { flex: none; }
        .file-drop.is-busy .file-chip-x { visibility: hidden; }
        
        .file-drop.is-compact { flex: 1 1 18rem; margin: 0; }
        .file-drop.is-compact .file-drop-zone { flex-direction: row; justify-content: flex-start; min-height: 0; padding: 0.6rem 0.8rem; text-align: left; gap: 0.7rem; }
        .file-drop.is-compact .file-drop-ico { width: 2.1rem; height: 2.1rem; }
        .file-drop.is-compact .file-drop-words { align-items: flex-start; }
        @keyframes ui-in { from { opacity: 0; transform: translateY(4px); } }

        
        /* :where keeps this at element weight, so a page's own rule that hides
           a menu (the settings group switch on a computer) still wins. */
        details:where(.menu-dropdown, .pop) { position: relative; display: inline-block; }
        details:is(.menu-dropdown, .pop) > summary { list-style: none; cursor: pointer; }
        details:is(.menu-dropdown, .pop) > summary::-webkit-details-marker { display: none; }
        details:is(.menu-dropdown, .pop) > summary::marker { content: ""; }
        .menu-panel { padding: 6px; }
        .menu-panel hr { margin: 6px 4px; }
        .menu-item {
            display: flex; align-items: flex-start; gap: 0.6rem; width: 100%; box-sizing: border-box;
            padding: 0.45rem 0.6rem; border-radius: calc(var(--radius-sm) - 2px); text-decoration: none; line-height: 1.35;
        }
        a.menu-item, button.menu-item { color: var(--text); }
        .menu-item:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
        .menu-item > .ui-ico, .menu-item > .menu-ico { flex: none; width: 1rem; height: 1rem; margin-top: 0.1rem; color: var(--text-muted); }
        .menu-name { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
        .menu-desc { display: block; font-size: 0.78rem; font-weight: 400; color: var(--text-muted); white-space: normal; }
        .menu-item.is-danger, .menu-item.is-danger > .ui-ico { color: var(--red); }
        .menu-item.is-current { font-weight: 600; background: var(--accent-light); }
        .menu-h {
            display: block; padding: 0.4rem 0.6rem 0.2rem; margin: 0; color: var(--text-muted);
            font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase;
        }
        .pop-panel, .pop-card {
            background: var(--card-bg); color: var(--text); border: 1px solid var(--border);
            border-radius: var(--radius-sm); box-shadow: var(--shadow-lift);
            padding: 0.85rem 1rem; font-size: 0.88rem; font-weight: 400; line-height: 1.45; text-align: left;
            text-transform: none; letter-spacing: normal; white-space: normal;
        }
        .pop-panel {
            position: absolute; top: calc(100% + 6px); left: 0; z-index: 45;
            width: max-content; min-width: 14rem; max-width: min(22rem, calc(100vw - 24px));
        }
        .pop-panel.pop-panel-right, .pop-panel.is-flipped, .menu-panel.is-flipped { left: auto; right: 0; }
        .pop-panel.is-up { top: auto; bottom: calc(100% + 6px); }
        .pop-h { display: block; margin: 0 0 0.35rem; font-weight: 600; font-size: 0.9rem; }
        :is(.pop-panel, .pop-card) > p { margin: 0 0 0.5rem; }
        :is(.pop-panel, .pop-card) > :last-child { margin-bottom: 0; }
        
        .pop-i {
            display: inline-grid; place-items: center; width: 1.35rem; height: 1.35rem; min-height: 0; padding: 0;
            border-radius: 999px; color: var(--text-muted); vertical-align: middle; cursor: pointer;
        }
        .pop-i .ui-ico { width: 0.95rem; height: 0.95rem; }
        details[open] > .pop-i { color: var(--text); background: var(--bg-subtle); }
        @media (hover: hover) and (pointer: fine) { .pop-i:hover { color: var(--text); background: var(--bg-subtle); } }
        details[open] > :is(.menu-panel, .pop-panel, .pop-card, .wul-panel) { transform-origin: top left; animation: ui-menu 150ms cubic-bezier(0.2, 0.8, 0.2, 1); }
        details[open] > :is(.menu-panel-right, .pop-panel-right, .is-flipped) { transform-origin: top right; }
        .pop-card:popover-open, .acct-menu:popover-open { animation: ui-menu 150ms cubic-bezier(0.2, 0.8, 0.2, 1); }
        @keyframes ui-menu { from { opacity: 0; transform: translateY(-4px) scale(0.97); filter: blur(2px); } }
        
        .draft-menu > summary { display: inline-flex; align-items: center; gap: 0.35em; white-space: nowrap; }
        .draft-menu > .menu-panel { min-width: 15.5rem; text-align: left; white-space: normal; }
        .draft-menu > .menu-panel b { font-weight: 600; }
        
        .draft-menu .menu-item { justify-content: flex-start; }

        
        .av {
            --s: 2rem;
            position: relative; display: inline-grid; place-items: center; flex: none; width: var(--s); height: var(--s);
            border-radius: 999px; background: var(--bg-subtle); color: var(--text-muted);
            box-shadow: inset 0 0 0 1px var(--border); font-size: calc(var(--s) * 0.38); font-weight: 600;
            line-height: 1; letter-spacing: 0; text-transform: uppercase; vertical-align: middle;
            -webkit-user-select: none; user-select: none;
        }
        .av > img { width: 100%; height: 100%; border-radius: inherit; object-fit: cover; display: block; }
        .av.has-photo { box-shadow: none; background: none; }
        .av.is-tint { background: var(--accent-light); color: var(--accent); box-shadow: none; }
        .av.av-20 { --s: 1.25rem; font-size: 0.5rem; } .av.av-24 { --s: 1.5rem; } .av.av-28 { --s: 1.75rem; }
        .av.av-32 { --s: 2rem; } .av.av-36 { --s: 2.25rem; } .av.av-40 { --s: 2.5rem; }
        .av-dot {
            position: absolute; right: -1px; bottom: -1px; width: max(8px, calc(var(--s) * 0.28)); height: max(8px, calc(var(--s) * 0.28));
            border-radius: 50%; background: var(--text-muted); box-shadow: 0 0 0 2px var(--card-bg);
        }
        .av-dot.is-on { background: var(--green); }
        .av-stack { display: inline-flex; }
        .av-stack > .av { box-shadow: 0 0 0 2px var(--card-bg); }
        .av-stack > .av + .av { margin-left: -0.5rem; }
        .av-name { display: inline-flex; align-items: center; gap: 0.6rem; min-width: 0; }
        .av-name > :last-child { min-width: 0; overflow-wrap: break-word; }

        
        .dt {
            min-width: 0; margin: 0 0 1.5rem; background: var(--card-bg); border: 1px solid var(--border);
            border-radius: var(--radius); box-shadow: var(--shadow); overflow: hidden;
        }
        .dt-bar {
            display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0.6rem 1rem;
            padding: 0.75rem 1rem; border-bottom: 1px solid var(--border);
        }
        .dt-total { display: flex; align-items: baseline; gap: 0.45rem; min-width: 0; color: var(--text-muted); font-size: 0.85rem; }
        .dt-total b { color: var(--text); font-size: 1rem; font-weight: 600; font-variant-numeric: tabular-nums; }
        .dt-tools { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; margin-left: auto; min-width: 0; }
        .dt-tools > form { display: contents; }
        .dt-tools select { width: auto; margin: 0; min-height: 2.25rem; }
        .dt-search { position: relative; display: inline-flex; align-items: center; min-width: 0; flex: 0 1 15rem; }
        .dt-search > .ui-ico { position: absolute; left: 0.75rem; width: 1rem; height: 1rem; color: var(--text-muted); pointer-events: none; }
        .dt-search > input { width: 100%; margin: 0; min-height: 2.25rem; padding-left: 2.2rem; border-radius: 999px; }
        .dt-density { flex: none; }
        .dt-density > button { min-height: 2rem; padding: 0 0.7rem; font-size: 0.8rem; }
        .dt-scroll, .dt .table-scroll { overflow-x: auto; margin: 0; border-radius: 0; box-shadow: none; overscroll-behavior-x: contain; }
        .dt table { margin: 0; border: 0; border-radius: 0; box-shadow: none; }
        .dt :is(th, td):first-child { padding-left: 1rem; }
        .dt :is(th, td):last-child { padding-right: 1rem; }
        .dt[data-density="compact"] :is(th, td) { padding-top: 0.3rem; padding-bottom: 0.3rem; }
        .dt[data-density="compact"] .av { --s: 1.5rem; }
        .dt-foot { padding: 0.25rem 1rem 0.75rem; border-top: 1px solid var(--border); }
        .dt-foot > .pager { margin-top: 0.5rem; }
        .dt-empty { padding: 2rem 1rem; text-align: center; color: var(--text-muted); }
        
        a.dt-sort { display: inline-flex; align-items: center; gap: 0.3rem; color: inherit; text-decoration: none; white-space: nowrap; }
        a.dt-sort::after {
            content: ""; width: 0.8rem; height: 0.8rem; flex: none; background: currentColor; opacity: 0.45;
            -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m7 15 5 5 5-5'/%3E%3Cpath d='m7 9 5-5 5 5'/%3E%3C/svg%3E") center / contain no-repeat;
                    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m7 15 5 5 5-5'/%3E%3Cpath d='m7 9 5-5 5 5'/%3E%3C/svg%3E") center / contain no-repeat;
        }
        th[aria-sort="ascending"] a.dt-sort::after, th[aria-sort="descending"] a.dt-sort::after { opacity: 1; }
        th[aria-sort="ascending"] a.dt-sort::after {
            -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m7 14 5-5 5 5'/%3E%3C/svg%3E");
                    mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m7 14 5-5 5 5'/%3E%3C/svg%3E");
        }
        th[aria-sort="descending"] a.dt-sort::after {
            -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m7 10 5 5 5-5'/%3E%3C/svg%3E");
                    mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m7 10 5 5 5-5'/%3E%3C/svg%3E");
        }
        .dt-num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
        .dt-amt {
            display: inline-block; padding: 0.12rem 0.55rem; border-radius: 999px; background: var(--bg-subtle);
            color: var(--text); font-variant-numeric: tabular-nums; font-weight: 500; font-size: 0.85em; white-space: nowrap;
        }
        .dt-chip {
            display: inline-flex; align-items: center; gap: 0.35rem; padding: 0.12rem 0.55rem; border-radius: 999px;
            background: var(--gray-bg); color: var(--text); font-size: 0.78rem; font-weight: 600; white-space: nowrap;
        }
        .dt-chip.is-green { background: var(--green-bg); color: var(--green); }
        .dt-chip.is-red { background: var(--red-bg); color: var(--red); }
        .dt-chip.is-gold { background: var(--gold-bg); color: var(--gold); }
        .dt-chip.is-orange { background: var(--orange-bg); color: var(--orange); }
        
        .dt-pick { position: relative; display: inline-flex; align-items: center; }
        .dt-pick::before {
            content: ""; position: absolute; left: 0.6rem; width: 0.45rem; height: 0.45rem; border-radius: 50%;
            background: var(--dot, var(--text-muted)); pointer-events: none;
        }
        .dt-pick > select { width: auto; min-height: 1.9rem; margin: 0; padding: 0 1.7rem 0 1.35rem; border-radius: 999px; font-size: 0.8rem; font-weight: 500; }

        
        :where(table[data-resizable] thead th[data-col]) { position: relative; }
        .col-grip {
            position: absolute; top: 0; right: 0; bottom: 0; z-index: 2; width: 8px; margin: 0; padding: 0;
            cursor: col-resize; touch-action: none; -webkit-user-select: none; user-select: none; outline: none;
        }
        .col-grip::after {
            content: ""; position: absolute; top: 22%; bottom: 22%; right: 3px; width: 2px; border-radius: 2px;
            background: var(--accent); opacity: 0; transition: opacity 150ms ease;
        }
        @media (hover: hover) {
            table[data-resizable] thead th:hover > .col-grip::after { opacity: 0.35; }
            .col-grip:hover::after { opacity: 1; }
        }
        .col-grip:focus-visible::after, .col-grip.is-dragging::after, .col-grip.is-flash::after { opacity: 1; }
        .col-grip:focus-visible { box-shadow: inset 0 0 0 2px var(--accent); border-radius: 3px; }
        table.is-col-resizing, table.is-col-resizing * { cursor: col-resize !important; -webkit-user-select: none; user-select: none; }
        @media (hover: none) and (pointer: coarse) { .col-grip { display: none; } }
        @media (prefers-reduced-motion: reduce) { .col-grip::after { transition: none; } }

        
        dialog::backdrop { background: rgba(15, 23, 42, 0.42); }
        dialog[open] { animation: ui-rise 200ms cubic-bezier(0.2, 0.8, 0.2, 1); }
        dialog[open]::backdrop { animation: ui-fade 200ms ease-out; }
        @keyframes ui-rise { from { opacity: 0; scale: 0.96; } }
        @keyframes ui-fade { from { opacity: 0; } }
        dialog.drawer {
            position: fixed; inset: 12px 12px 12px auto; margin: 0; width: min(26rem, calc(100vw - 24px));
            max-width: none; height: auto; max-height: none; padding: 0; overflow: hidden;
            background: var(--card-bg); color: var(--text); border: 1px solid var(--border);
            border-radius: var(--radius); box-shadow: var(--shadow-lift);
        }
        dialog.drawer[open] { display: flex; flex-direction: column; animation: ui-drawer 250ms cubic-bezier(0.2, 0.8, 0.2, 1); }
        .drawer-head { display: flex; align-items: flex-start; gap: 0.6rem; padding: 1.1rem 1.1rem 0.75rem; }
        .drawer-head > :first-child { flex: 1; min-width: 0; margin: 0; font-size: 1.05rem; }
        .drawer-body { flex: 1 1 auto; overflow-y: auto; overscroll-behavior: contain; padding: 0.25rem 1.1rem 1rem; }
        .drawer-foot { display: flex; gap: 0.5rem; padding: 0.85rem 1.1rem 1.1rem; border-top: 1px solid var(--border); }
        .drawer-grab { display: none; width: 2.25rem; height: 4px; margin: 8px auto 0; flex: none; border-radius: 999px; background: var(--border); }
        @keyframes ui-drawer { from { transform: translateX(calc(100% + 24px)); } }
        @media (max-width: 640px) {
            dialog.drawer {
                inset: auto 0 0 0; width: auto; max-height: 86vh; border-radius: var(--radius) var(--radius) 0 0;
                padding-bottom: env(safe-area-inset-bottom, 0px);
            }
            dialog.drawer[open] { animation-name: ui-sheet; }
            .drawer-grab { display: block; }
        }
        @keyframes ui-sheet { from { transform: translateY(100%); } }

        
        .att-tray { display: flex; flex-wrap: wrap; gap: 14px; padding: 8px 6px 10px; }
        .att-tray:empty { display: none; }
        .att-tile { position: relative; width: 56px; height: 56px; flex: none; animation: ui-in 200ms cubic-bezier(0.2, 0.8, 0.2, 1); }
        .att-img {
            position: absolute; inset: 0; overflow: hidden; border-radius: 14px;
            background: var(--bg-subtle) center / cover no-repeat; box-shadow: inset 0 0 0 1px var(--border);
        }
        
        .att-img::after { content: ""; position: absolute; inset: 0; background: rgba(15, 23, 42, 0.32); opacity: 0; transition: opacity 240ms ease; }
        .att-tile.is-up:not(.is-doc) .att-img::after { opacity: 1; }
        .att-img.att-doc { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px; padding: 4px; color: var(--text-muted); }
        .att-doc .ui-ico { width: 1.2rem; height: 1.2rem; flex: none; }
        .att-doc-name { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text); font-size: 9px; font-weight: 600; }
        .att-ring { position: absolute; left: -4px; top: -4px; width: 64px; height: 64px; overflow: visible; pointer-events: none; opacity: 0; transition: opacity 240ms ease; }
        .att-ring rect { x: 1.25px; y: 1.25px; width: 61.5px; height: 61.5px; rx: 17px; fill: none; stroke-width: 2.5; }
        .att-ring .trk { stroke: var(--border); }
        .att-ring .prg { stroke: var(--accent); stroke-linecap: round; stroke-dasharray: 100; stroke-dashoffset: 100; }
        .att-pct {
            position: absolute; right: 3px; bottom: 3px; padding: 1px 4px; border-radius: 999px;
            background: color-mix(in srgb, var(--card-bg) 88%, transparent); color: var(--text);
            font-size: 9px; font-weight: 700; line-height: 1.2; font-variant-numeric: tabular-nums;
            opacity: 0; transition: opacity 240ms ease;
        }
        .att-tile.is-up .att-ring, .att-tile.is-up .att-pct { opacity: 1; }
        .att-x {
            position: absolute; top: -7px; right: -7px; display: grid; place-items: center; width: 22px; height: 22px;
            min-width: 0; min-height: 0; padding: 0; border-radius: 999px; border: 2px solid var(--card-bg);
            background: var(--text); color: var(--card-bg); cursor: pointer;
            transition: opacity 220ms ease 80ms, transform 220ms ease 80ms;
        }
        .att-x .ui-ico { width: 12px; height: 12px; }
        
        .att-x::after { content: ""; position: absolute; inset: -11px; border-radius: 50%; }
        .att-tile.is-up .att-x { opacity: 0; transform: scale(0.8); pointer-events: none; }
        .att-tile.is-bad .att-img { box-shadow: inset 0 0 0 2px var(--red); }
        .att-tile.is-bad .att-pct { color: var(--red); opacity: 1; }

        @media (prefers-reduced-motion: reduce) {
            .kit-spin::before, .kit-spin::after, .sync-spinner::before, .sync-spinner::after,
            :is(button, .btn)[aria-busy="true"]::before, :is(.kit-loading, .dl-busy)::before { animation-duration: 2.4s; }
            .file-chip, .att-tile, dialog[open], dialog[open]::backdrop, .pop-card:popover-open, .acct-menu:popover-open,
            details[open] > :is(.menu-panel, .pop-panel, .pop-card, .wul-panel) { animation: none !important; }
            .file-drop-zone, .file-drop-ico, .file-drop-trace, .att-img::after, .att-ring, .att-pct, .att-x { transition: none !important; }
        }

        
        .notice, .error {
            --msg: var(--green); position: relative;
            color: var(--text); background: var(--card-bg); border: 1.5px solid var(--msg);
            border-radius: var(--radius-sm); padding: 0.7rem 1rem 0.7rem 2.1rem; font-size: 0.9rem;
        }
        .error { --msg: var(--red); white-space: pre-wrap; }
        .notice.is-warn { --msg: var(--gold); }
        
        .notice.is-info { --msg: var(--tile5); }
        /* The dot: 0.55rem, centred on the first line (half of the body's
           1.55 line height, in the message's own em). */
        .notice::before, .error::before {
            content: ""; position: absolute; left: 0.85rem; top: calc(0.7rem + 0.775em - 0.275rem);
            width: 0.55rem; height: 0.55rem; border-radius: 50%; background: var(--msg);
            pointer-events: none;
        }
        
        .error:has(> :is(ul, ol, p, div, table, details)) { white-space: normal; }

        /* ---------------- Disclosure ----------------

           ONE CARET FOR ONE CONTROL. 127 disclosures across 61 templates, in
           three different states before this: 25 templates showing the
           browser's own triangle, nine drawing a triangle character of their
           own out of \25B8 / \25BE, three having independently arrived at this
           exact rotated-borders recipe and written it out again, and twenty-one
           hiding the marker and drawing nothing at all - so a third of the
           disclosures in the app gave no sign they opened, and the ones that
           did looked different page to page.

           Two borders rotated, not a glyph: it is currentColor in all eight
           themes and there is no character to go missing in a font. flex is
           set because several summaries are flex rows, where a caret with no
           flex basis gets squeezed to nothing.

           THE EXCLUSIONS BELOW ARE DELIBERATE, and each one draws a marker that
           means something this caret does not. Anything else with its own
           summary::before will end up with a glyph inside a bordered box, which
           is what tests/test_the_native_controls_are_ours.py checks for. */
        summary { cursor: pointer; }
        details > summary { list-style: none; }
        details > summary::-webkit-details-marker { display: none; }
        details > summary::before {
            content: ""; display: inline-block; flex: 0 0 auto; vertical-align: middle;
            width: 0.4rem; height: 0.4rem; margin-right: 0.5rem;
            border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
            transform: rotate(-45deg); transition: transform 0.15s ease;
        }
        details[open] > summary::before { transform: rotate(45deg); }
        @media (prefers-reduced-motion: reduce) {
            details > summary::before { transition: none; }
        }
        /* The nav's section carets are ::after and sit at the far right of the
           row; the FAQ and the override panel use + / - because they mean "add
           one", not "there is more to read"; the safety types list marks itself
           with a ::after chevron of its own; Message dispatch's picker
           (_message_dispatch.html) is a button beside Call 911, drawn like it;
           a report card's "+ Add a note" (driver_reports/_report_card.html)
           carries its own plus, because it adds one. */
        details.sidebar-section > summary::before,
        .rc-note > summary::before,
        .faq details > summary::before,
        .sa-types > summary::before,
        .pr-override > summary::before,
        .chat-plus > summary::before,
        .chat-alerts > summary::before,
        .rp-fold > summary::before,
        /* The quiz editor's "..." menu is an icon button showing its own dots. */
        .qze-more > summary::before,
        /* A Follow-through row is a whole table row that opens; its chevron
           sits at the row's far right, under the other rows' chevrons. */
        .ftb-d > summary::before,
        /* The Amerit and Rivian sort menu is the kit's dropdown button, with
           its own sort icon and no caret. */
        .fp-sort > details > summary::before,
        /* Finance's training Resolve (money/invoices.html) is a button, not
           a fold: it opens a note field under itself. */
        .iv-res > summary::before,
        /* Draft... (_draft_menu.html) carries its own pencil. */
        .draft-menu > summary::before,
        /* More (_more_menu.html) shows its own dots. */
        .btn-more > summary::before,
        .md-pick > summary::before { content: none; border: 0; margin-right: 0; width: 0; height: 0; }

        /* ---------------- The folded explainer ----------------

           "How to get the PAVE sessions file". "How the deductible works".
           The three-step preamble above an upload. These are instructions
           somebody reads ONCE, on their first visit, and then scrolls past
           every day for a year. Given a full card at the top of the page, that
           one-time reading outranks the thing the page is actually for.

           So it folds. A details element with class "howto" is shut by default, says in its
           summary what it will tell you, and opens in place without moving the
           page's own content anywhere else. The prose inside is set quiet and
           narrow because it IS prose: muted, around 65 characters a line, and
           not competing with the page's real voice. */
        details.howto {
            border: 1px solid var(--border); border-radius: var(--radius-sm);
            background: var(--bg-subtle); margin: 0 0 1.25rem;
        }
        details.howto > summary {
            cursor: pointer; list-style: none; padding: 0.6rem 0.9rem;
            font-size: 0.82rem; font-weight: 700; color: var(--text-muted);
            display: flex; align-items: center; gap: 0.5rem;
        }
        details.howto > summary:hover { color: var(--text); }
        .howto-body {
            padding: 0 0.9rem 0.9rem; font-size: 0.86rem; color: var(--text-muted);
            max-width: 68ch;
        }
        .howto-body > *:first-child { margin-top: 0; }
        .howto-body > *:last-child { margin-bottom: 0; }
        .howto-body ol, .howto-body ul { margin: 0.2rem 0 0.7rem; padding-left: 1.25rem; }
        .howto-body li { margin-bottom: 0.3rem; }
        .howto-body strong { color: var(--text); }

        /* Small reusable primitives so dense pages stop hand-rolling these. */
        .btn-sm { padding: 0.4rem 0.7rem; font-size: 0.8rem; min-height: auto; }
        /* Secondary words: "the sync service is waiting", a dash in an empty
           cell. Pages used the class before anything defined it, so those
           read as body text. */
        .muted { color: var(--text-muted); }
        .card > h2:first-child, .card > h3:first-child, .card-title { margin-top: 0; }
        .nav-pinned { display: flex; align-items: center; gap: 0.4rem; }
        /* A PINNED ROW BEING DRAGGED into order (app/static/js/pin-order.js):
           lifted over the others, which slide out of its way. The lift is a
           shadow and a card; the move itself is transform only. */
        .sidebar-nav details[data-section=pinned] .sidebar-section-body > .nav-pinned.is-dragging {
            position: relative; z-index: 4; cursor: grabbing;
            background: var(--card-bg); color: var(--text);
            box-shadow: 0 0 0 1px var(--border), 0 10px 24px rgba(16, 35, 63, 0.16);
        }
        html.pin-sorting, html.pin-sorting * { cursor: grabbing !important; -webkit-user-select: none; user-select: none; }
        html.pin-sorting details[data-section=pinned] .nav-star-form .nav-star { opacity: 0 !important; }
        .nav-pinned-label { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis;
                            white-space: nowrap; }
        /* The way back to the setup walk (see the markup by the content block). */
        .walk-return {
            display: flex; align-items: center; justify-content: space-between; gap: 0.5rem 1rem;
            flex-wrap: wrap; margin: 0 0 1rem; padding: 0.6rem 0.9rem;
            border: 1px solid var(--border); border-radius: var(--radius);
            background: var(--card-bg); font-size: 0.88rem;
        }
        .walk-return-where { color: var(--text-muted); }
        .walk-return-where strong { color: var(--text); }
        .walk-return-back { font-weight: 700; white-space: nowrap; }
        .walk-return-acts { display: inline-flex; align-items: center; gap: 0.4rem 0.9rem; flex-wrap: wrap; }
        /* THE BANNER FORM (unfinished essentials): full width, an icon, its
           own colour and a button. Gold is the warning. */
        .walk-return[data-reminder] {
            justify-content: flex-start; padding: 0.7rem 0.9rem 0.7rem 0.85rem; font-size: 0.9rem;
            border-left-width: 4px;
            background: var(--gold-bg); border-color: color-mix(in srgb, var(--gold) 40%, var(--border));
            border-left-color: var(--gold);
        }
        .walk-return-ico { flex: none; display: grid; place-items: center; width: 2rem; height: 2rem;
            border-radius: 50%; }
        .walk-return-ico .ui-ico { width: 1.1rem; height: 1.1rem; }
        .walk-return[data-reminder] .walk-return-ico { color: var(--gold); background: color-mix(in srgb, var(--gold) 16%, var(--card-bg)); }
        .walk-return[data-reminder] .walk-return-where {
            flex: 1 1 16rem; min-width: 0; color: var(--text); line-height: 1.45;
        }
        .walk-return[data-reminder] .walk-return-acts { margin-left: auto; }
        .walk-return .walk-return-hide { color: var(--text-muted); font-size: 0.85rem; white-space: nowrap; }
        .walk-return[data-reminder] .walk-return-back {
            display: inline-flex; align-items: center; min-height: max(2.25rem, var(--tap-min)); padding: 0.35rem 0.95rem;
            border-radius: var(--radius-sm); text-decoration: none; border: 1px solid transparent;
            /* Quiet: an outline, not a filled button. The page's own main
               action is the one that stands out. */
            background: var(--card-bg); color: var(--text); border-color: color-mix(in srgb, var(--gold) 55%, var(--border));
            font-weight: 600;
        }
        .walk-return[data-reminder] .walk-return-back:hover { background: color-mix(in srgb, var(--gold) 12%, var(--card-bg)); }
        /* THE ROUND BACK TO SETUP BUTTON (the markup is at the foot of this
           file). Shown to an admin on every page once the walk has sent them
           somewhere else, until they are back in it or four hours pass.

           IN THE CORNER, ABOVE THE OTHER TWO. The Messenger launcher (bottom
           right, 3.2rem) and the help launcher (54px, above it) already own that
           corner, so this stacks above whichever of them this page has:
           data-chat and data-sup say which, and each adds one launcher's height
           to the lift. On a phone the Messenger is gone and the tab bar is
           there, so the button rides on --dock-h, which already includes the
           home indicator's inset, the same way the help launcher does. */
        .walk-back {
            --lift-chat: 0px; --lift-sup: 0px;
            position: fixed; z-index: 1148;
            right: max(20px, env(safe-area-inset-right, 0px));
            bottom: calc(20px + var(--lift-chat) + var(--lift-sup) + env(safe-area-inset-bottom, 0px));
            width: 3.2rem; height: 3.2rem; border-radius: 50%; box-sizing: border-box;
            display: grid; place-items: center; text-decoration: none;
            background: var(--accent); color: var(--accent-on, #fff);
            box-shadow: 0 6px 20px rgba(0, 0, 0, 0.28);
        }
        .walk-back[data-chat] { --lift-chat: 68px; }
        .walk-back[data-sup] { --lift-sup: 68px; }
        .walk-back:hover, .walk-back:active { background: var(--accent); color: var(--accent-on, #fff);
            text-decoration: none; transform: none; filter: brightness(1.06); }
        .walk-back:focus-visible { outline: 3px solid var(--accent-dark); outline-offset: 3px; }
        .walk-back .ui-ico { width: 1.5rem; height: 1.5rem; }
        .walk-back-label {
            position: absolute; right: calc(100% + 0.6rem); top: 50%; transform: translateY(-50%);
            white-space: nowrap; padding: 0.35rem 0.75rem; border-radius: 999px;
            background: var(--card-bg); color: var(--text); border: 1px solid var(--border);
            box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18); font-size: 0.85rem; font-weight: 700;
            opacity: 0; pointer-events: none;
        }
        .walk-back:hover .walk-back-label, .walk-back:focus-visible .walk-back-label { opacity: 1; }
        /* The highlight: a ring that grows and fades, three times, shortly after
           the page loads. Not at all when the person has asked for less motion. */
        .walk-back::after {
            content: ""; position: absolute; inset: 0; border-radius: 50%; pointer-events: none;
            border: 3px solid var(--accent); opacity: 0;
        }
        @media (prefers-reduced-motion: no-preference) {
            .walk-back::after { animation: walk-back-ring 1.8s ease-out 0.5s 3; }
        }
        @keyframes walk-back-ring {
            0% { transform: scale(1); opacity: 0.7; }
            100% { transform: scale(1.75); opacity: 0; }
        }
        @media (max-width: 900px) {
            .walk-back { bottom: calc(14px + var(--dock-h, 0px) + var(--lift-sup)); }
        }
        @media (max-width: 30rem) {
            .walk-back { right: max(12px, env(safe-area-inset-right, 0px)); }
        }
        @media print { .walk-back { display: none; } }
        /* THE GUIDED MOVE'S CARD (see the markup by the content block). A card,
           not a blocker: the page is still there under it, and Later puts it
           away for a day. Theme tokens only, so it reads in every theme; the
           accent rail is what says "this one is for you". */
        .move-card {
            display: flex; align-items: center; gap: 0.75rem 1rem; flex-wrap: wrap;
            margin: 0 0 1rem; padding: 0.85rem 1rem;
            background: var(--card-bg); color: var(--text);
            border: 1px solid var(--border); border-left: 4px solid var(--accent);
            border-radius: var(--radius); box-shadow: var(--shadow);
        }
        .move-card[hidden] { display: none; }
        .move-card-body { flex: 1 1 16rem; min-width: 0; display: flex; flex-direction: column; gap: 0.2rem; }
        .move-card-title { font-size: 1rem; font-weight: 800; color: var(--text); }
        .move-card-text { font-size: 0.9rem; color: var(--text-muted); }
        .move-card-actions { display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; }
        .move-card-actions form { margin: 0; }
        .move-card-actions button { min-height: 2.75rem; }
        .move-card-later {
            background: transparent; border-color: var(--border); color: var(--text-muted); box-shadow: none;
        }
        .move-card-later:hover {
            background: var(--bg-subtle); border-color: var(--text-muted); color: var(--text);
            box-shadow: none; transform: none;
        }
        /* On a phone the two buttons take the width, Move first, so the thumb
           lands on the one that matters. */
        @media (max-width: 560px) {
            .move-card-actions { width: 100%; }
            .move-card-actions form:first-child { flex: 1 1 auto; }
            .move-card-actions form:first-child button { width: 100%; }
        }

        /* ---------------- The top of a page: .crumbs, .page-header ---------------- */
        
        .crumbs {
            display: flex; flex-wrap: wrap; align-items: center; margin: 0 0 0.35rem;
            font-size: 0.84rem; font-weight: 600; line-height: 1.3; color: var(--text-muted);
        }
        .crumbs > * { display: inline-flex; align-items: center; min-width: 0; }
        .crumbs > a { color: var(--accent-dark); text-decoration: none; min-height: var(--tap-min); }
        .crumbs > a:hover { color: var(--accent-darker); }
        .crumbs > a:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 3px; }
        .crumbs > [aria-current] { color: var(--text-muted); }
        .crumbs > * + *::before {
            content: "/"; content: "/" / ""; margin: 0 0.45rem; font-weight: 400; color: var(--text-muted);
        }
        
        .page-header { display: flex; justify-content: space-between; align-items: center; gap: 0.75rem; flex-wrap: wrap; margin-bottom: 1rem; }
        .page-header h1 { margin: 0; }
        .page-lede { margin: 0.25rem 0 0; max-width: 62ch; font-size: 0.9rem; line-height: 1.45; color: var(--text-muted); }
        h1:has(+ .page-lede) { margin-bottom: 0; }
        h1 + .page-lede { margin-bottom: 1rem; }
        .page-header .page-lede { margin-bottom: 0; }
        .page-actions { display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; }
        .action-row { display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; }
        /* --- form fields ------------------------------------------------
           WHY THE OLD ONE LOOKED BROKEN, because it is worth not rebuilding.
           This was an auto-fit grid and nothing else, and two templates pasted
           a copy of it inline. Each cell stretched to the row's height, and two
           of the cells carried a two-line explanation INSIDE the label - so
           those labels were three lines tall, their controls started lower than
           everybody else's, and the row had no baseline at all. The last field
           on a short row then sat alone against a lake of dead space.

           Two rules fix it and both are here rather than per page: controls
           align to the START of their cell, and a hint lives UNDER its control
           instead of inside its label, so every label is one line high and
           every control begins on the same line. */
        .form-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
            gap: 0.9rem 1rem; align-items: start; }
        /* Grid children refuse to shrink below their content without this, which
           is how a long email in a narrow column pushes the whole row wider. */
        .form-grid > * { min-width: 0; }
        .field { display: flex; flex-direction: column; gap: 0.3rem; }
        .field > label { margin: 0; }
        .field > input, .field > select, .field > textarea { margin: 0; width: 100%; }
        /* "(optional)" and the like: part of the label, not shouted like one. */
        .field-opt { font-weight: 400; text-transform: none; color: var(--text-muted); }
        .field-hint { font-size: 0.78rem; color: var(--text-muted); line-height: 1.4; margin: 0; }
        /* Two controls that are one field - a birthday's month and day. */
        .field-pair { display: flex; gap: 0.4rem; }
        .field-pair > select { margin: 0; min-width: 0; }
        /* A field that should keep the full width rather than be squeezed into
           a column, and one that should not be stretched to fill it. */
        .field-wide { grid-column: 1 / -1; }
        .field-narrow > input, .field-narrow > select { max-width: 14rem; }
        /* Groups inside one form, so identity, account and options read apart
           instead of as one undifferentiated wall of boxes. */
        .form-section + .form-section { margin-top: 1.2rem; padding-top: 1rem;
            border-top: 1px solid var(--border); }
        .form-section > h4 { margin: 0 0 0.7rem; font-size: 0.75rem; text-transform: uppercase;
            letter-spacing: 0.05em; color: var(--text-muted); font-weight: 700; }
        /* STACKED, NEVER SIDE BY SIDE. Two inline-flex checkbox labels with no
           block between them render as one run-on line, which is how "Email
           this person their invite" ended up butted against "Seasonal driver". */
        .form-checks { display: flex; flex-direction: column; gap: 0.55rem; }
        .form-checks > label { display: inline-flex; align-items: flex-start; gap: 0.45rem;
            font-weight: 400; text-transform: none; font-size: 0.9rem; line-height: 1.45; }
        .form-checks input[type="checkbox"] { width: auto; margin: 0.15rem 0 0; flex: 0 0 auto; }
        /* The submit row, set apart from the last field rather than touching it. */
        .form-actions { display: flex; gap: 0.6rem; align-items: center; flex-wrap: wrap;
            margin-top: 1.2rem; padding-top: 1rem; border-top: 1px solid var(--border); }
        /* Friendly empty-state inside an otherwise-empty table cell: the "no rows
           yet" line plus the primary create action for that page. */
        .empty-state { display: flex; flex-direction: column; align-items: center; gap: 0.6rem; padding: 1.75rem 1rem; color: var(--text-muted); text-align: center; }
        /* --- the tab bar ------------------------------------------------
           Hidden by default and only shown on a phone: on a desktop the nav is
           on screen already. Every role has one now (the markup says which
           tabs); the last is always More, which raises the nav as a sheet.
           Tabs are 3rem tall so every target clears the 44pt / 48dp minimum.

           On the card ground rather than the sidebar's dark one, so the bar
           and the sheet it raises read as one surface. The current tab is
           marked the way the rest of the app marks "you are here": the accent
           ink, on a pill of the accent's light tint. */
        .tabbar { display: none; }
        @media (max-width: 900px) {
            .tabbar {
                display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr);
                position: fixed; left: 0; right: 0; bottom: 0; z-index: 35;
                height: var(--tabbar-h); box-sizing: border-box;
                /* Clear of the iPhone home indicator (its whole inset), and never
                   flush to the glass on a phone without one. */
                padding: 0.3rem max(0.25rem, env(safe-area-inset-right, 0px))
                         var(--tabbar-bottom-gap) max(0.25rem, env(safe-area-inset-left, 0px));
                background: var(--card-bg); border-top: 1px solid var(--border);
            }
            .tabbar a, .tabbar .tabbar-more {
                display: flex; flex-direction: column; align-items: center; justify-content: center;
                gap: 0.2rem; min-width: 0; min-height: 3rem; margin: 0; padding: 0.15rem 0.1rem;
                color: var(--text-muted); text-decoration: none; cursor: pointer;
                background: transparent; border: none; border-radius: 0; box-shadow: none;
                font-family: var(--font-body); font-size: 0.75rem; font-weight: 600;
                letter-spacing: 0.01em; line-height: 1.1;
            }
            /* Six tabs (an admin's, with Roxxy) on a 360px phone: the labels
               a size smaller so "Dashboard" is not cut. */
            .tabbar:has(> :nth-child(6)) :is(a, .tabbar-more) { font-size: 0.6875rem; letter-spacing: 0; }
            .tabbar .tabbar-more:is(:hover, :active) {
                background: transparent; color: var(--text-muted); box-shadow: none; transform: none;
            }
            .tabbar-ico {
                position: relative; display: inline-flex; align-items: center; justify-content: center;
                width: 3.3rem; height: 1.9rem; border-radius: 999px; transition: background 0.15s ease;
            }
            .tabbar .ui-ico { width: 1.35rem; height: 1.35rem; vertical-align: 0; }
            .tabbar a.active, .tabbar .tabbar-more[aria-expanded="true"] {
                color: var(--accent-dark); font-weight: 800;
            }
            .tabbar a.active .tabbar-ico, .tabbar .tabbar-more[aria-expanded="true"] .tabbar-ico {
                background: var(--accent-light);
            }
            .tabbar-badge {
                position: absolute; top: -0.3rem; left: 2rem; min-width: 1.15rem; height: 1.15rem;
                padding: 0 0.28rem; border-radius: 999px; background: var(--red); color: var(--red-on);
                font-size: 0.75rem; font-weight: 700; display: flex; align-items: center;
                justify-content: center; line-height: 1;
            }
            /* ON THE FOOT OF THE SCREEN, NOT OF A SHRUNKEN PAGE. "Nav sometimes
               drags up" - in the installed app iOS shrinks the page for the
               keyboard, and now and then it does not grow it back when the
               keyboard goes. bottom: 0 then stays where the top of the keys
               was, mid-screen, with the list carrying on underneath. While
               no keyboard is up (html.tabbar-at-foot, set by the script
               after the bar) the bar hangs from the top of the screen
               by --foot-h, the tallest the page has been at this width with
               no keyboard up, which a page left shrunken never lowers. While
               one is, it rides on the keys as it always did.

               NOT 100lvh. That was the first try, and in the installed iPhone
               app it counts the status bar the page starts under, so the bar
               hung a status bar's height below the glass with only its top
               edge showing ("Footer way too low"). --foot-h is measured off
               the page itself, so at rest it is exactly where bottom: 0 is.

               --foot-shift: iOS 26 can leave visualViewport.offsetTop above 0
               after a keyboard or picker closes, with nothing to pan, and then
               draws every fixed element that far up the glass. The script
               measures it and the bar moves down by it; 0 everywhere else. */
            html.tabbar-at-foot .tabbar { top: calc(var(--foot-h) - var(--tabbar-h) + var(--foot-shift, 0px)); bottom: auto; }
            /* The bar is fixed, so the last of the page would sit under it. */
            .app-main:has(.tabbar) > main,
            html.has-tabbar .app-main > main { padding-bottom: calc(var(--tabbar-h) + 1.5rem); }
            /* The floating chat launcher hides below 860px on its own, but
               between 861px and 900px it would land on top of the bar - and
               Messenger is a tab or a tile by then, so it has nothing to add. */
            .app-shell:has(.tabbar) ~ .zchat-launcher { display: none !important; }
        }

        /* Trend sparklines (app/tools/driver_trends.py's sparkline_svg). The SVG
           ships classes and no colours, so it takes them from whichever theme is
           active - it used to bake dark ink onto a near-white plate, which put a
           stark white box in the middle of every dark-theme card. */
        .spark-plate { fill: color-mix(in srgb, var(--text) 4%, transparent); }
        .spark-line { stroke: var(--accent); }
        .spark-dot { fill: var(--accent); }
        .spark-value { fill: var(--text); }
        .spark-label { fill: var(--text-muted); }
        /* The latest week's dot, ringed in the card's own ground so it sits
           clear of the line; the best and worst weeks' numbers a step quieter
           than the latest's. */
        .spark-dot-end { stroke: var(--card-bg); stroke-width: 2px; }
        .spark-value-side { fill: var(--text-muted); }

        /* Inline line-icons (see _icons.html). Sized to the text they sit beside
           and inheriting its color, so nav/topbar icons adapt to light/dark. */
        .ui-ico { width: 1.15em; height: 1.15em; flex-shrink: 0; vertical-align: -0.19em; }
        /* The icon column is set with the nav rows above (fixed basis, so every
           label starts at the same x). No extra right margin here - it would
           knock the labels off that grid by a fraction. */

        /* ---------------- Cards / toolbar ----------------

           A CARD IS A SURFACE, NOT AN ANNOUNCEMENT.

           This rule used to paint every card with a 4px indigo left rail and a
           wash of --tile1-bg fading into the card colour. There are 461 cards
           across 141 templates, so that put the same loud frame around the
           page's subject, a row of filters, a fold-away help panel and an
           empty state alike - and a page where everything is emphasised has no
           emphasis left in it. "these packages are in desparate need of a
           visual redesign" was, more than any other single thing, this rule.

           So the default is quiet: the card colour, a hairline border, the
           standard lift. Emphasis is opt-in, one card at a time, and each
           modifier says what KIND of emphasis rather than merely "louder":

             .is-feature   the one card that IS the page's subject - the old
                           look, now spent where it buys something.
             .is-quiet     a panel nested inside another surface: no lift, so
                           it reads as part of its parent and not as a rival.
             .is-good / .is-warn / .is-bad
                           a soft ground in the semantic colour and a dot of
                           it, for a card whose STATE is the reason you are
                           looking at it. Semantic colour stays separate from
                           the accent on purpose: the accent means "this app",
                           not "this is fine".

           The feature rail is drawn as box-shadow: inset, never border-left,
           so adding it cannot shift the card's contents sideways by 3px the
           way swapping a 1px border for a 4px one does. */
        .card {
            background: var(--card-bg);
            border: 1px solid var(--border); border-radius: var(--radius);
            box-shadow: var(--shadow); padding: 1.25rem 1.5rem; margin-bottom: 1.25rem;
        }
        .card.is-feature {
            background: linear-gradient(180deg, var(--tile1-bg), var(--card-bg) 55%);
            box-shadow: inset 4px 0 0 0 var(--tile1), var(--shadow);
        }
        .card.is-quiet { background: var(--bg-subtle); box-shadow: none; }
        /* SOFT COLOUR WITH A DOT. Picked in One Look: "C, soft colour with a
           dot". A state card was a white card with a 4px rail, which is also
           how the feature card, the dashboard tiles and six page-own panels
           mark themselves, so "this is a problem" and "this is the page's
           subject" looked alike. Now the state is the whole ground: the
           status wash (--green-bg, --gold-bg, --red-bg), no edge and no lift,
           and a small round dot of the status colour at the top left.

           A card holds anything - headings, paragraphs, a form - so it is not
           made a flex or grid row. The dot is absolutely placed in a wider
           left padding, level with the first line, and the first child loses
           its top margin so that line is where the dot is. The border stays
           1px wide and only goes clear, so a state never reflows the card;
           the left padding is the same for all three, so status cards in a
           column line up. --text, --text-muted and --accent read on all three
           washes at 4.5:1 or better in all four themes
           (tests/test_onelook_the_shared_kit.py measures them). */
        .card.error, .card:is(.is-good, .is-warn, .is-bad) {
            --dot-x: 1.2rem; --dot-y: 1.72rem;
            position: relative; padding-left: 2.6rem; box-shadow: none;
        }
        .card:is(.is-good, .is-warn, .is-bad) { border-color: transparent; }
        .card:is(.is-good, .is-warn, .is-bad, .error) > :first-child { margin-top: 0; }
        .card.is-good { --dot: var(--green); background: var(--green-bg); }
        .card.is-warn { --dot: var(--gold); background: var(--gold-bg); }
        .card.is-bad { --dot: var(--red); background: var(--red-bg); }
        .card:is(.is-good, .is-warn, .is-bad)::before, .card.error::before {
            content: ""; position: absolute; left: var(--dot-x); top: var(--dot-y);
            width: 0.6rem; height: 0.6rem; border-radius: 50%; background: var(--dot); pointer-events: none;
        }
        /* A card rendered AS a message box (scheduler/driver_home's late-swap
           error). .card is defined after .error, so without this it wins the
           surface back and the box stops looking like a problem. It wears the
           message look: the card's ground, a red edge, a red dot. */
        .card.error { --dot: var(--red); background: var(--card-bg); border: 1.5px solid var(--red); color: var(--text); }
        @media (max-width: 600px) {
            .card.error, .card:is(.is-good, .is-warn, .is-bad) { --dot-x: 0.95rem; --dot-y: 1.47rem; padding-left: 2.2rem; }
        }
        
        .card.is-slim { padding: 0.65rem 0.9rem; }
        .card.is-slim:is(.is-good, .is-warn, .is-bad) {
            --dot-x: 0.85rem; --dot-y: calc(0.65rem + 0.775em - 0.3rem); padding-left: 2.2rem;
        }
        .toolbar { margin-bottom: 1.25rem; display: flex; gap: 0.75rem; align-items: center; flex-wrap: wrap; }
        .toolbar a, .toolbar button:where(:not(:has(> .ui-ico))) { display: inline-block; }

        /* ---------------- Dashboard ---------------- */
        .dashboard-grid {
            display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
            gap: 1rem; margin-top: 1.5rem;
        }
        /* Cycle a 5-color tile palette across every direct child of a grid
           (works for both .dashboard-card tiles and plain .card boxes used
           inside a grid, e.g. Manage Lists) so a row of boxes reads as
           genuinely distinct cards, not the same indigo card repeated. */
        .dashboard-grid > *:nth-child(5n+1) { --tile: var(--tile1); --tile-dk: var(--tile1-dark); --tile-bg: var(--tile1-bg); }
        .dashboard-grid > *:nth-child(5n+2) { --tile: var(--tile2); --tile-dk: var(--tile2-dark); --tile-bg: var(--tile2-bg); }
        .dashboard-grid > *:nth-child(5n+3) { --tile: var(--tile3); --tile-dk: var(--tile3-dark); --tile-bg: var(--tile3-bg); }
        .dashboard-grid > *:nth-child(5n+4) { --tile: var(--tile4); --tile-dk: var(--tile4-dark); --tile-bg: var(--tile4-bg); }
        .dashboard-grid > *:nth-child(5n)   { --tile: var(--tile5); --tile-dk: var(--tile5-dark); --tile-bg: var(--tile5-bg); }
        .dashboard-grid > .card, .dashboard-grid > .dashboard-card {
            background: linear-gradient(180deg, var(--tile-bg, var(--tile1-bg)), var(--card-bg) 55%);
            border-left-color: var(--tile, var(--tile1));
        }
        .dashboard-card {
            background: var(--card-bg); border: 1px solid var(--border); border-left: 4px solid var(--tile1);
            border-radius: var(--radius); box-shadow: var(--shadow); padding: 1.25rem;
            transition: var(--transition); display: block; color: var(--text);
        }
        .dashboard-card:hover {
            transform: translateY(-3px); box-shadow: var(--shadow-lift); color: var(--text);
            border-left-color: var(--tile-dk, var(--tile1-dark));
        }
        .dashboard-card .icon {
            font-size: 1.4rem; margin-bottom: 0.6rem; display: inline-flex; align-items: center; justify-content: center;
            width: 2.4rem; height: 2.4rem; border-radius: var(--radius-sm); color: #fff;
            background: linear-gradient(135deg, var(--tile, var(--tile1)), var(--tile-dk, var(--tile1-dark)));
            box-shadow: 0 2px 6px rgba(15, 23, 42, 0.18);
        }
        .dashboard-card h3 { margin: 0 0 0.3rem; font-size: 1rem; color: var(--text); }
        .dashboard-card p { margin: 0; font-size: 0.83rem; color: var(--text-muted); }

        /* ---------------- Figures: a page's row of counts ---------------- */
        /* ONE CARD, SPLIT INTO SEGMENTS. "ANY page that still uses these kind
           of boxes should be revisualized. They are out of date" - the boxes
           being a separate card per number, each with a coloured band across
           its top, the colour cycling through the five tile shades whether it
           meant anything or not. This is the look the Loadout, the Amerit
           Calendar and Service Type Compliance's settings already had: one
           card, a hairline between the numbers, a small label over each, and
           colour only on a number that needs attention.

           The markup: a .figs wrapper holding one .fig per number, and in each
           a .fig-k label, a .fig-v number and, if it needs one, a .fig-sub
           line under it. A .fig can be a link to the list the number counts.

           .is-bad (red, tinted), .is-warn (amber), .is-good (green), .is-quiet
           (muted, for a zero that is good news). A segment that is a link
           lights on hover. --figs-min sets how narrow a segment may get before
           the row wraps; a phone gets two or three to a row.

           THE HAIRLINES are each segment's own shadow, down and to the right,
           cut off by the card's edge: they stay right however the row wraps,
           which a border between siblings does not. Down and to the right, not
           up and to the left: a last row with fewer segments than the rows
           above left the segments over its empty end with no line under them
           and its last segment with no line after it ("we're also missing some
           bottom borders here", the Driver Metrics Trends scorecard).

           Picked in One Look: "B, one joined strip" - which is this. Its
           corner is the card's own radius, so a strip and the card under it
           read as the same kind of surface. */
        .figs {
            display: grid; grid-template-columns: repeat(auto-fit, minmax(var(--figs-min, 8.5rem), 1fr));
            margin: 0 0 1rem; background: var(--card-bg); border: 1px solid var(--border);
            border-radius: var(--radius); box-shadow: var(--shadow); overflow: hidden;
        }
        .fig {
            --c: var(--text); display: flex; flex-direction: column; gap: 0.3rem; min-width: 0;
            padding: 0.65rem 1rem 0.75rem; box-shadow: 1px 1px 0 var(--border);
            color: var(--text); text-decoration: none;
        }
        .fig-k {
            font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase;
            color: var(--text-muted); overflow-wrap: anywhere;
        }
        .fig-v {
            font-family: var(--font-nav); font-size: 1.75rem; font-weight: 700; line-height: 1;
            color: var(--c); font-variant-numeric: tabular-nums;
        }
        .fig-sub { font-size: 0.75rem; color: var(--text-muted); line-height: 1.35; }
        /* A row that is a list (ul / ol) or a definition list (dl, a dt label
           over a dd number) loses the browser's own indent and bullets, so the
           markup can say what the figures are without changing how they look. */
        ul.figs, ol.figs { padding: 0; list-style: none; }
        .figs dt, .figs dd { margin: 0; }
        .fig.is-good { --c: var(--green); }
        .fig.is-warn { --c: var(--gold); }
        .fig.is-bad { --c: var(--red); background: color-mix(in srgb, var(--red) 6%, var(--card-bg)); }
        .fig.is-bad .fig-k { color: var(--red); }
        .fig.is-quiet { --c: var(--text-muted); }
        a.fig:hover { background: color-mix(in srgb, var(--accent) 7%, var(--card-bg)); color: var(--text); }
        a.fig.is-bad:hover { background: color-mix(in srgb, var(--red) 11%, var(--card-bg)); }
        a.fig:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
        /* A .fig can also be a button: a count that is the filter for the list
           under it (the Organizations strip). It lets go of the button's own
           fill, edge and lift so it reads as a segment of the card, and the
           one picked (.is-on) wears an accent ring inside its edges. */
        button.fig {
            margin: 0; border: 0; border-radius: 0; background: none; font: inherit; text-align: left;
            min-height: 0; cursor: pointer; box-shadow: 1px 1px 0 var(--border); transform: none;
        }
        button.fig:hover, button.fig:active {
            border: 0; background: color-mix(in srgb, var(--accent) 7%, var(--card-bg)); color: var(--text);
            box-shadow: 1px 1px 0 var(--border); transform: none;
        }
        button.fig.is-bad:hover { background: color-mix(in srgb, var(--red) 11%, var(--card-bg)); }
        button.fig:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
        .fig.is-on, button.fig.is-on:hover { box-shadow: 1px 1px 0 var(--border); }
        /* THE PICKED TILE AND THE FOCUSED TILE share one layer: a soft accent
           tint and a thin accent ring, drawn INSIDE the tile by a ::after that
           is inset a few px and rounded to the card's own corner (the card's
           radius less its 1px edge less the inset). The old ring was a square
           inset shadow on the tile itself: at the card's corner it was cut by
           the card's curve and showed a notch, and on a phone, where the row
           wraps, every wrapped row's end tile did the same. An inset, rounded
           layer is concentric with the card wherever the tiles wrap, and it
           sits clear of the hairlines between tiles. It fades in and out
           (opacity only), so picking another tile hands the ring over. The
           square outline gives way to the same layer on focus (a heavier ring
           and a halo, so a focused picked tile still reads as focused). The
           Driver Trends cards (.dt-figs) are separate cards with their own. */
        .figs:not(.dt-figs) .fig { --fig-in: 3px; }
        .figs:not(.dt-figs) :is(a.fig, button.fig, .fig.is-on) { position: relative; }
        .figs:not(.dt-figs) :is(a.fig, button.fig, .fig.is-on)::after {
            content: ""; position: absolute; inset: var(--fig-in); pointer-events: none;
            border-radius: max(6px, calc(var(--radius) - 1px - var(--fig-in)));
            background: color-mix(in srgb, var(--accent) 8%, transparent);
            box-shadow: inset 0 0 0 1.5px var(--accent);
            opacity: 0; transition: opacity 180ms ease;
        }
        .figs:not(.dt-figs) .fig.is-on::after { opacity: 1; }
        .figs:not(.dt-figs) :is(a.fig, button.fig):focus-visible { outline: none; }
        .figs:not(.dt-figs) :is(a.fig, button.fig):focus-visible::after {
            opacity: 1; box-shadow: inset 0 0 0 2px var(--accent), inset 0 0 0 4px color-mix(in srgb, var(--accent) 28%, transparent);
        }
        @media (prefers-reduced-motion: reduce) {
            .figs:not(.dt-figs) :is(a.fig, button.fig, .fig.is-on)::after { transition: none; }
        }
        
        .figs.has-panels { overflow: visible; }
        
        .fig-v.is-word {
            font-family: var(--font-body); font-size: 1.1rem; line-height: 1.25;
            font-variant-numeric: normal; overflow-wrap: anywhere;
        }

        
        .seg {
            display: inline-flex; flex-wrap: nowrap; gap: 2px; max-width: 100%; margin: 0 0 1rem; padding: 3px;
            overflow-x: auto; scrollbar-width: none; vertical-align: middle;
            background: var(--card-bg); border: 1px solid var(--border); border-radius: 999px;
        }
        .seg::-webkit-scrollbar { display: none; }
        .seg > :is(a, button, label) {
            display: inline-flex; align-items: center; justify-content: center; gap: 0.35rem; flex: none;
            min-height: max(2rem, var(--tap-min)); margin: 0; padding: 0.3rem 0.95rem;
            font: inherit; font-size: 0.85rem; font-weight: 600; line-height: 1.2; white-space: nowrap;
            color: var(--text-muted); background: transparent; border: 0; border-radius: 999px;
            box-shadow: none; transform: none; text-decoration: none; cursor: pointer;
            -webkit-tap-highlight-color: transparent;
        }
        
        .seg > :is(a, button, label):hover {
            color: var(--text); background: var(--gray-bg); border: 0; box-shadow: none; transform: none;
        }
        .seg > :is(.is-on, [aria-current="page"], [aria-pressed="true"], [aria-selected="true"]),
        .seg > :is(.is-on, [aria-current="page"], [aria-pressed="true"], [aria-selected="true"]):hover,
        .seg > label:has(> input:checked), .seg > label:has(> input:checked):hover {
            color: var(--accent-dark); background: var(--accent-light); box-shadow: inset 0 0 0 1px var(--accent);
        }
        /* The track as a radio group (role="radiogroup", My App's Look, Menu
           and Style): the checked option is the current one. */
        .seg > [aria-checked="true"], .seg > [aria-checked="true"]:hover {
            color: var(--accent-dark); background: var(--accent-light); box-shadow: inset 0 0 0 1px var(--accent);
        }
        .seg > :is(a, button):focus-visible,
        .seg > label:has(> input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 1px; }
        .seg > button:disabled, .seg > button:disabled:hover,
        .seg > label:has(> input:disabled), .seg > label:has(> input:disabled):hover {
            color: var(--text-muted); background: transparent; box-shadow: none; opacity: 0.55; cursor: not-allowed;
        }
        
        .seg > label { position: relative; }
        .seg > label > input[type="radio"] {
            position: absolute; inset: 50% auto auto 50%; width: 1px; height: 1px; min-height: 0;
            margin: 0; padding: 0; border: 0; opacity: 0; pointer-events: none;
            -webkit-appearance: none; appearance: none;
        }

        
        .meter {
            --meter: var(--accent);
            position: relative; display: block; height: 1.25rem; min-width: 3.5rem; overflow: hidden;
            background: var(--card-bg); border: 1px solid var(--border); border-radius: 6px;
            font-size: 0.75rem; line-height: 1;
        }
        .meter > i {
            display: block; height: 100%; max-width: 100%; font-style: normal;
            background: color-mix(in srgb, var(--meter) 35%, var(--card-bg));
        }
        .meter > b {
            position: absolute; inset: 0 0.45rem; display: flex; align-items: center; justify-content: flex-end;
            font-weight: 700; font-variant-numeric: tabular-nums; color: var(--text); white-space: nowrap;
        }
        .meter.is-good { --meter: var(--green); }
        .meter.is-warn { --meter: var(--gold); }
        .meter.is-bad { --meter: var(--red); }
        
        .meter[hidden] { display: none; }
        .meter.is-thin { height: 0.4rem; min-width: 2rem; border-radius: 999px; overflow: visible; }
        .meter.is-thin > i { border-radius: inherit; background: var(--meter); }
        .meter.is-thin > b { display: none; }
        
        .meter[style*="--mark"]::after {
            content: ""; content: "" / attr(data-mark-label);
            position: absolute; top: 0; bottom: 0; left: calc(var(--mark) - 1px); width: 2px;
            border-radius: 1px; background: var(--text); box-shadow: 0 0 0 1px var(--card-bg);
            pointer-events: none;
        }
        .meter.is-thin[style*="--mark"]::after { top: -0.25rem; bottom: -0.25rem; }

        /* .stat-grid is only the Loadout's now, where it is a hook rather than a
           look: recomputeStats() writes `.stat-grid .value` by index, and the
           Loadout's own stylesheet draws the strip. The banded stat cards it
           used to lay out are gone - every row of counts is .figs above. */
        .stat-grid { display: grid; }

        /* ---------------- Tables ---------------- */
        /* .table-scroll: JS wraps every table in one of these at load time
           (see the script at the bottom) so any table can scroll
           horizontally on narrow viewports without every template needing
           its own wrapper div. */
        .table-scroll { overflow-x: auto; border-radius: var(--radius); box-shadow: var(--shadow); margin-bottom: 1.5rem; }
        .table-scroll table { box-shadow: none; margin-bottom: 0; }
        table {
            border-collapse: collapse; width: 100%; margin-bottom: 1.5rem; font-size: 0.88rem;
            background: var(--card-bg); border-radius: var(--radius); overflow: hidden; box-shadow: var(--shadow);
            border: 1px solid var(--border);
        }
        /* A PLAIN HEADER AND LINES. Picked in One Look: "B, plain header,
           lines". The header was a shaded band - a light gradient in Light,
           patched with a mix of its own in Harbor and Dusk and a zebra grey in
           Professional - set in small capitals, and every second row was
           striped. Forty-one pages then restyled that header their own way
           and twenty-six striped their own rows, so no two lists matched.

           Now a header is the column's name in normal case, a size under the
           rows, bold, in the page's ink, on the table's own ground, with a
           2px rule under it; rows are divided by a 1px line and there is no
           striping. The row under the pointer still lights. No hex and no
           per-theme rule: --text on --card-bg is 9:1 or better in every
           theme. */
        th, td { padding: 0.6rem 0.8rem; text-align: left; border-bottom: 1px solid var(--border); }
        th {
            background: transparent; color: var(--text); font-weight: 700; font-size: 0.82rem;
            white-space: nowrap; border-bottom: 2px solid var(--border);
        }
        tbody tr { transition: var(--transition); }
        tbody tr:hover { background: var(--accent-light); }
        /* A ROW THAT MEANS SOMETHING GETS A RAIL AND A TINT, NOT A FILL.

           tr.low-charge was var(--gold-light) - #fde68a - which is a
           saturated yellow, so three low Rivians read as a band painted across
           the page and the numbers inside them got harder to read, not easier.
           .compact-table's own docstring already says this in as many words:
           state belongs on the text or a pill, "never a full-bleed block. A
           table where every cell is a coloured rectangle is a quilt."

           Both hovers were also hardcoded light hex - #fee2e2 and #fcd34d -
           which is exactly the bug tests/test_row_tints_work_in_every_theme.py
           exists about: a background pinned to a light value while the text
           moves with the theme cannot be right in both, and three of the eight
           themes are dark.

           So the tint is mixed from the RUNNING theme's own red and amber, at a
           strength you read through rather than around, and the severity is
           carried by a 3px rail on the row's first cell - the same device
           .compact-table already offers as --rail.

           --tile3 and not --gold for the amber: --gold is the dark ink gold,
           meant to be read ON a pale ground, and it tints to brown. */
        tr.grounded, tr.grounded:nth-child(even) {
            background: color-mix(in srgb, var(--red) 10%, var(--card-bg));
        }
        tr.grounded:hover { background: color-mix(in srgb, var(--red) 18%, var(--card-bg)); }
        tr.grounded > td:first-child { box-shadow: inset 3px 0 0 0 var(--red); }
        tr.low-charge, tr.low-charge:nth-child(even) {
            background: color-mix(in srgb, var(--tile3) 11%, var(--card-bg));
        }
        tr.low-charge:hover { background: color-mix(in srgb, var(--tile3) 20%, var(--card-bg)); }
        tr.low-charge > td:first-child { box-shadow: inset 3px 0 0 0 var(--tile3); }
        tr.dimmed { color: var(--text-muted); }

        /* ---------------- The dense table skin ----------------

           "I like how the repair tickets page/table looks." That look was
           written inside #ticketsTable and could not be had anywhere else
           without copying it, which is how ten pages end up with ten nearly
           identical table styles that drift apart one fix at a time.

           So it lives here now, on .compact-table - a class five templates
           already carried and NOTHING defined, which is as clear a statement of
           intent as a dead class ever makes.

           WHAT MAKES IT WORK, and none of it is decoration:

             one line per row    a table you scan is a table whose rows are the
                                 same height. Anything that can outgrow its
                                 column asks for .clip and keeps its full text
                                 in a title attribute.
             digits that line up .num, because comparing 72 against 123 down a
                                 column is the entire job of a number column.
             state as colour     on the text or a .tag pill, never a full-bleed
                                 block. A table where every cell is a coloured
                                 rectangle is a quilt, and that is the word that
                                 got used.
             a rail, not a fill  --rail on a row draws a 3px edge on its first
                                 cell. It groups without shouting, and it
                                 leaves the row's own ground intact.

           The opt-in classes (.clip, .num, .rail) do nothing until a cell or
           row asks for them, so adopting this on a page is additive. */
        .compact-table tbody td { vertical-align: middle; white-space: nowrap; padding-top: 0.34rem; padding-bottom: 0.34rem; }
        .compact-table tbody td.clip { overflow: hidden; text-overflow: ellipsis; max-width: 0; }
        /* A COLGROUP IS AN INSTRUCTION, NOT A SUGGESTION - which is only true
           with a fixed layout, and .clip is why it matters.

           The auto layout algorithm sizes a column to its content, and the
           max-width above tells it .clip cells have none. So a .clip column
           collapsed to the width of its HEADING and handed the space it was
           given to whichever column had the longest text - very often an empty
           one. The Fleet List showed it plainly: Type was written 128px wide
           and rendered at 58, so "Branded Sprinter" came out as "Br...", while
           Date Received was written 100px, rendered at 124, and had nothing in
           any row. Current Issue lost 52px the same way, and Vendor 40.

           Fixed layout is what a colgroup means. Every column lands on the
           width it was given, a .clip cell shortens its own text instead of
           its neighbours' columns, and a table wider than the page scrolls
           inside .table-scroll rather than squeezing. Scoped with :has so it
           only applies where somebody actually wrote the widths down; a table
           with no colgroup keeps sizing itself. */
        .compact-table:has(> colgroup) { table-layout: fixed; }
        /* TABULAR DIGITS AND RIGHT-ALIGNMENT ARE TWO DIFFERENT DECISIONS, and
           bundling them was a mistake that quietly changed the tickets table.
           #ticketsTable td.num was tabular-nums ALONE; folding text-align into
           the shared class right-aligned its Ticket Date, Days Down, Age and
           Internal # columns - a change to the one page that was already right.

           .num is the figures rule: digits that line up column to column, so
           comparing 72 against 123 down a column works. It says nothing about
           alignment, because a VIN, a date and an id all want it and none of
           them wants to be flush right.

           .qty is for a quantity, where the ones column lining up IS the
           comparison - counts, money, days. It opts into both. */
        .compact-table td.num, .compact-table th.num,
        .compact-table td.qty, .compact-table th.qty { font-variant-numeric: tabular-nums; }
        .compact-table td.qty, .compact-table th.qty { text-align: right; }
        /* A number that needs attention is a number, not a red block. */
        .compact-table td.alert { color: var(--red); font-weight: 700; }
        .compact-table td.muted { color: var(--text-muted); }
        /* The row's handle - the one link that identifies it. */
        .compact-table .row-key { font-weight: 700; }
        /* The rail. Set --rail on the <tr> to colour it; unset rows get none,
           so a page can rail only the rows that mean something. */
        .compact-table tbody tr[style*="--rail"] > td:first-child,
        .compact-table tbody tr.railed > td:first-child { box-shadow: inset 3px 0 0 0 var(--rail, transparent); }
        /* A pill riding beside the value it qualifies: quieter than the value,
           because the value is what you came for and the pill is why you stop. */
        .compact-table .tag-sm { font-size: 0.75rem; padding: 0.05rem 0.33rem; }

        /* ---------------- The key/value panel ----------------

           A table-header cell in the LEFT column of a two-column table is a
           ROW's label, not a COLUMN's, and the header treatment above is drawn for a bar
           across the TOP of a table: a 180deg gradient, uppercase at 0.72rem,
           a 2px bottom rule. Turned on its side down a left column it makes
           every label a bevelled grey block - which is how the vehicle detail
           page came to read as three spreadsheets standing side by side rather
           than three panels of facts about one van.

           .kv is that panel. The label goes quiet and the value gets the
           weight, which is the right way round and the exact opposite of what
           was happening. It stays a table because the markup already is one
           and because two columns that line up is what tables are for.

           table.kv th is (0,1,2) on purpose: the per-theme `th` rules were
           (0,1,1), and without the extra weight a label kept whichever
           header bar the running theme painted. Since One Look the header is
           plain in every theme and the base `th` is bold 0.82rem in the
           page's ink; the label here is still quieter than that. */
        table.kv { box-shadow: none; border: 0; background: transparent; margin-bottom: 0; }
        table.kv th, table.kv td {
            background: transparent; border-bottom: 1px solid var(--border);
            padding: 0.5rem 0; vertical-align: top;
        }
        table.kv tr:last-child th, table.kv tr:last-child td { border-bottom: 0; }
        table.kv th {
            width: 1%; white-space: nowrap; padding-right: 1.25rem; text-align: left;
            font-family: var(--font-body); font-size: 0.78rem; font-weight: 600;
            text-transform: none; letter-spacing: 0; color: var(--text-muted);
        }
        table.kv td { color: var(--text); font-size: 0.92rem; }
        /* No row hover: these are facts about one thing, not rows to scan
           down and pick from. (Nothing stripes rows since One Look; the
           even-row rule stays so a page's own stripes stop at a .kv.) */
        table.kv tbody tr:nth-child(even) { background: transparent; }
        table.kv tbody tr:hover { background: transparent; }

        
        :where(table.card-table) { --ct-cols: 2; }
        @media (min-width: 601px) and (max-width: 1000px) {
            :where(table.card-table) { --ct-cols: 3; }
        }
        @media (max-width: 1000px) {
            .table-scroll:has(> table.card-table) {
                overflow: visible; box-shadow: none; border-radius: 0; background: none;
            }
            
            table.card-table, table.card-table > tbody {
                display: block; width: 100% !important; min-width: 0 !important; max-width: 100%;
            }
            table.card-table {
                margin-bottom: 1rem; border: 0; border-radius: 0; background: none; box-shadow: none; overflow: visible;
            }
            
            table.card-table > thead, table.card-table > colgroup,
            table.card-table > tbody > tr:has(> th):not(:has(> td)) { display: none; }
            table.card-table > tbody > tr {
                display: flex; flex-wrap: wrap; align-items: flex-start; gap: 0.55rem 1rem;
                position: relative; margin: 0 0 0.65rem; padding: 0.8rem 0.95rem;
                background: var(--card-bg); border: 1px solid var(--border); border-radius: var(--radius);
                box-shadow: var(--shadow);
            }
            table.card-table > tbody > tr:nth-child(even),
            table.card-table > tbody > tr:hover { background: var(--card-bg); }
            
            table.card-table > tbody > tr::before { content: ""; order: 1; flex: 0 0 100%; height: 0; }
            table.card-table > tbody > tr::after { content: ""; order: 3; flex: 0 0 100%; height: 0; }
            table.card-table > tbody > tr > :is(td, td.clip) {
                order: 2; display: block; box-sizing: border-box;
                flex: 0 0 calc((100% - (var(--ct-cols) - 1) * 1rem) / var(--ct-cols));
                min-width: 0; max-width: none; height: auto; padding: 0; border: 0; background: none;
                white-space: normal; overflow: visible; overflow-wrap: anywhere; text-align: left;
                box-shadow: none !important; position: static;
            }
            table.card-table > tbody > tr > td[data-label]::before {
                content: attr(data-label); display: block; margin: 0 0 0.1rem;
                font-size: 0.75rem; font-weight: 700; line-height: 1.3; letter-spacing: 0.04em;
                text-transform: uppercase; color: var(--text-muted); white-space: normal;
            }
            table.card-table > tbody > tr > :is(td:empty, td.ct-blank, td.ct-hide) { display: none; }
            table.card-table > tbody > tr > td.ct-check { order: 0; flex: none; align-self: center; }
            table.card-table > tbody > tr > td.ct-title {
                order: 0; flex: 1 1 8rem; font-size: 1.05rem; font-weight: 700; color: var(--text);
            }
            table.card-table > tbody > tr > td.ct-status {
                order: 0; flex: 0 1 auto; margin-left: auto; max-width: 100%; text-align: right;
            }
            table.card-table > tbody > tr > td.ct-status .tag { max-width: none; }
            table.card-table > tbody > tr > :is(td.ct-title, td.ct-status, td.ct-check, td.ct-actions)::before {
                content: none;
            }
            table.card-table > tbody > tr > :is(td.ct-wide, td[colspan]) { flex-basis: 100%; }
            table.card-table > tbody > tr > td.ct-actions {
                order: 4; flex: 1 1 100%; display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem;
                padding-top: 0.65rem; border-top: 1px solid var(--border);
            }
            table.card-table > tbody > tr > td.ct-actions > :is(a, form, button, .btn, details) { flex: 1 1 8rem; margin: 0; }
            table.card-table > tbody > tr > td.ct-actions > :is(a, form) > :is(button, .btn),
            table.card-table > tbody > tr > td.ct-actions > :is(button, .btn) { width: 100%; }
            table.card-table > tbody > tr > td.ct-actions form.inline { display: block; }
            
            table.card-table > tbody > tr > td.ct-actions > a:not(:has(> button, > .btn)) {
                display: inline-flex; align-items: center; justify-content: center; min-height: 2.5rem;
                padding: 0 1rem; border: 1px solid var(--border); border-radius: var(--radius-sm);
                background: var(--card-bg); color: var(--text);
                font-size: 0.88rem; font-weight: 600; text-decoration: none;
            }
            table.card-table > tbody > tr.grounded {
                --rail: var(--red); background: color-mix(in srgb, var(--red) 8%, var(--card-bg));
            }
            table.card-table > tbody > tr.low-charge {
                --rail: var(--tile3); background: color-mix(in srgb, var(--tile3) 9%, var(--card-bg));
            }
            table.card-table > tbody > tr:is([style*="--rail"], .railed, .grounded, .low-charge) {
                padding-left: 1.2rem; box-shadow: inset 4px 0 0 0 var(--rail, transparent), var(--shadow);
            }
        }

        
        @media (min-width: 901px) {
            body:has(#zchat-launcher) main,
            body:has(#sup-w) main { padding-bottom: 5.5rem; }
            body:has(#zchat-launcher):has(#sup-w) main { padding-bottom: 10rem; }
        }
        @media (max-width: 900px) {
            html:has(.tabbar):has(#sup-w) .app-main > main { padding-bottom: calc(var(--tabbar-h) + 5rem); }
        }


        /* ---------------- Status badges ---------------- */
        .badge {
            display: inline-block; padding: 0.2rem 0.65rem; border-radius: 999px; border: 1px solid transparent;
            font-size: 0.75rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.02em;
        }
        .badge-red { background: var(--red-bg); color: var(--red); border-color: #fecaca; }
        .badge-green { background: var(--green-bg); color: var(--green); border-color: #a7f3d0; }
        .badge-gold { background: #fef3c7; color: #92400e; border-color: #fde68a; }
        .badge-gray { background: var(--gray-bg); color: var(--gray); border-color: var(--border); }
        .badge-orange { background: #ffedd5; color: #9a3412; border-color: #fed7aa; }
        /* "Imported from Hera": a record brought over from Hera's export
           (app/tools/hera_import.py). Its own colour, so it reads as where the
           record came from and never as a severity or a status. */
        .badge-hera { background: var(--tile5-bg); color: color-mix(in srgb, var(--tile5) 70%, var(--text));
            border-color: color-mix(in srgb, var(--tile5) 40%, var(--border)); }

        /* ---------------- Write-up Details/Evidence sections ---------------- */
        .wu-section {
            border: 1px solid var(--border); border-left: 4px solid var(--tile1); border-radius: var(--radius-sm);
            background: var(--card-bg); padding: 0.85rem 1rem; margin: 0.6rem 0 1rem;
        }
        .wu-section-evidence { border-left-color: var(--tile3); }
        .wu-section-label {
            font-size: 0.75rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em;
            color: var(--text-muted); margin-bottom: 0.5rem;
        }
        .wu-section-label .wu-section-hint { font-weight: 400; text-transform: none; letter-spacing: 0; margin-left: 0.4rem; }
        .wu-section > *:last-child { margin-bottom: 0; }
        .wu-section img { max-width: 100%; border-radius: var(--radius-sm); margin-top: 0.4rem; }

        /* ---------------- Responsive ---------------- */
        /* THE NAV IS THE MORE SHEET ON A PHONE. "Yes let's do the phone
           recommendations too." It was a drawer from the left behind a
           hamburger in the top corner - the farthest point on the screen from
           a thumb - and a swipe from the edge that fought the browser's own
           back gesture. It is now a sheet that rises from the tab bar at the
           foot of the screen, each section a heading over a grid of tiles, with
           a box at the top to narrow them down.

           THE SAME ELEMENT, NOT A COPY. The sheet is the aside restyled, so a
           row added to the nav is a tile here without anybody remembering it,
           and whatever only_reachable took out of the nav is not here either.

           Hidden (not only moved) when shut, so a keyboard cannot tab into a
           sheet nobody can see. */
        .nav-filter, .nav-filter-none { display: none; }
        @media (max-width: 900px) {
            :root {
                --tabbar-bottom-gap: max(0.5rem, env(safe-area-inset-bottom, 0px));
                --tabbar-h: calc(3.65rem + var(--tabbar-bottom-gap));
            }
            /* WHAT IS PINNED TO THE BOTTOM SITS ABOVE THE TAB BAR. Save bars,
               the scheduler's bulk bar and the list pages' row pop-ups were
               written for a screen with nothing at its foot; the office has a
               tab bar there now too, so they lift by its height. */
            html:has(.tabbar) { --dock-h: var(--tabbar-h); }
            /* The same, from the first byte: has-tabbar is printed on <html> by
               the server (every signed-in page has the bar), so a fixed layer
               that sits above the bar - the open chat on a phone - is laid out
               right the first time instead of a bar too tall until the bar, the
               last thing in <body>, has been parsed. */
            html.has-tabbar { --dock-h: var(--tabbar-h); }
            /* Landscape on a notched phone: keep the page out of the notch. */
            body { padding-left: env(safe-area-inset-left, 0px); padding-right: env(safe-area-inset-right, 0px); }
            .topbar { padding-left: max(1rem, env(safe-area-inset-left, 0px));
                      padding-right: max(1rem, env(safe-area-inset-right, 0px)); }
            .app-main { margin-left: 0; }
            .topbar { display: flex; }
            .sidebar-collapse { display: none !important; }
            .sidebar {
                top: auto; left: 0; right: 0; bottom: var(--tabbar-h); width: auto;
                max-height: calc(100vh - var(--tabbar-h) - 3.25rem);
                max-height: calc(100dvh - var(--tabbar-h) - 3.25rem);
                border: none; border-radius: 20px 20px 0 0;
                background: var(--card-bg); color: var(--text);
                box-shadow: 0 -12px 40px rgba(16, 35, 63, 0.25);
                z-index: 34; overscroll-behavior: contain;
                transform: translateY(calc(100% + var(--tabbar-h) + 12px));
                visibility: hidden;
                transition: transform 0.22s ease, visibility 0s linear 0.22s;
            }
            .sidebar.open { transform: none; visibility: visible; transition: transform 0.22s ease; }
            /* And the sheet stays on the bar when the bar is held to the foot
               of the screen (html.tabbar-at-foot, by the tab bar): 100% is
               the page as iOS has it, --foot-h its height with no keyboard,
               and the difference is how far short of the foot a shrunken
               page ends. */
            html.tabbar-at-foot .sidebar { bottom: calc(var(--tabbar-h) + 100% - var(--foot-h) - var(--foot-shift, 0px)); }
            .sidebar::before {
                content: ""; flex: none; align-self: center;
                width: 44px; height: 5px; border-radius: 999px;
                background: var(--border); margin: 0.55rem 0 0.35rem;
            }
            .sidebar-overlay.open { display: block; }
            /* The support button floats in the corner the sheet rises into. */
            html:has(#sidebar.open) #sup-w { display: none; }
            .sidebar-head { border-bottom: none; padding: 0; }
            .sidebar-head-row, .sidebar-icon-actions { display: none !important; }
            .sidebar-head :is(.station-switch, .station-static) {
                margin: 0.2rem 1.1rem 0.4rem; padding-left: 0; color: var(--text-muted);
            }
            /* In the sheet the pill is a row of its own under your card, the
               width of the sheet and the height of a thumb. */
            .sidebar-head .station-switch-pill { width: 100%; height: 2.75rem; padding: 0 0.95rem; border-radius: 12px; font-size: 1.02rem; }
            .sidebar-head .station-switch-caret { margin-left: auto; }
            /* AND ON THE BAR, beside the DSP's name: the code alone, as a chip,
               so the station is on screen without opening anything. The name
               gives way to it, cut short, rather than pushing it off the bar. */
            .topbar .station-chip { flex: none; margin: 0; }
            .topbar .station-chip .station-switch-pill {
                --stn-rim: var(--accent);    /* the rim of the Search and My App squares beside it */
                height: 2.25rem; gap: 0.3rem; padding: 0 0.6rem 0 0.5rem; font-size: 1rem;
            }
            .topbar .station-chip .station-switch-name { display: none; }
            /* THE NAME GIVES WAY, ALWAYS. It only did beside the station chip,
               so a long DSP name pushed Search, What's new and My App off a
               narrow phone; it is cut short now, whatever sits beside it. */
            .topbar .topbar-brand { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
            .nav-filter {
                display: flex; align-items: center; gap: 0.6rem; flex: none;
                margin: 0.35rem 1.1rem 0.2rem; padding: 0 0.85rem; min-height: 2.9rem;
                border-radius: 12px; background: var(--gray-bg); color: var(--text-muted);
            }
            .nav-filter .ui-ico { width: 1.15rem; height: 1.15rem; vertical-align: 0; }
            .nav-filter input {
                flex: 1; min-width: 0; width: auto; margin: 0; padding: 0.6rem 0;
                border: none; background: transparent; box-shadow: none;
                font-size: 1rem; color: var(--text);
            }
            .nav-filter input:focus { box-shadow: none; outline: none; }
            .nav-filter-none:not([hidden]) {
                display: block; margin: 1rem 1.1rem 0; color: var(--text-muted); font-size: 0.9rem;
            }
            /* SEARCHING THE SHEET FILLS WHAT THE KEYBOARD LEAVES. "Don't like
               how when searching, results disappear behind the keyboard." The
               sheet sits on the tab bar at the foot of the screen, and an
               iPhone keyboard rises over that foot without moving anything
               fixed - so the box stayed in sight and every page it found went
               under the keys. While the box is in use the sheet is the visible
               strip instead (--nav-vv-top / --nav-vv-h, from visualViewport,
               the same measure the chat composer uses): the box at the top,
               the pages it found in the rest, the profile and station picker
               out of the way, and a Cancel to put it all back. */
            .nav-filter-cancel { display: none; }
            :root { --nav-vv-top: 0px; --nav-vv-h: 100vh; }
            html.nav-searching .sidebar.open {
                top: var(--nav-vv-top); bottom: auto;
                height: var(--nav-vv-h); max-height: none;
                border-radius: 0; padding-top: env(safe-area-inset-top, 0px);
            }
            html.nav-searching .sidebar.open::before,
            html.nav-searching .sidebar-me-sheet,
            html.nav-searching .sidebar-head,
            html.nav-searching .sidebar-user,
            html.nav-searching .sidebar-footer { display: none; }
            html.nav-searching .nav-filter { margin-top: 0.75rem; }
            html.nav-searching .tabbar { visibility: hidden; }
            html.nav-searching .nav-filter-cancel {
                display: inline-flex; flex: none; align-items: center; min-height: 0;
                margin: 0 -0.35rem 0 0; padding: 0.35rem 0.4rem; border: 0; background: transparent;
                box-shadow: none; color: var(--accent); font-size: 0.95rem; font-weight: 600;
            }
            html.nav-searching .nav-filter-cancel:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
            .sidebar-nav { flex: none; padding: 0 1.1rem 0.9rem; }
            .sidebar-nav [hidden] { display: none !important; }
            details.sidebar-section { margin: 0.85rem 0 0; }
            details.sidebar-section > summary {
                padding: 0 0 0.5rem; cursor: default;
                color: var(--text-muted); font-family: var(--font-body);
                font-size: 0.75rem; letter-spacing: 0.07em;
            }
            details.sidebar-section > summary:hover { color: var(--text-muted); }
            details.sidebar-section > summary::after, .sidebar-section-caret { display: none; }
            .sidebar-section-body {
                display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 0.5rem; padding: 0;
            }
            .sidebar-nav .sidebar-section-body a {
                position: relative; flex-direction: column; justify-content: center; gap: 0.4rem;
                min-height: 4.5rem; margin: 0; padding: 0.6rem 0.2rem;
                border: none; border-radius: 12px; background: var(--page-bg);
                color: var(--text); font-family: var(--font-body); font-size: 0.75rem;
                font-weight: 600; letter-spacing: 0; line-height: 1.2;
                text-align: center; white-space: normal; overflow-wrap: anywhere;
            }
            .sidebar-nav .sidebar-section-body a .ui-ico {
                width: 1.4rem; height: 1.4rem; flex: none; color: var(--accent-dark); opacity: 1; vertical-align: 0;
            }
            .sidebar-nav .sidebar-section-body a:hover { background: var(--gray-bg); color: var(--text); }
            .sidebar-nav .sidebar-section-body a.active { background: var(--accent-light); color: var(--accent-dark); }
            .sidebar-nav .sidebar-section-body a .nav-chat-badge { position: absolute; top: 0.35rem; right: 0.4rem; margin: 0; }
            .sidebar-nav .sidebar-section-body a .nav-lock { position: absolute; top: 0.3rem; right: 0.35rem; margin: 0; }
            /* A tile is a name too; the side bar's line under it and its Quick
               actions stay on the side bar. */
            .sidebar-nav .sidebar-section-body :is(.nav-desc, .nav-quick) { display: none; }
            /* A pinned tile wraps its name like every other tile here, rather
               than cutting it off the way a row in the desktop panel does. */
            .sidebar-nav .sidebar-section-body a .nav-pinned-label { flex: none; white-space: normal; overflow: visible; }

            /* A SHEET OF FORTY-FIVE TILES IS A WALL; THIS IS ITS MAP. "This seems very
               cluttered." On a phone the sheet (the script
               below adds .nav-folds) shows each section as one row - its icon, its
               name, how many pages it holds - and opens in place, one at a time. YOUR
               PINS ARE THE FIRST THING: big tiles, three across, open and not
               foldable, because they are the pages you chose to have in reach. A
               driver's sheet, which is short, keeps the tiles it always had. */
            #sidebar.nav-folds details.sidebar-section { margin: 0; border-top: 1px solid var(--border); }
            #sidebar.nav-folds details.sidebar-section > summary {
                display: flex; align-items: center; gap: 0.8rem; width: 100%; box-sizing: border-box;
                min-height: 3rem; padding: 0; cursor: pointer;
                color: var(--text); font-family: var(--font-body); font-size: 0.98rem; font-weight: 700;
                letter-spacing: 0; text-transform: none;
            }
            #sidebar.nav-folds details.sidebar-section > summary:hover { color: var(--text); }
            #sidebar.nav-folds details.sidebar-section > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
            #sidebar.nav-folds details.sidebar-section > summary .sidebar-section-ico { display: inline-flex; color: var(--accent-dark); }
            #sidebar.nav-folds details.sidebar-section > summary .sidebar-section-ico .ui-ico { width: 1.3rem; height: 1.3rem; vertical-align: 0; }
            #sidebar.nav-folds details.sidebar-section > summary::after {
                display: block; margin-left: auto; content: attr(data-n) "  \25B8";
                color: var(--text-muted); font-size: 0.82rem; font-weight: 600;
            }
            #sidebar.nav-folds details[open].sidebar-section > summary::after { content: attr(data-n) "  \25BE"; }
            #sidebar.nav-folds details.is-shown > summary { color: var(--accent-dark); }
            #sidebar.nav-folds .sidebar-section-body { padding: 0.1rem 0 0.7rem; }
            #sidebar.nav-folds details[data-section=pinned] { border-top: 0; }
            #sidebar.nav-folds details[data-section=pinned] > summary {
                min-height: 0; padding: 0.3rem 0 0.55rem; cursor: default;
                color: var(--text-muted); font-size: 0.75rem; letter-spacing: 0.07em; text-transform: uppercase;
            }
            #sidebar.nav-folds details[data-section=pinned] > summary:hover { color: var(--text-muted); }
            #sidebar.nav-folds details[data-section=pinned] > summary .sidebar-section-ico,
            #sidebar.nav-folds details[data-section=pinned] > summary::after { display: none; }
            #sidebar.nav-folds details[data-section=pinned] .sidebar-section-body {
                grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.6rem; padding: 0 0 0.9rem;
            }
            #sidebar.nav-folds details[data-section=pinned] .sidebar-section-body a {
                min-height: 5.6rem; border-radius: 16px; font-size: 0.86rem; gap: 0.5rem;
            }
            #sidebar.nav-folds details[data-section=pinned] .sidebar-section-body a .ui-ico { width: 1.75rem; height: 1.75rem; }
            .sidebar-user {
                flex: none; margin-top: 0.4rem; padding: 0.85rem 1.1rem;
                border-top: 1px solid var(--border);
            }
            .sidebar-user > span { color: var(--text); font-size: 0.85rem; }
            .sidebar-user-app, .sidebar-user button { border-color: var(--border); color: var(--text); }
            /* The sheet's head says who you are, so its foot keeps only the
               two buttons. */
            .sidebar-user .nav-me { display: none; }
            /* YOU, AT THE HEAD OF THE SHEET: a card on the sheet's own ground,
               face, full name, and role with the DSP and station under it.
               Card tokens here, not the nav's - the sheet is --card-bg. */
            .sidebar-me-sheet { display: flex; align-items: center; gap: 0.5rem; flex: none; margin: 0.1rem 1.1rem 0.25rem; }
            .sidebar-me-sheet .nav-me {
                display: grid; grid-template-columns: auto minmax(0, 1fr) auto; flex: 1; min-width: 0;
                align-items: center; gap: 0.1rem 0.7rem;
                padding: 0.3rem 0.35rem; border-radius: 14px;
                background: transparent; color: var(--text);
            }
            .sidebar-me-sheet .nav-me:hover, .sidebar-me-sheet .nav-me.is-active { background: var(--gray-bg); color: var(--text); }
            .sidebar-me-sheet .nav-me:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
            .sidebar-me-sheet .nav-me-av {
                width: 2.4rem; height: 2.4rem; font-size: 0.85rem;
                background: var(--accent); color: var(--accent-on);
            }
            .sidebar-me-sheet .nav-me.is-viewing .nav-me-av { box-shadow: 0 0 0 2px var(--page-bg), 0 0 0 4px var(--accent); }
            .sidebar-me-sheet .nav-me-eye { background: var(--page-bg); color: var(--accent-dark); }
            .sidebar-me-sheet .nav-me-name { color: var(--text); font-size: 0.95rem; }
            .sidebar-me-sheet .nav-me-role { color: var(--text-muted); font-size: 0.78rem; white-space: normal; }
            /* SWITCH ACCOUNT IS A SQUARE BESIDE YOU, NOT A CARD UNDER YOU. It was a
               full-width card of its own, 110px of a phone before the first page.
               The button, the popover it opens and its words for a screen reader
               are the same; only the card is gone. */
            .sidebar-me-sheet .acct-sheet-btn {
                flex: none; width: 2.75rem; height: 2.75rem; min-height: 0; margin: 0; padding: 0;
                justify-content: center; border-radius: 12px;
            }
            .sidebar-me-sheet .acct-sheet-ico { width: auto; }
            .sidebar-me-sheet .acct-sheet-txt, .sidebar-me-sheet .acct-sheet-go { display: none; }
            /* Placed, not left to auto-flow: the viewing-as line spans the
               card under the first row, and without a cell of its own "My App"
               fell to a third row beneath it. */
            .sidebar-me-sheet .nav-me-go {
                grid-column: 3; grid-row: 1;
                display: inline-flex; align-items: center; white-space: nowrap;
                color: var(--accent-dark); font-family: var(--font-body); font-size: 0.82rem; font-weight: 700;
            }
            .sidebar-me-sheet .nav-me-as {
                grid-column: 1 / -1; grid-row: 2; padding: 0.45rem 0.7rem; border-radius: 10px;
                background: var(--accent-light); color: var(--text);
                box-shadow: inset 0 0 0 1px var(--accent); font-size: 0.82rem;
            }
            .sidebar-me-sheet .nav-me-as-k { color: var(--accent-dark); }
            .sidebar-footer { flex: none; color: var(--text-muted); opacity: 1; padding-bottom: 1.1rem; }
        }
        @media (max-width: 359px) {
            .sidebar-section-body { grid-template-columns: repeat(3, minmax(0, 1fr)); }
        }
        @media (max-width: 900px) and (prefers-reduced-motion: reduce) {
            .sidebar, .sidebar.open { transition: none; }
        }

        /* THE PAGES BESIDE THIS ONE, as chips under the phone's title bar - the
           top bar's tab row, sized for a thumb. Cut from the nav by
           app/tools/nav_sections.py; scrolls sideways rather than wrapping so it
           is always one row. */
        .topbar-chips { display: none; }
        @media (max-width: 900px) {
            .topbar-chips {
                display: flex; gap: 0.5rem; overflow-x: auto; scrollbar-width: none;
                padding: 0.6rem 1rem; background: var(--card-bg); border-bottom: 1px solid var(--border);
            }
            .topbar-chips::-webkit-scrollbar { display: none; }
            .topbar-chips a {
                flex: none; display: inline-flex; align-items: center; gap: 0.35rem;
                height: 2.3rem; padding: 0 0.95rem; border-radius: 999px;
                background: var(--gray-bg); color: var(--text); text-decoration: none;
                font-size: 0.87rem; font-weight: 600; white-space: nowrap;
            }
            .topbar-chips a.active { background: var(--text); color: var(--card-bg); }
            .topbar-chips a svg { display: none; }
            .topbar-chips a .nav-desc { display: none; }
            .topbar-chips a .nav-lock .nav-lock-ico { display: inline-block; }
            .topbar-chips a .nav-chat-badge { margin-left: 0.1rem; }
        }

        
        .settings-tabs, .set-nav-groups {
            position: relative; display: flex; flex-wrap: nowrap; align-items: stretch; gap: 2px;
            width: fit-content; max-width: 100%; margin-bottom: 1.4rem; padding: 4px;
            overflow-x: auto; overflow-y: hidden; scrollbar-width: none; overscroll-behavior-x: contain;
            background: var(--tab-track); border-radius: 12px;
            box-shadow: inset 0 1px 2px var(--tab-track-shade);
            --fade-bg: var(--tab-track); --fade-pad: 4px;
        }
        .settings-tabs::-webkit-scrollbar, .set-nav-groups::-webkit-scrollbar { display: none; }
        .settings-tab, .set-nav-group {
            position: relative; z-index: 1; flex: none; display: inline-block; max-width: 15rem;
            padding: 0.625rem 1rem; border-radius: 9px; line-height: 1.25rem; align-content: center;
            font-size: 0.94rem; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
            text-decoration: none; color: var(--text-muted); background: transparent;
            -webkit-tap-highlight-color: transparent; touch-action: manipulation;
            -webkit-user-select: none; user-select: none; -webkit-touch-callout: none;
            transition: color 0.15s ease, background-color 0.12s ease, transform 0.12s cubic-bezier(0.23, 1, 0.32, 1);
        }
        .settings-tab:hover, .set-nav-group:hover { color: var(--text); text-decoration: none; }
        
        @media (hover: hover) {
            .settings-tab:not(.active):hover, .set-nav-group:not(.active):hover { background: var(--tab-hover); }
        }
        .settings-tab:active, .set-nav-group:active { transform: scale(0.97); }
        .settings-tab.active, .set-nav-group.active {
            color: var(--text); background: var(--tab-thumb);
            box-shadow: var(--tab-thumb-shadow), 0 0 0 1px var(--tab-thumb-ring);
        }
        .settings-tab:focus-visible, .set-nav-group:focus-visible {
            outline: 2px solid var(--tab-mark); outline-offset: -2px;
        }
        .settings-tab .ui-ico, .set-nav-group .ui-ico {
            width: 1.05rem; height: 1.05rem; margin-right: 0.45rem; vertical-align: -0.2em;
            color: var(--text-muted); transition: color 0.15s ease;
        }
        .settings-tab:hover .ui-ico, .set-nav-group:hover .ui-ico { color: var(--text); }
        .settings-tab.active .ui-ico, .set-nav-group.active .ui-ico { color: var(--tab-mark); }
        
        .settings-tab.nav-locked { opacity: 0.6; }
        .settings-tab.nav-locked:hover { opacity: 0.85; }
        .settings-tab .nav-lock { margin-left: 0.35rem; }
        
        .settings-tab.is-urgent { color: var(--red); }
        .settings-tab.is-urgent::before {
            content: ""; display: inline-block; width: 0.45rem; height: 0.45rem; margin-right: 0.4rem;
            border-radius: 50%; background: var(--red); vertical-align: 0.08em;
        }
        
        .settings-tabs::before, .settings-tabs::after, .set-nav-groups::before, .set-nav-groups::after {
            content: ""; position: sticky; z-index: 2; flex: 0 0 2rem; align-self: stretch;
            pointer-events: none; opacity: 0; transition: opacity 0.15s ease;
        }
        .settings-tabs::before, .set-nav-groups::before {
            left: calc(-1 * var(--fade-pad)); margin-right: calc(-2rem - 2px);
            background: linear-gradient(90deg, var(--fade-bg) 35%, transparent);
        }
        .settings-tabs::after, .set-nav-groups::after {
            right: calc(-1 * var(--fade-pad)); margin-left: calc(-2rem - 2px);
            background: linear-gradient(270deg, var(--fade-bg) 35%, transparent);
        }
        :is(.settings-tabs, .set-nav-groups).is-clip-l::before,
        :is(.settings-tabs, .set-nav-groups).is-clip-r::after { opacity: 1; }
        
        .tabs-scroll {
            display: none; position: sticky; z-index: 3; flex: none; align-self: center; place-items: center;
            width: 1.75rem; height: 1.75rem; min-height: 0; margin: 0; padding: 0; border-radius: 50%;
            border: 1px solid var(--border); background: var(--card-bg); color: var(--text);
            box-shadow: 0 1px 3px color-mix(in srgb, var(--text) 18%, transparent); cursor: pointer; transform: none;
        }
        .tabs-scroll.is-l { left: 0; margin-right: calc(-1.75rem - 2px); }
        .tabs-scroll.is-r { right: 0; margin-left: calc(-1.75rem - 2px); }
        .tabs-scroll:hover { background: var(--card-bg); color: var(--text); border-color: var(--text-muted); transform: none; }
        .tabs-scroll:focus-visible { outline: 2px solid var(--tab-mark); outline-offset: 1px; }
        .tabs-scroll .ui-ico { width: 1rem; height: 1rem; vertical-align: 0; }
        @media (hover: hover) and (pointer: fine) {
            :is(.settings-tabs, .set-nav-groups).is-clip-l > .tabs-scroll.is-l,
            :is(.settings-tabs, .set-nav-groups).is-clip-r > .tabs-scroll.is-r { display: grid; }
        }
        @media (max-width: 30rem) {
            .settings-tab, .set-nav-group { padding-left: 0.85rem; padding-right: 0.85rem; }
        }
        @media (prefers-reduced-motion: reduce) {
            .settings-tab, .set-nav-group, .settings-tab .ui-ico, .set-nav-group .ui-ico,
            :is(.settings-tabs, .set-nav-groups)::before, :is(.settings-tabs, .set-nav-groups)::after { transition: none; }
            .settings-tab:active, .set-nav-group:active { transform: none; }
        }

        
        @media (prefers-reduced-motion: no-preference) {
            @view-transition { navigation: auto; }
        }
        .tab-vt-thumb {
            position: absolute; z-index: 0; border-radius: 9px; pointer-events: none;
            background: var(--tab-thumb); box-shadow: var(--tab-thumb-shadow), 0 0 0 1px var(--tab-thumb-ring);
            view-transition-name: tab-thumb;
        }
        .is-sliding > .active { background: transparent; box-shadow: none; }
        ::view-transition-group(tab-thumb) {
            z-index: 1; animation-duration: 240ms; animation-timing-function: cubic-bezier(0.77, 0, 0.175, 1);
        }
        ::view-transition-old(tab-thumb), ::view-transition-new(tab-thumb) { height: 100%; animation-duration: 240ms; }
        ::view-transition-group(*.tab-label) { z-index: 2; animation-duration: 240ms; }
        ::view-transition-old(*.tab-label), ::view-transition-new(*.tab-label) { animation-duration: 240ms; }
        html.tab-sliding::view-transition-old(root), html.tab-sliding::view-transition-new(root) { animation: none; }

        
        .set-nav { margin-bottom: 1.25rem; }
        .set-nav > .set-nav-groups { margin-bottom: 0.3rem; }
        .set-nav > :is(.set-nav-items, .plat-strip) { margin-bottom: 0; }

        
        .set-nav-items {
            display: flex; flex-wrap: nowrap; gap: 1.35rem; margin: 0 0 1rem; padding: 4px 0.5rem 4px 1.25rem;
            overflow-x: auto; overflow-y: hidden; scrollbar-width: none; overscroll-behavior-x: contain;
        }
        .set-nav-items::-webkit-scrollbar { display: none; }
        .set-nav-item {
            position: relative; flex: none; display: inline-block; padding: 0.4rem 0 0.5rem; line-height: 1.25rem;
            font-size: 0.88rem; font-weight: 600; white-space: nowrap; text-decoration: none;
            color: var(--text-muted); background: none; border: 0; border-radius: 0;
            -webkit-tap-highlight-color: transparent; touch-action: manipulation; transition: color 0.15s ease;
        }
        .set-nav-item::after {
            content: ""; position: absolute; left: 0; right: 0; bottom: 0.15rem; height: 2px; border-radius: 2px;
            background: transparent; transition: background-color 0.15s ease;
        }
        .set-nav-item:hover { color: var(--text); text-decoration: none; }
        @media (hover: hover) {
            .set-nav-item:not(.active):hover::after { background: var(--border); }
        }
        .set-nav-item.active { color: var(--text); }
        .set-nav-item.active::after { background: var(--tab-mark); }
        .set-nav-item:focus-visible { outline: 2px solid var(--tab-mark); outline-offset: 2px; border-radius: 4px; }
        .set-nav-items:is(.is-clip-l, .is-clip-r) {
            -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 var(--fade-l, 0px),
                                                #000 calc(100% - var(--fade-r, 0px)), transparent 100%);
            mask-image: linear-gradient(90deg, transparent 0, #000 var(--fade-l, 0px),
                                        #000 calc(100% - var(--fade-r, 0px)), transparent 100%);
        }
        .set-nav-items.is-clip-l { --fade-l: 2rem; }
        .set-nav-items.is-clip-r { --fade-r: 2rem; }
        @media (prefers-reduced-motion: reduce) {
            .set-nav-item, .set-nav-item::after { transition: none; }
        }

        
        @media (max-width: 900px) {
            .scroll-row {
                flex-wrap: nowrap; overflow-x: auto; overflow-y: hidden;
                scrollbar-width: none; overscroll-behavior-x: contain;
            }
            .scroll-row::-webkit-scrollbar { display: none; }
            .scroll-row > * { flex: none; }
            .settings-tabs { margin-bottom: 1rem; }
            /* Fleet List's Vans / Devices switch (_fleet_list_tabs.html). */
            .fl-seg { margin: 0 0 0.9rem; } .fl-seg[hidden] { display: none; }
            .set-nav { margin-bottom: 0.85rem; }

            
            .topbar-chips { --fade-bg: var(--card-bg); --fade-pad: 0.5rem; padding-left: 0.5rem; padding-right: 0.5rem; }
            .topbar-chips::before, .topbar-chips::after {
                content: ""; position: sticky; z-index: 2; flex: 0 0 2rem; align-self: stretch;
                pointer-events: none; opacity: 0; transition: opacity 0.15s ease;
            }
            
            .topbar-chips::before {
                left: calc(-1 * var(--fade-pad)); margin-right: -2rem;
                background: linear-gradient(90deg, var(--fade-bg) 35%, transparent);
            }
            .topbar-chips::after {
                right: calc(-1 * var(--fade-pad)); margin-left: -2rem;
                background: linear-gradient(270deg, var(--fade-bg) 35%, transparent);
            }
            .topbar-chips.is-clip-l::before, .topbar-chips.is-clip-r::after { opacity: 1; }
            .scroll-row:is(.is-clip-l, .is-clip-r) {
                -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 var(--fade-l, 0px),
                                                    #000 calc(100% - var(--fade-r, 0px)), transparent 100%);
                mask-image: linear-gradient(90deg, transparent 0, #000 var(--fade-l, 0px),
                                            #000 calc(100% - var(--fade-r, 0px)), transparent 100%);
            }
            .scroll-row.is-clip-l { --fade-l: 2rem; }
            .scroll-row.is-clip-r { --fade-r: 2rem; }
        }
        @media (max-width: 900px) and (prefers-reduced-motion: reduce) {
            .topbar-chips::before, .topbar-chips::after { transition: none; }
        }
        
        .settings-tabs:has(+ .set-nav-items) { margin-bottom: 0.3rem; }
        .settings-tabs + .set-nav-items { margin-bottom: 1rem; }
        .settings-tabs:not(:has(> .settings-tab:not(.active))),
        .set-nav-items:not(:has(> .set-nav-item:not(.active))) { display: none; }

        
        @media (max-width: 900px) {
            body:has(main .set-nav) .topbar-chips { display: none; }
        }

        /* LESS ON A PHONE. "mobile is too cluttered."
           - The Home chips (Dashboard, Messenger, Roxxy Admin, Reminders) are
             the tab bar again, a row under it; they stand down here. Reminders
             is under More, in Home. A section's own pages keep their chips.
           - The round "?" and the page tour's light bulb are two buttons at the
             top of the More sheet, which says so with the "?" button's unread
             dot on the More tab while a reply waits.
           - "Powered by" is not repeated at the foot of a phone's pages. */
        @media (max-width: 900px) {
            html.has-tabbar .topbar-chips[data-section=home] { display: none; }
            html.has-tabbar .dsp-credit { display: none; }
            .sheet-acts:not([hidden]) { display: flex; gap: 0.5rem; flex: none; margin: 0.5rem 1.1rem 0.2rem; }
            html.nav-searching .sheet-acts { display: none; }
            .sheet-act:not([hidden]) {
                flex: 1 1 0; min-width: 0; position: relative;
                display: inline-flex; align-items: center; justify-content: center; gap: 0.5rem;
                min-height: 2.75rem; margin: 0; padding: 0 0.75rem;
                border: 1px solid var(--border); border-radius: 12px; box-shadow: none;
                background: var(--card-bg); color: var(--text);
                font-size: 0.9375rem; font-weight: 600; white-space: nowrap;
                touch-action: manipulation; -webkit-tap-highlight-color: transparent;
                user-select: none; -webkit-user-select: none;
                transition: transform 120ms cubic-bezier(0.23, 1, 0.32, 1), background-color 120ms ease;
            }
            .sheet-act:hover { background: var(--card-bg); color: var(--text); border-color: var(--border); transform: none; box-shadow: none; }
            .sheet-act:active { transform: scale(0.97); background: var(--gray-bg); }
            .sheet-act .ui-ico { width: 1.15rem; height: 1.15rem; flex: none; color: var(--accent); vertical-align: 0; }
            .sheet-act > span:not(.sheet-act-dot) { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
            .sheet-act-dot { position: absolute; top: 0.45rem; right: 0.55rem; width: 0.55rem; height: 0.55rem;
                border-radius: 50%; background: var(--red); }
            .sheet-act-dot[hidden] { display: none; }
            html:has([data-sup-sheet-dot]:not([hidden])) .tabbar .tabbar-more .tabbar-ico { position: relative; }
            html:has([data-sup-sheet-dot]:not([hidden])) .tabbar .tabbar-more .tabbar-ico::after {
                content: ""; position: absolute; top: -0.1rem; right: -0.2rem; width: 0.55rem; height: 0.55rem;
                border-radius: 50%; background: var(--red); box-shadow: 0 0 0 2px var(--card-bg);
            }
        }
        @media (max-width: 900px) and (prefers-reduced-motion: reduce) {
            .sheet-act { transition: none; }
            .sheet-act:active { transform: none; }
        }

        
        .set-nav-switch { display: none; }
        @media (max-width: 900px) {
            .set-nav:has(> .set-nav-switch) { display: flex; align-items: center; gap: 0.25rem; }
            .set-nav:has(> .set-nav-switch) > .set-nav-groups { display: none; }
            .set-nav:has(> .set-nav-switch) > :is(.set-nav-items, .plat-strip) { flex: 1 1 auto; min-width: 0; margin: 0; }
            .set-nav:has(> .set-nav-switch) > .set-nav-items,
            .set-nav:has(> .set-nav-switch) > .plat-strip > .set-nav-items { padding-left: 0.75rem; }
            
            .set-nav-switch {
                display: block; flex: none; position: relative; padding: 4px; border-radius: 12px;
                background: var(--tab-track); box-shadow: inset 0 1px 2px var(--tab-track-shade);
            }
            .set-nav-switch > summary {
                display: inline-flex; align-items: center; min-height: 2.5rem; padding: 0 0.85rem; border-radius: 9px;
                background: var(--tab-thumb); box-shadow: var(--tab-thumb-shadow), 0 0 0 1px var(--tab-thumb-ring);
                color: var(--text); font-size: 0.92rem; font-weight: 600; white-space: nowrap;
                -webkit-tap-highlight-color: transparent; touch-action: manipulation;
            }
            .set-nav-switch > summary:focus-visible { outline: 2px solid var(--tab-mark); outline-offset: -2px; }
            .set-nav-switch > summary::before { margin-right: 0.45rem; }
            .set-nav-switch .menu-panel { min-width: 13rem; }
            .set-nav-switch .menu-item.is-current { font-weight: 700; background: var(--accent-light); }
        }

        
        :root { --tap-min: 0px; }
        @media (pointer: coarse) {
            :root { --tap-min: 2.75rem; }
            button, .btn, summary.btn, input[type="submit"], input[type="button"], input[type="reset"],
            .btn-sm, form.inline button, .btn-nav, :where(.btn-secondary, .btn-ghost, .btn-text, .btn-danger-quiet) { min-height: 2.75rem; }
            .settings-tab, .set-nav-group, .set-nav-item, .set-nav-switch > summary, .menu-item, .walk-return-back,
            .crumbs > a, .seg > :is(a, button, label) {
                min-height: 2.75rem; align-content: center;
            }
            .walk-return-back { display: inline-block; padding: 0 0.5rem; }
            .topbar { padding-top: 0.45rem; padding-bottom: 0.45rem; }
            .topbar-icon-btn { width: 2.75rem; height: 2.75rem; }
            .topbar .station-chip .station-switch-pill { height: 2.75rem; }
            .topbar-chips { padding-top: 0.45rem; padding-bottom: 0.45rem; }
            .topbar-chips a { height: 2.75rem; }
            .week-stepper-arrow { width: 2.75rem; height: 2.75rem; }
            input[type="checkbox"]:not(.switch), input[type="radio"] { width: 1.25rem; height: 1.25rem; }
            .opt-group { --opt-box: 1.25rem; }
            label:has(> input[type="checkbox"], > input[type="radio"]) { min-height: 2.75rem; align-content: center; }
        }

        
        
        input[type="checkbox"].switch {
            -webkit-appearance: none !important; appearance: none !important; position: relative; flex: none;
            display: inline-block; vertical-align: middle; box-sizing: border-box;
            width: 2.5rem !important; height: 1.5rem !important; min-width: 2.5rem; min-height: 0 !important;
            margin: 0 0.5rem 0 0; padding: 0 !important;
            border-radius: 999px; cursor: pointer; box-shadow: none;
            background: color-mix(in srgb, var(--text-muted) 30%, var(--card-bg));
            border: 1px solid color-mix(in srgb, var(--text-muted) 42%, var(--card-bg));
            transition: background-color 0.15s ease, border-color 0.15s ease;
        }
        input[type="checkbox"].switch::before {
            content: ""; position: absolute; top: 2px; left: 2px;
            width: calc(1.5rem - 6px); height: calc(1.5rem - 6px); border-radius: 50%;
            background: #ffffff; box-shadow: 0 1px 2px rgba(15, 23, 42, 0.35);
            transition: transform 0.15s ease;
        }
        input[type="checkbox"].switch:focus { box-shadow: none; border-color: color-mix(in srgb, var(--text-muted) 42%, var(--card-bg)); }
        input[type="checkbox"].switch:checked,
        input[type="checkbox"].switch:checked:focus {
            background: var(--switch-on, var(--green)); border-color: var(--switch-on, var(--green));
        }
        input[type="checkbox"].switch:checked::before { transform: translateX(1rem); }
        input[type="checkbox"].switch:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
        input[type="checkbox"].switch:disabled { opacity: 0.5; cursor: not-allowed; }
        .switch-row {
            display: flex; align-items: center; gap: 0.6rem; margin: 0.35rem 0;
            font-weight: 600; text-transform: none; cursor: pointer;
        }
        .switch-row > input[type="checkbox"].switch { margin: 0; }
        @media (prefers-reduced-motion: reduce) {
            input[type="checkbox"].switch, input[type="checkbox"].switch::before { transition: none; }
        }

        .nav-chat-badge {
            background: var(--red); color: var(--red-on); border-radius: 999px; font-size: 0.75rem;
            padding: 0.05rem 0.4rem; margin-left: 0.35rem; font-weight: 700; vertical-align: middle;
        }
        /* The Messenger nav item - labeled + icon so it reads as a real
           destination, not a bare emoji. Icon tinted so it stands out; the
           unread badge floats to the right edge of the row. */
        .sidebar-nav a.nav-messenger { display: flex; align-items: center; gap: 0.45rem; }
        .nav-messenger-ico { font-size: 1.15rem; line-height: 1; }
        .sidebar-nav a.nav-messenger .nav-chat-badge { margin-left: auto; }

        /* ---- .tag: a value that carries a colour, set as a pill -------------
           WHY THIS EXISTS. The repair-ticket tables painted every meaningful
           cell as a saturated block - severity, status, EFR, days down, and
           seven more columns tinted by van. Forty rows of that is a quilt: the
           one grounded van reads at exactly the same weight as the forty
           beside it, and colour stops meaning anything.

           A .tag keeps the same hues (muscle memory is worth more than a new
           palette) and spends them differently: the cell keeps the table's own
           ground, and the VALUE wears the colour as a tint plus a border.

           THE HUE ARRIVES AS --tag, from Python, because which colour a status
           gets is a decision the app makes per DSP's own list - not something
           a stylesheet can know. Text stays var(--text) rather than the hue
           itself: this app has six themes, and #F5C518 on white is unreadable
           in three of them. The tint carries the meaning; the text stays
           legible everywhere.

           nowrap is not cosmetic. At the ticket table's column widths a
           one-word status was breaking across two lines mid-word, which is
           what made the whole page look broken rather than merely loud. The
           example is left unquoted on purpose: this file is included by every
           page in the app, so a status name written here is a literal on all
           of them - which is exactly how it first broke a test that asserts
           one of those words is absent from an unrelated page. */
        .tag {
            display: inline-flex; align-items: center; gap: 0.3em;
            padding: 0.14rem 0.5rem; border-radius: 999px;
            font-size: 0.78rem; font-weight: 600; line-height: 1.35;
            white-space: nowrap; max-width: 100%;
            color: var(--text);
            background: color-mix(in srgb, var(--tag, var(--text-muted)) 16%, transparent);
            border: 1px solid color-mix(in srgb, var(--tag, var(--text-muted)) 38%, transparent);
        }
        /* A tag with nothing to say is not a tag. Keeps an empty EFR cell from
           rendering an empty pill. */
        .tag:empty { display: none; }
        /* Loud enough to find, quiet enough to sit in a table. The dot is for
           the one state that has to be findable while scrolling. */
        .tag-strong {
            background: color-mix(in srgb, var(--tag) 26%, transparent);
            border-color: color-mix(in srgb, var(--tag) 60%, transparent);
        }
        .tag::before {
            content: ""; width: 0.42em; height: 0.42em; border-radius: 50%;
            background: var(--tag, var(--text-muted)); flex: 0 0 auto;
        }
        .tag-nodot::before { display: none; }
        
        .tag.is-wrap { white-space: normal; align-items: flex-start; border-radius: 0.75em; }
        .tag.is-wrap::before { margin-top: calc(0.675em - 0.21em); }

        /* ---- "Working...": a sync in flight, said on the page it feeds -----
           "[The vendor calendars] need a working.... so people know it's
           working." (Not quoted by name: this comment is rendered on every
           page, and a nav label in it is found before the nav.) A line of
           muted 12px text was the whole answer
           before, and it read as the page being idle. A spinner and a sentence,
           where the page's own status lives; _sync_run_js keeps the seconds
           counting and reloads when the run lands. */
        .sync-working {
            display: flex; align-items: center; gap: 0.7rem;
            padding: 0.65rem 0.9rem; margin: 0 0 1rem;
            border: 1px solid color-mix(in srgb, var(--accent) 45%, var(--border));
            border-radius: var(--radius);
            background: color-mix(in srgb, var(--accent) 9%, var(--card-bg));
            color: var(--text);
        }
        .sync-working strong { display: block; font-size: 0.94rem; }
        .sync-working-sub { display: block; font-size: 0.82rem; color: var(--text-muted); }
        .sync-working > .sync-spinner { --s: 1.25rem; }

        @media (max-width: 600px) {
            main { padding: 1.25rem 1rem 3rem; }
            h1 { font-size: 1.3rem; }
            .card { padding: 1rem; }
            .toolbar { flex-direction: column; align-items: stretch; }
            .toolbar > * { width: 100%; }
            /* A TOOLBAR OF A FEW SHORT ACTIONS, two to a row. The stack above
               gave each button its own line at its own width - a wrapper at
               full width with a pill of whatever size inside it - so five
               actions were a ragged column taller than the screen. The page's
               one primary action keeps a row to itself. */
            .toolbar.toolbar-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0.5rem; }
            .toolbar.toolbar-grid > * { width: auto; min-width: 0; margin: 0 !important; }
            .toolbar.toolbar-grid > :is(a:where(:not(.btn)), form, details) { display: block; }
            .toolbar.toolbar-grid > :has(> .btn-primary) { grid-column: 1 / -1; }
            .toolbar.toolbar-grid > :where(:not(.seg)) > :is(button, summary.btn) {
                width: 100%; display: flex; align-items: center; justify-content: center;
                text-align: center; padding-left: 0.5rem; padding-right: 0.5rem;
            }
            
            .toolbar.toolbar-grid > .seg { grid-column: 1 / -1; justify-self: start; }
        }
        /* One height and one shape across a grid toolbar, at every width: a
           quiet .btn-plain beside a pill beside a square read as three kinds
           of control when they are one kind. Colour still says which is which.

           THE BAR'S OWN CONTROLS ONLY - a button one level inside a cell, never
           the items inside an open menu or a group of smaller buttons (Repair
           Tickets' Sync strip). A plain descendant selector made "Show All
           Columns" in the Columns menu a full-width pill.

           The shape is the button's own rectangle since One Look ("one shape
           for every button"); this only evens the height. */
        .toolbar.toolbar-grid > :where(:not(.seg)) > :is(button, summary.btn) { min-height: 2.5rem; border-radius: var(--radius-sm); }

        /* ================= Professional's components =================
           The structure that makes Graphite more than a palette (its tokens are
           up with the other themes'). LAST IN THE SHEET ON PURPOSE.

           HOW THESE ARE WEIGHTED, because it is the thing to get right. A rule
           that restates one of the shell's GENERIC defaults (button, th, table)
           is wrapped in :where(), so it weighs exactly what the default does
           and wins only by coming after it - every page's own, more specific
           rule still beats it. A rule aimed at one named component keeps the
           full :root[data-theme] weight, so it beats that component's own. */

        /* The amber glows the shell paints under buttons are Light's brand.
           Mixed off graphite here. */
        :where(:root[data-theme="professional"]) button:hover,
        :where(:root[data-theme="professional"]) .btn:hover {
            box-shadow: 0 2px 6px color-mix(in srgb, var(--accent) 22%, transparent);
        }
        :where(:root[data-theme="professional"]) .btn-primary,
        :where(:root[data-theme="professional"]) .btn-primary:active,
        :where(:root[data-theme="professional"]) form.inline button.btn-primary {
            box-shadow: 0 1px 2px color-mix(in srgb, var(--accent) 30%, transparent);
        }
        :where(:root[data-theme="professional"]) .btn-primary:hover {
            box-shadow: 0 6px 14px -6px color-mix(in srgb, var(--accent) 65%, transparent);
        }
        /* Plain buttons are white keys with a hairline and a small lift. The
           accent is graphite, so the shell's solid flip on hover would turn
           every button black; hover is a grey wash instead. The named kinds
           (primary, danger, gold, the inline pairs) keep their own rules. */
        :where(:root[data-theme="professional"]) button:where(:not(.btn-primary, .btn-gold, .btn-danger, .btn-success, .btn-plain)),
        :where(:root[data-theme="professional"]) .btn:where(:not(.btn-primary, .btn-gold, .btn-danger, .btn-success, .btn-plain)) {
            background: var(--card-bg); border-color: #C3C9CF; color: var(--text);
            box-shadow: 0 1px 2px rgba(20, 24, 28, 0.08);
        }
        :where(:root[data-theme="professional"]) button:where(:not(.btn-primary, .btn-gold, .btn-danger, .btn-success, .btn-plain)):hover,
        :where(:root[data-theme="professional"]) .btn:where(:not(.btn-primary, .btn-gold, .btn-danger, .btn-success, .btn-plain)):hover {
            background: var(--gray-bg); border-color: #A9B0B7; color: var(--text);
            box-shadow: 0 1px 3px rgba(20, 24, 28, 0.12);
        }

        /* The tertiary tiers keep no edge and no lift here either: the same
           weight as the white keys just above, later, so they win. */
        :where(:root[data-theme="professional"]) :is(.btn-ghost, .btn-text, .btn-danger-quiet) {
            background: transparent; border-color: transparent; box-shadow: none;
        }
        :where(:root[data-theme="professional"]) .btn-danger-quiet { color: var(--text-muted); }
        :where(:root[data-theme="professional"]) :is(.btn-ghost, .btn-text):hover:where(:not(:disabled)) {
            background: color-mix(in srgb, var(--text) 7%, transparent); border-color: transparent; box-shadow: none;
        }
        :where(:root[data-theme="professional"]) .btn-danger-quiet:is(:hover, :focus-visible):where(:not(:disabled)) {
            background: var(--red-bg); border-color: transparent; color: var(--red); box-shadow: none;
        }
        /* Cards are lifted, not outlined, and the figures row is a card. The
           coloured edge a dashboard card keeps on its left stays: it carries
           a colour, and so does a card drawn as an error message. */
        :root[data-theme="professional"] .card:not(.error) { border-color: transparent; }
        :root[data-theme="professional"] .figs { border-color: transparent; }
        :root[data-theme="professional"] .dashboard-card { border-top-color: transparent; border-right-color: transparent; border-bottom-color: transparent; }
        :where(:root[data-theme="professional"]) table { border-color: transparent; }
        :root[data-theme="professional"] .loadout-table thead tr { background: var(--zebra); }
        :root[data-theme="professional"] h1 { font-weight: 700; letter-spacing: -0.01em; }

        /* With graphite links, the line under it is what says "link" in
           running text. */
        :root[data-theme="professional"] main :is(p, li, td) > a:not([class]) {
            text-decoration: underline; text-underline-offset: 0.18em;
            text-decoration-color: color-mix(in srgb, var(--accent) 40%, transparent);
        }
        /* Messages: grey on white, your own a shade deeper. */
        :root[data-theme="professional"] .chat-msg { background: var(--gray-bg); }
        :root[data-theme="professional"] .chat-msg.me { background: #DDE2E7; }
        /* The page tabs need nothing of their own here: the raised tab is a
           white key on a grey track already, and every graphite mark on them
           (the keyboard ring, a group's icon, the line under a second row's
           page) reads --tab-mark, which this theme sets to its accent. */

        /* THE LIGHT SIDEBAR. Every rule here is one the shell wrote for a dark
           rail: a white mark, white washes and scrollbars, black overflow
           shadows. Desktop only - on a phone the nav is the More sheet, which
           is already light. The rail stands on the ground, so it has no edge
           of its own; the panel beside it is white and keeps its rule, and the
           top bar keeps its bottom one. */
        @media (min-width: 901px) {
            :root[data-theme="professional"] .sidebar {
                box-shadow: none; border-right-color: transparent;
                scrollbar-color: rgba(20, 24, 28, 0.2) transparent;
            }
            :root[data-theme="professional"][data-nav="top"] .sidebar { border-bottom-color: var(--sidebar-border); }
            :root[data-theme="professional"] .sidebar:hover::-webkit-scrollbar-thumb {
                background: rgba(20, 24, 28, 0.18); background-clip: content-box;
            }
            :where(:root[data-theme="professional"]) .sidebar-nav a:hover { background: var(--sidebar-bg-hover); }
            :root[data-theme="professional"] .sidebar.nav-more-above .sidebar-icon-actions {
                box-shadow: 0 10px 10px -10px rgba(20, 24, 28, 0.22);
            }
            :root[data-theme="professional"] .sidebar.nav-more-below .sidebar-user {
                box-shadow: 0 -10px 10px -10px rgba(20, 24, 28, 0.22);
            }
            /* The mark is drawn white for a dark rail; its ink cut is the one
               the light top bars already use. */
            :root[data-theme="professional"] .sidebar-brand-icon.brand-logo {
                content: url("/static/brand/mark-ink.webp");
            }
        }
        @media (max-width: 900px) {
            :root[data-theme="professional"] .topbar { box-shadow: 0 1px 0 var(--border); }
        }
        /* THE DSP'S OWN BRAND (app/tools/dsp_brand.py, templates/brand/). Every
           colour is a token: --dsp and --dsp-on come from _dsp_brand_style.html
           for the DSP being shown, and fall back to the app's accent. */
        .dsp-plate { width: 2.5rem; height: 2.5rem; border-radius: 10px; flex: none; object-fit: contain; }
        .dsp-plate.is-initials { display: inline-flex; align-items: center; justify-content: center;
            background: var(--dsp-band); color: var(--dsp-band-on);
            font-weight: 800; font-size: 0.85rem; letter-spacing: 0.02em; user-select: none; }
        .dsp-band { display: flex; align-items: center; gap: 0.75rem; min-height: 56px; box-sizing: border-box;
            margin: 0 0 1rem; padding: 0.6rem 1rem; border-radius: var(--radius);
            border: 1px solid color-mix(in srgb, var(--dsp) 22%, var(--border));
            border-top: 4px solid var(--dsp);
            background: color-mix(in srgb, var(--dsp) 8%, var(--card-bg)); }
        .dsp-band-name { min-width: 0; flex: 1; font-weight: 800; font-size: 1.08rem; line-height: 1.3; color: var(--text);
            white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
        .dsp-band.is-slim { min-height: 40px; padding: 0; border: 0; background: none; border-radius: 0; margin: 0 0 0.85rem; gap: 0.6rem; }
        .dsp-band.is-slim .dsp-plate { width: 2rem; height: 2rem; border-radius: 8px; }
        .dsp-band.is-slim .dsp-plate.is-initials { font-size: 0.75rem; }
        .dsp-band.is-slim .dsp-band-name { font-size: 1rem; font-weight: 700; }
        /* A WIDE LOGO (a wordmark) keeps its shape and stands for the name,
           which stays for screen readers. */
        .dsp-plate.is-wide, .dsp-band .dsp-plate.is-wide { width: auto; max-width: 12rem; border-radius: 0; }
        .dsp-band.is-wide .dsp-band-name { position: absolute; width: 1px; height: 1px; overflow: hidden;
            clip: rect(0 0 0 0); white-space: nowrap; }
        .topbar.has-dsp-stripe { box-shadow: inset 0 -3px 0 var(--dsp), var(--shadow); }
        .dsp-credit { display: flex; align-items: center; justify-content: center; gap: 0.4rem; min-height: 44px;
            margin: 1.5rem 0 0.25rem; font-size: 0.78rem; color: var(--text-muted); }
        .dsp-credit img { width: 14px; height: 14px; flex: none; }
        .dsp-credit b { font-weight: 600; color: var(--text); }
        .dsp-hero { background: var(--dsp-band); color: var(--dsp-band-on);
            padding: 2.2rem 1rem; padding-top: max(2.2rem, env(safe-area-inset-top, 0px));
            padding-left: max(1rem, env(safe-area-inset-left, 0px)); padding-right: max(1rem, env(safe-area-inset-right, 0px)); text-align: center; }
        .dsp-hero .dsp-plate { width: 72px; height: 72px; border-radius: 18px; box-shadow: 0 10px 26px -12px rgba(0, 0, 0, 0.55); }
        .dsp-hero .dsp-plate.is-wide { width: auto; max-width: min(18rem, 80vw); background: #FFFFFF; padding: 0.6rem 0.9rem;
            box-sizing: border-box; border-radius: 14px; }
        .dsp-hero .dsp-plate.is-initials { background: var(--dsp-band-on); color: var(--dsp-band); font-size: 1.5rem; }
        .dsp-hero h1 { color: inherit; margin: 0.8rem auto 0; font-size: 1.6rem; max-width: 40rem; overflow-wrap: anywhere; text-wrap: balance; }
        @media (max-width: 600px) { .dsp-hero h1 { font-size: 1.4rem; } }
    
        /* A VENDOR SYNC'S RAW FAILURE, for the platform team only. The plain
           sentence a DSP reads sits above it (vendor_error_line); this is the
           technical text under "Details (super admin)". It fades in over 180ms
           when opened and does nothing under reduced motion. */
        .vse-details { margin: 0.3rem 0 0; font-size: 0.78rem; color: var(--text-muted); }
        .vse-details > summary { cursor: pointer; font-weight: 600; width: fit-content; }
        .vse-raw { margin: 0.3rem 0 0; padding: 0.45rem 0.6rem; max-width: 100%; box-sizing: border-box;
            white-space: pre-wrap; overflow-wrap: anywhere; font-family: var(--font-mono, monospace);
            font-size: 0.75rem; line-height: 1.4; color: var(--text);
            background: var(--gray-bg, rgba(127, 127, 127, 0.1)); border: 1px solid var(--border);
            border-radius: var(--radius-sm, 6px); }
        @media (prefers-reduced-motion: no-preference) {
            .vse-details[open] > .vse-raw { animation: vse-in 180ms ease-out; }
        }
        @keyframes vse-in { from { opacity: 0; transform: translateY(-3px); } to { opacity: 1; transform: none; } }.kf-num {
    display: inline-flex; align-items: center; gap: 2px; box-sizing: border-box;
    width: max-content; max-width: 100%; min-height: 28px; padding: 2px; align-self: flex-start; justify-self: start;
    border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--input-bg);
    vertical-align: middle; transition: border-color 150ms ease, box-shadow 150ms ease;
}
.kf-num:focus-within { border-color: var(--accent); box-shadow: var(--accent-glow); }
.kf-num.kf-bad, .kf-num:has(> input:user-invalid) { border-color: var(--red); }
.kf-num > input.kf-num-in {
    flex: 1 1 auto; width: var(--kf-w, 3.5ch); min-width: 2.5ch; max-width: 100%; min-height: 0; height: 24px;
    margin: 0; padding: 0 2px; border: 0; border-radius: 0; background: transparent; box-shadow: none;
    text-align: center; font-size: 14px; font-weight: 600; font-variant-numeric: tabular-nums; color: var(--text);
    -moz-appearance: textfield; appearance: textfield;
}
.kf-num > input.kf-num-in::-webkit-outer-spin-button,
.kf-num > input.kf-num-in::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.kf-num > input.kf-num-in:focus { outline: none; border: 0; box-shadow: none; }
.kf-num > input.kf-num-in:disabled { background: transparent; }.kf-num > .kf-num-btn {
    position: relative; flex: none; display: inline-grid; place-items: center; width: 24px; height: 24px; min-height: 0;
    margin: 0; padding: 0; border: 0; border-radius: calc(var(--radius-sm) - 4px);
    background: transparent; color: var(--text-muted); box-shadow: none; cursor: pointer;
    transition: background-color 150ms ease, border-color 150ms ease, color 150ms ease, transform 150ms ease;
}
.kf-num > .kf-num-btn:hover:not(:disabled) { background: transparent; color: var(--text); }
@media (hover: hover) and (pointer: fine) { .kf-num > .kf-num-btn:hover:not(:disabled) { background: var(--gray-bg); color: var(--text); } }
.kf-num > .kf-num-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.kf-num > .kf-num-btn:active:not(:disabled) { transform: scale(0.92); background: var(--gray-bg); box-shadow: none; }
.kf-num > .kf-num-btn:disabled { opacity: 0.4; cursor: not-allowed; background: transparent; }
.kf-num > .kf-num-btn svg { width: 12px; height: 12px; }
.kf-num > :is(.kf-num-pre, .kf-num-suf) { flex: none; font-size: 13px; color: var(--text-muted); font-weight: 500; font-variant-numeric: tabular-nums; }
.kf-num > .kf-num-pre { padding-left: 4px; margin-right: -3px; }
.kf-num > .kf-num-suf { padding-right: 2px; margin-left: -3px; }.kf-num.kf-num-sm { min-height: 26px; padding: 1px; }
.kf-num.kf-num-sm > .kf-num-btn { width: 22px; height: 22px; }
.kf-num.kf-num-sm > input.kf-num-in { height: 22px; }
.kf-num-disabled { opacity: 0.6; }
.kf-hint[hidden] { display: none; }
.kf-hint { display: block; margin: 0.3rem 0 0; font-size: 0.78rem; line-height: 1.4; color: var(--text-muted); font-weight: 400; }
.kf-bad + .kf-hint, .kf-num:has(> input:user-invalid) + .kf-hint { color: var(--red); }:where(input, select, textarea):where(:user-invalid, [aria-invalid="true"]) { border-color: var(--red); }
:where(.field:has(:is(input, select, textarea):is(:user-invalid, [aria-invalid="true"]))) .field-hint { color: var(--red); }textarea.kf-grow { resize: vertical; overflow-y: hidden; }
textarea.kf-grow.kf-at-max { overflow-y: auto; }
.kf-ta { position: relative; display: block; min-width: 0; }
.kf-ta > textarea { padding-bottom: 1.6rem; }
.kf-ta > .kf-count {
    position: absolute; right: 0.75rem; bottom: 0.45rem; pointer-events: none;
    font-size: 0.72rem; line-height: 1; color: var(--text-muted); font-variant-numeric: tabular-nums;
}
.kf-ta > .kf-count.kf-near { color: var(--orange); font-weight: 600; }
.kf-ta > .kf-count.kf-full { color: var(--red); font-weight: 600; }input.kf-dp-native, input.kf-tf-native, input.kf-otp-native { display: none !important; }
/* THE DAY BEFORE THE CALENDAR ARRIVES. enhance.js loads at the foot of the
   page, so a long page painted the browser's own "11/03/2026" box for a
   moment or two before the kit's trigger replaced it ("why does this appear
   for a couple seconds ... when I refresh the page - loadout", 2026-11-03). A
   date field with data-dp-short is hidden until then, and a .kf-dp-pre the
   server wrote ("Tue, Nov 3") stands in; enhanceDate removes it. */
input[type="date"][data-dp-short]:not(.kf-dp-native) { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.kf-dp-pre { display: inline-flex; align-items: center; white-space: nowrap; font-weight: 700; }
.kf-dp-gone { display: none !important; }
.kf-dp-trig {
    display: inline-flex; align-items: center; gap: 0.5rem; box-sizing: border-box;
    max-width: 100%; min-height: 2.6rem; margin: 0; padding: 0 0.7rem;
    border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--input-bg);
    color: var(--text); font: inherit; font-size: 0.95rem; font-weight: 500; text-align: left; white-space: nowrap;
    box-shadow: none; cursor: pointer; vertical-align: middle;
}
.kf-dp-trig:hover { background: var(--input-bg); border-color: var(--text-muted); }
.kf-dp-trig:active { background: var(--input-bg); box-shadow: none; }
.kf-dp-trig:focus-visible, .kf-dp-trig[aria-expanded="true"] { outline: none; border-color: var(--accent); box-shadow: var(--accent-glow); }
.kf-dp-trig.kf-bad { border-color: var(--red); }
.kf-dp-trig > svg { flex: none; width: 1rem; height: 1rem; color: var(--text-muted); }
.kf-dp-trig > .kf-dp-txt { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; font-variant-numeric: tabular-nums; }
.kf-dp-trig.kf-empty > .kf-dp-txt { color: var(--text-muted); }
.kf-dp-trig:disabled { opacity: 0.6; cursor: not-allowed; }
.kf-dp-range-on { min-width: 0; }.kf-range { display: contents; }.kf-dp {
    position: fixed; z-index: 1200; display: flex; box-sizing: border-box; max-width: calc(100vw - 32px);
    background: var(--card-bg); color: var(--text); border: 1px solid var(--border);
    border-radius: 16px; box-shadow: var(--shadow-lift); font-size: 0.875rem;
    transform-origin: top left;
}
.kf-dp[hidden] { display: none; }
.kf-dp-quick { display: flex; flex-direction: column; gap: 2px; padding: 10px; border-right: 1px solid var(--border); min-width: 8rem; }
.kf-dp .kf-dp-quick > button {
    height: 1.9rem; min-height: 0; margin: 0; padding: 0 0.65rem; border: 0; border-radius: 9px;
    background: transparent; color: var(--text); font-size: 0.85rem; font-weight: 500; text-align: left; white-space: nowrap; box-shadow: none;
}
.kf-dp .kf-dp-quick > button:hover { background: transparent; border: 0; color: var(--text); }
@media (hover: hover) and (pointer: fine) { .kf-dp .kf-dp-quick > button:hover { background: var(--gray-bg); color: var(--text); } }
.kf-dp .kf-dp-quick > button[aria-pressed="true"] { background: var(--accent-light); color: var(--accent-dark); }
.kf-dp-main { display: flex; flex-direction: column; min-width: 0; }
.kf-dp-months { display: flex; gap: 20px; padding: 14px 16px 6px; }
.kf-dp-m { width: 238px; }
.kf-dp-mh { position: relative; display: flex; align-items: center; justify-content: center; height: 2rem; margin-bottom: 6px; font-weight: 600; }
.kf-dp .kf-dp-mh > button {
    position: absolute; top: 0; display: inline-grid; place-items: center; width: 2rem; height: 2rem; min-height: 0;
    margin: 0; padding: 0; border: 0; border-radius: 999px; background: transparent; color: var(--text-muted); box-shadow: none;
}
.kf-dp .kf-dp-mh > button:hover { background: transparent; border: 0; color: var(--text-muted); }
@media (hover: hover) and (pointer: fine) { .kf-dp .kf-dp-mh > button:hover { background: var(--gray-bg); color: var(--text); } }
.kf-dp .kf-dp-mh > button svg { width: 1rem; height: 1rem; }
.kf-dp-mh > .kf-dp-prev { left: 0; }
.kf-dp-mh > .kf-dp-next { right: 0; }
.kf-dp-grid { display: grid; grid-template-columns: repeat(7, 34px); row-gap: 2px; }
.kf-dp-grid > [role="row"] { display: contents; }
.kf-dp-grid [role="columnheader"] { height: 1.75rem; display: grid; place-items: center; font-size: 0.72rem; font-weight: 500; color: var(--text-muted); }
.kf-dp-c { position: relative; height: 34px; display: grid; place-items: center; }
.kf-dp-c.kf-band { background: var(--accent-light); }
/* Week calendar: the whole row lights under the pointer (rows are display: contents; :hover still reaches them). */
@media (hover: hover) and (pointer: fine) { .kf-dp-wk .kf-dp-grid > [role="row"]:hover .kf-dp-c:has(.kf-dp-d:not(:disabled)) { background: var(--accent-light); } }
.kf-dp-c.kf-band.kf-rs { border-radius: 999px 0 0 999px; }
.kf-dp-c.kf-band.kf-re { border-radius: 0 999px 999px 0; }
.kf-dp-c.kf-band.kf-rs.kf-re { border-radius: 999px; }
.kf-dp-c.kf-band.kf-wk0:not(.kf-rs) { border-top-left-radius: 10px; border-bottom-left-radius: 10px; }
.kf-dp-c.kf-band.kf-wk6:not(.kf-re) { border-top-right-radius: 10px; border-bottom-right-radius: 10px; }
.kf-dp .kf-dp-d {
    width: 34px; height: 34px; min-height: 0; margin: 0; padding: 0; border: 0; border-radius: 999px;
    background: transparent; color: var(--text); box-shadow: none;
    font-size: 0.8rem; font-weight: 500; font-variant-numeric: tabular-nums;
}
.kf-dp .kf-dp-d:hover:not(:disabled) { background: transparent; border: 0; color: var(--text); }
@media (hover: hover) and (pointer: fine) { .kf-dp .kf-dp-d:hover:not(:disabled) { background: var(--gray-bg); color: var(--text); } }
.kf-dp .kf-dp-d.kf-out { color: var(--text-muted); opacity: 0.55; }
.kf-dp .kf-dp-d.kf-today { box-shadow: inset 0 0 0 1.5px var(--accent); }
.kf-dp .kf-dp-d.kf-end, .kf-dp .kf-dp-d.kf-end:hover { background: var(--accent-gradient); color: var(--accent-gradient-on); font-weight: 700; }
.kf-dp .kf-dp-d:disabled { background: transparent; color: var(--text-muted); opacity: 0.35; cursor: not-allowed; text-decoration: line-through; }
.kf-dp .kf-dp-d:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.kf-dp-foot { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 10px 16px 14px; border-top: 1px solid var(--border); }
.kf-dp .kf-dp-foot > input {
    flex: 1 1 11rem; width: auto; min-width: 0; height: 2rem; margin: 0; padding: 0 0.75rem;
    border-radius: 999px; font-size: 0.85rem; font-variant-numeric: tabular-nums;
}
.kf-dp .kf-dp-foot > .btn-sm { margin: 0; }
.kf-dp-scrim { position: fixed; inset: 0; z-index: 1199; background: rgba(15, 23, 42, 0.42); }
.kf-dp-grab { width: 40px; height: 4px; border-radius: 999px; background: var(--border); margin: 8px auto 0; flex: none; }
.kf-vh { position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }.kf-dp.kf-sheet {
    left: 0; right: 0; bottom: 0; top: auto; max-width: none; max-height: 90vh; max-height: 90dvh; overflow: auto; overscroll-behavior: contain;
    flex-direction: column; border-radius: 24px 24px 0 0; border-bottom: 0;
    padding-bottom: env(safe-area-inset-bottom, 0px); transform-origin: bottom center;
}
.kf-dp.kf-sheet .kf-dp-quick {
    flex-direction: row; overflow-x: auto; border-right: 0; border-bottom: 1px solid var(--border);
    padding: 12px 16px; gap: 6px; scrollbar-width: none; min-width: 0;
}
.kf-dp.kf-sheet .kf-dp-quick::-webkit-scrollbar { display: none; }
.kf-dp.kf-sheet .kf-dp-quick > button { flex: none; height: 2.25rem; border: 1px solid var(--border); border-radius: 999px; }
.kf-dp.kf-sheet .kf-dp-months { justify-content: center; }
.kf-dp.kf-sheet .kf-dp-m { width: 100%; max-width: 360px; }
.kf-dp.kf-sheet .kf-dp-grid { grid-template-columns: repeat(7, minmax(40px, 1fr)); }
.kf-dp.kf-sheet .kf-dp-c { height: 42px; }
.kf-dp.kf-sheet .kf-dp-d { width: 40px; height: 40px; font-size: 0.9rem; }
.kf-dp.kf-sheet .kf-dp-foot > input { font-size: 16px; height: 2.5rem; }
.kf-dp.kf-sheet .kf-dp-foot > .btn-sm { min-height: 2.5rem; }.kf-time {
    display: inline-flex; align-items: center; gap: 1px; box-sizing: border-box; max-width: 100%; align-self: flex-start; justify-self: start;
    min-height: 2.6rem; padding: 0 0.6rem; border: 1px solid var(--border); border-radius: var(--radius-sm);
    background: var(--input-bg); color: var(--text-muted); vertical-align: middle; cursor: text;
    transition: border-color 150ms ease, box-shadow 150ms ease;
}
.kf-time:focus-within { border-color: var(--accent); box-shadow: var(--accent-glow); }
.kf-time.kf-bad { border-color: var(--red); }
.kf-time > svg { flex: none; width: 1rem; height: 1rem; margin-right: 0.4rem; }
.kf-ts {
    display: inline-grid; place-items: center; min-width: 1.5rem; height: 1.75rem; padding: 0 0.2rem;
    border-radius: 6px; color: var(--text); font-weight: 500; font-variant-numeric: tabular-nums;
    outline: none; cursor: default; -webkit-user-select: none; user-select: none;
}
.kf-ts.kf-ts-empty { color: var(--text-muted); }
.kf-ts:focus { background: var(--accent-gradient); color: var(--accent-gradient-on); }
.kf-ts-c { color: var(--text-muted); padding: 0 1px; }
.kf-ts-ap { margin-left: 0.25rem; min-width: 1.9rem; }
.kf-time-disabled { opacity: 0.6; pointer-events: none; }.kf-dayseg {
    display: grid; grid-template-columns: repeat(var(--kf-n, 2), minmax(0, 1fr)) 40px; gap: 3px;
    box-sizing: border-box; width: 100%; max-width: 100%; min-width: 0; min-height: 40px; padding: 3px;
    border: 1px solid var(--border); border-radius: 12px; background: var(--zebra, var(--gray-bg));
}
.kf-dayseg > .kf-dayseg-b {
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1px;
    min-width: 0; min-height: 32px; margin: 0; padding: 0 6px; border: 0; border-radius: 9px;
    background: transparent; box-shadow: none; color: var(--text); font: inherit; font-size: 14px; font-weight: 500;
    line-height: 1.1; white-space: nowrap; cursor: pointer; -webkit-tap-highlight-color: transparent;
    transition: background-color 180ms ease, color 180ms ease, box-shadow 180ms ease;
}
.kf-dayseg > .kf-dayseg-b:hover { background: transparent; border: 0; color: var(--text); box-shadow: none; }
@media (hover: hover) and (pointer: fine) { .kf-dayseg > .kf-dayseg-b:hover:not(:disabled):not([aria-pressed="true"]) { background: color-mix(in srgb, var(--text) 6%, transparent); } }
.kf-dayseg > .kf-dayseg-b[aria-pressed="true"],
.kf-dayseg > .kf-dayseg-b[aria-pressed="true"]:hover {
    background: var(--card-bg); color: var(--accent-dark); font-weight: 600;
    box-shadow: 0 1px 2px rgba(16, 24, 40, 0.08), 0 0 0 1px var(--border);
}
.kf-dayseg > .kf-dayseg-b:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.kf-dayseg > .kf-dayseg-b:disabled { opacity: 0.45; cursor: not-allowed; }
.kf-dayseg-t { max-width: 100%; overflow: hidden; text-overflow: ellipsis; }
.kf-dayseg-d { max-width: 100%; overflow: hidden; text-overflow: ellipsis; font-size: 12px; font-weight: 400; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.kf-dayseg-b[aria-pressed="true"] .kf-dayseg-d { color: var(--accent-dark); }
.kf-dayseg-cal > svg { width: 16px; height: 16px; flex: none; }
.kf-dayseg-cal .kf-dp-txt { font-size: 12px; font-variant-numeric: tabular-nums; }
.kf-dayseg-cal.kf-on > svg, .kf-dayseg-cal:not(.kf-on) .kf-dp-txt { display: none; }.kf-tstep {
    display: grid; grid-template-columns: 36px minmax(0, 1fr) auto 36px; align-items: center; gap: 4px;
    box-sizing: border-box; width: 100%; max-width: 100%; min-width: 0; height: 44px; padding: 3px;
    border: 1px solid var(--line-strong, var(--border)); border-radius: 12px; background: var(--card-bg); color: var(--text);
    cursor: text; transition: border-color 150ms ease, box-shadow 150ms ease;
}
.kf-tstep:focus-within { border-color: var(--accent); box-shadow: var(--accent-glow); }
.kf-tstep.kf-bad { border-color: var(--red); }
.kf-tstep-hm { display: flex; align-items: center; justify-content: center; gap: 2px; min-width: 0; }
.kf-tstep .kf-tstep-in {
    flex: none; box-sizing: border-box; width: 2.4ch; height: 32px; min-height: 0; margin: 0; padding: 0;
    border: 0; border-radius: 6px; background: transparent; box-shadow: none; outline: none;
    color: var(--text); font: inherit; font-size: 17px; font-weight: 600; text-align: center; font-variant-numeric: tabular-nums;
    transition: background-color 150ms ease, color 150ms ease;
}
.kf-tstep .kf-tstep-in::placeholder { color: var(--text-muted); opacity: 1; }
.kf-tstep .kf-tstep-in:focus { border: 0; outline: none; box-shadow: none; background: var(--accent-light); color: var(--accent-dark); }
.kf-tstep .kf-tstep-in::selection { background: var(--accent); color: var(--accent-gradient-on); }
.kf-tstep-c { font-size: 17px; font-weight: 600; color: var(--text); }
.kf-tstep.kf-blank .kf-tstep-c { color: var(--text-muted); }
.kf-tstep .kf-tstep-btn {
    display: inline-grid; place-items: center; box-sizing: border-box; width: 36px; height: 36px; min-height: 0;
    margin: 0; padding: 0; border: 0; border-radius: 9px; background: var(--zebra, var(--gray-bg)); color: var(--text);
    box-shadow: none; cursor: pointer; -webkit-tap-highlight-color: transparent;
    transition: background-color 150ms ease, color 150ms ease, transform 150ms ease;
}
.kf-tstep .kf-tstep-btn:hover:not(:disabled) { background: var(--zebra, var(--gray-bg)); border: 0; color: var(--text); }
@media (hover: hover) and (pointer: fine) { .kf-tstep .kf-tstep-btn:hover:not(:disabled) { background: color-mix(in srgb, var(--text) 10%, var(--card-bg)); } }
.kf-tstep .kf-tstep-btn:active:not(:disabled) { transform: scale(0.92); box-shadow: none; }
.kf-tstep .kf-tstep-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.kf-tstep .kf-tstep-btn svg { width: 14px; height: 14px; }
.kf-tstep-ap {
    display: inline-flex; align-items: center; gap: 2px; box-sizing: border-box; height: 32px; padding: 2px;
    border-radius: 8px; background: var(--zebra, var(--gray-bg));
}
.kf-tstep .kf-tstep-ap > button {
    height: 28px; min-height: 0; margin: 0; padding: 0 8px; border: 0; border-radius: 6px;
    background: transparent; box-shadow: none; color: var(--text-muted); font: inherit; font-size: 12px; font-weight: 600;
    cursor: pointer; -webkit-tap-highlight-color: transparent;
    transition: background-color 180ms ease, color 180ms ease, box-shadow 180ms ease;
}
.kf-tstep .kf-tstep-ap > button:hover { background: transparent; border: 0; color: var(--text); box-shadow: none; }
.kf-tstep .kf-tstep-ap > button[aria-pressed="true"],
.kf-tstep .kf-tstep-ap > button[aria-pressed="true"]:hover {
    background: var(--card-bg); color: var(--text); box-shadow: 0 1px 2px rgba(16, 24, 40, 0.1), 0 0 0 1px var(--border);
}
.kf-tstep .kf-tstep-ap > button:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }@media (max-width: 600px), (pointer: coarse) {
    .kf-dayseg { grid-template-columns: repeat(var(--kf-n, 2), minmax(0, 1fr)) 44px; gap: 2px; padding: 1px; }
    .kf-dayseg > .kf-dayseg-b { min-height: 44px; border-radius: 10px; }
    .kf-tstep { grid-template-columns: 44px minmax(0, 1fr) auto 44px; gap: 2px; height: 48px; padding: 1px; }
    .kf-tstep .kf-tstep-btn { width: 44px; height: 44px; border-radius: 10px; }
    .kf-tstep .kf-tstep-in { height: 44px; font-size: 16px; }
    .kf-tstep-c { font-size: 16px; }
    .kf-tstep-ap { height: 44px; padding: 0; }
    .kf-tstep .kf-tstep-ap > button { height: 44px; padding: 0 9px; border-radius: 9px; }
}.kf-otp { display: flex; gap: 0.5rem; margin: 0.25rem 0 0; }
.kf-otp > input.kf-otp-box {
    flex: none; width: 2.75rem; height: 3.25rem; min-height: 0; margin: 0; padding: 0;
    border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--input-bg);
    text-align: center; font-size: 1.25rem; font-weight: 600; font-variant-numeric: tabular-nums;
    color: var(--text); caret-color: var(--accent);
}
.kf-otp > input.kf-otp-box:focus { outline: none; border-color: var(--accent); box-shadow: var(--accent-glow); }
.kf-otp > input.kf-otp-box.kf-filled { background: var(--card-bg); }
.kf-otp.kf-bad > input.kf-otp-box { border-color: var(--red); }
.kf-otp-alt { display: inline-block; align-self: flex-start; justify-self: start; margin: 0.5rem 0 0; padding: 0; min-height: 0; border: 0; background: none; box-shadow: none;
    color: var(--accent); font-size: 0.85rem; font-weight: 600; text-decoration: underline; text-underline-offset: 2px; cursor: pointer; }
.kf-otp-alt:hover { background: none; border: 0; color: var(--accent); }
@media (max-width: 380px) {
    .kf-otp { gap: 0.375rem; }
    .kf-otp > input.kf-otp-box { width: 2.5rem; height: 3rem; }
}.kf-tags { display: flex; flex-wrap: wrap; gap: 6px; min-width: 0; margin: 0; padding: 0; border: 0; }
.kf-tags > legend { padding: 0; margin: 0 0 0.35rem; font-size: 0.78rem; font-weight: 600; color: var(--text-muted); }
.kf-tags > :is(label, a, button, .kf-tag) {
    position: relative; display: inline-flex; align-items: center; gap: 0.35rem; box-sizing: border-box;max-width: 100%; height: auto; min-height: 2rem; margin: 0; padding: 0.3rem 0.8rem; border: 1px solid var(--border); border-radius: 1rem;
    background: var(--gray-bg); color: var(--text); font-size: 0.85rem; font-weight: 500; line-height: 1.25;
    text-align: left; overflow-wrap: anywhere; text-decoration: none; cursor: pointer; box-shadow: none;
    transition: background-color 150ms ease, border-color 150ms ease, color 150ms ease, transform 150ms ease;
}
.kf-tags > :is(label, a, button):hover { background: var(--gray-bg); border-color: var(--border); color: var(--text); }
@media (hover: hover) and (pointer: fine) { .kf-tags > :is(label, a, button):hover { background: color-mix(in srgb, var(--text) 8%, var(--gray-bg)); border-color: var(--border); color: var(--text); } }
.kf-tags > :is(label, a, button):active { transform: scale(0.96); box-shadow: none; }
.kf-tags > label > input:is([type="checkbox"], [type="radio"]) {
    position: absolute; width: 1px; height: 1px; margin: 0; padding: 0; clip-path: inset(50%); pointer-events: none;
}
.kf-tags > label:has(> input:checked),
.kf-tags > :is(a, button):is([aria-current]:not([aria-current="false"]), [aria-pressed="true"], .is-on),
.kf-tags > :is(label, a, button):has(> input:checked):hover,
.kf-tags > :is(a, button):is([aria-current]:not([aria-current="false"]), [aria-pressed="true"], .is-on):hover {
    background: var(--accent-gradient); border-color: transparent; color: var(--accent-gradient-on);
}
.kf-tags > label:has(> input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }
.kf-tags > label:has(> input:disabled) { opacity: 0.55; cursor: not-allowed; }
.kf-tags .kf-n { font-variant-numeric: tabular-nums; opacity: 0.75; font-size: 0.78rem; }.kf-tag { display: inline-flex; align-items: center; gap: 0.35rem; height: 1.75rem; padding: 0 0.65rem; border-radius: 999px;
    background: var(--gray-bg); color: var(--text); font-size: 0.8rem; font-weight: 600; white-space: nowrap; }
.kf-tag.kf-tone-red { background: var(--red-bg); color: var(--red); }
.kf-tag.kf-tone-green { background: var(--green-bg); color: var(--green); }
.kf-tag.kf-tone-amber { background: var(--orange-bg); color: var(--orange); }
.kf-tag.kf-tone-blue { background: var(--blue-bg); color: var(--accent); }
.kf-tag:has(> .kf-tag-x) { padding-right: 0.2rem; }
.kf-tag > .kf-tag-x {
    display: inline-grid; place-items: center; width: 1.375rem; height: 1.375rem; min-height: 0; margin: 0; padding: 0;
    border: 0; border-radius: 999px; background: transparent; color: inherit; box-shadow: none; cursor: pointer;
}
.kf-tag > .kf-tag-x:hover { background: transparent; border: 0; color: inherit; }
@media (hover: hover) and (pointer: fine) { .kf-tag > .kf-tag-x:hover { background: color-mix(in srgb, currentColor 16%, transparent); border: 0; color: inherit; } }
.kf-tag > .kf-tag-x svg { width: 0.75rem; height: 0.75rem; stroke-width: 2.6; }input.kf-cp-native { display: none !important; }
.kf-cp-trig {
    display: inline-flex; align-items: center; gap: 0.5rem; box-sizing: border-box; min-height: 2.6rem; margin: 0;
    padding: 0 0.7rem 0 0.35rem; border: 1px solid var(--border); border-radius: var(--radius-sm); align-self: flex-start; justify-self: start;
    background: var(--input-bg); color: var(--text); font: inherit; font-size: 0.9rem; box-shadow: none; cursor: pointer; vertical-align: middle;
}
.kf-cp-trig:hover { background: var(--input-bg); border-color: var(--text-muted); }
.kf-cp-trig:focus-visible, .kf-cp-trig[aria-expanded="true"] { outline: none; border-color: var(--accent); box-shadow: var(--accent-glow); }
.kf-cp-trig:disabled { opacity: 0.55; cursor: not-allowed; }
.kf-cp-sw { flex: none; width: 1.9rem; height: 1.9rem; border-radius: 8px; box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.14); }
.kf-cp-hex { font-family: var(--font-mono); font-size: 0.82rem; font-variant-numeric: tabular-nums; }
.kf-cp-trig.kf-cp-compact { padding: 0.3rem; }
.kf-cp-trig.kf-cp-compact > .kf-cp-hex { display: none; }
.kf-dp.kf-cp { display: grid; grid-template-columns: 240px 168px; gap: 16px; padding: 14px; }
.kf-dp.kf-cp.kf-sheet { grid-template-columns: minmax(0, 1fr); padding: 0 16px 16px; }
.kf-dp.kf-cp.kf-sheet > .kf-dp-grab { margin-bottom: 4px; }
.kf-cp-l { display: flex; flex-direction: column; gap: 12px; min-width: 0; }
.kf-cp-sq {
    position: relative; width: 100%; height: 180px; border-radius: 12px; cursor: crosshair; touch-action: none;
    background: linear-gradient(to top, #000, rgba(0, 0, 0, 0)), linear-gradient(to right, #fff, hsl(var(--kf-h, 0) 100% 50%));
}
.kf-cp-sq:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.kf-cp-th { position: absolute; width: 18px; height: 18px; border-radius: 50%; border: 3px solid #fff; box-sizing: border-box;
    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.25), 0 2px 6px rgba(0, 0, 0, 0.3); transform: translate(-50%, -50%); background: var(--kf-cur); pointer-events: none; }
.kf-dp input.kf-cp-hue {
    -webkit-appearance: none; appearance: none; display: block; width: 100%; height: 14px; min-height: 0; margin: 0; padding: 0; border: 0; border-radius: 999px;
    background: linear-gradient(to right, #f00, #ff0, #0f0, #0ff, #00f, #f0f, #f00); cursor: pointer; box-shadow: none;
}
.kf-dp input.kf-cp-hue::-webkit-slider-thumb { -webkit-appearance: none; width: 20px; height: 20px; border-radius: 50%; background: #fff; box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.25), 0 2px 6px rgba(0, 0, 0, 0.3); }
.kf-dp input.kf-cp-hue::-moz-range-thumb { width: 20px; height: 20px; border: 0; border-radius: 50%; background: #fff; box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.25), 0 2px 6px rgba(0, 0, 0, 0.3); }
.kf-dp input.kf-cp-hue:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.kf-cp-row { display: flex; gap: 8px; align-items: center; }
.kf-cp-sw2 { flex: none; width: 2.6rem; height: 2.6rem; border-radius: var(--radius-sm); background: var(--kf-cur); box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.12); }
.kf-cp-hexbox { flex: 1; display: flex; align-items: center; gap: 4px; min-width: 0; height: 2.6rem; padding: 0 0.6rem; box-sizing: border-box;
    border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--input-bg); color: var(--text-muted); font-family: var(--font-mono); }
.kf-cp-hexbox:focus-within { border-color: var(--accent); box-shadow: var(--accent-glow); }
.kf-dp .kf-cp-hexbox > input { flex: 1; min-width: 0; height: 100%; margin: 0; padding: 0; border: 0; background: transparent; box-shadow: none;
    color: var(--text); font-family: var(--font-mono); text-transform: uppercase; }
.kf-dp .kf-cp-hexbox > input:focus { outline: none; box-shadow: none; }
.kf-cp-bad { margin: -6px 0 0; color: var(--red); }
.kf-cp-presets { display: flex; gap: 8px; flex-wrap: wrap; }
.kf-dp .kf-cp-dot { width: 1.625rem; height: 1.625rem; min-height: 0; margin: 0; padding: 0; border: 0; border-radius: 50%;
    background: var(--c); box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.14); cursor: pointer; transition: transform 150ms ease; }
.kf-dp .kf-cp-dot:hover { background: var(--c); border: 0; }
.kf-dp .kf-cp-dot[aria-pressed="true"] { box-shadow: 0 0 0 2px var(--card-bg), 0 0 0 4px var(--c); }
.kf-dp .kf-cp-dot:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.kf-cp-prev { display: flex; flex-direction: column; align-self: start; overflow: hidden; border-radius: 12px; background: var(--gray-bg); }
.kf-cp-prev-s { height: 84px; display: grid; place-items: center; background: var(--kf-cur); }
.kf-cp-prev-s > span { max-width: calc(100% - 16px); padding: 6px 12px; border-radius: 10px; background: var(--kf-cur); color: var(--kf-cur-ink);
    font-weight: 600; font-size: 0.85rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--kf-cur-ink) 25%, transparent); }
.kf-cp-vals { margin: 0; padding: 10px 12px; font-family: var(--font-mono); font-size: 0.72rem; display: grid; grid-template-columns: auto 1fr; gap: 5px 10px; }
.kf-cp-vals dt { color: var(--text-muted); font-weight: 400; }
.kf-cp-vals dd { margin: 0; }
.kf-cp-ink { margin: 0; padding: 0 12px 10px; font-size: 0.75rem; color: var(--text-muted); }
.kf-cp-acts { display: flex; gap: 6px; justify-content: flex-end; padding: 0 10px 10px; }
.kf-cp-acts > .btn-sm { margin: 0; }:is(.kf-num-btn, .kf-dp-d, .kf-dp-quick > button, .kf-dp-mh > button, .kf-tags > *, .kf-cp-dot, .kf-otp-box, .kf-ts, .kf-dp-trig, .kf-cp-trig) {
    touch-action: manipulation; -webkit-tap-highlight-color: transparent;
}:is(.kf-num-btn, .kf-dp-d, .kf-dp-quick > button, .kf-dp-mh > button, .kf-tags > *, .kf-cp-dot, .kf-dp-trig, .kf-cp-trig) {
    -webkit-user-select: none; user-select: none; -webkit-touch-callout: none;
}@media (pointer: coarse) {
    .kf-num > input.kf-num-in, .kf-dp .kf-dp-foot > input { font-size: 16px; }
    .kf-num > .kf-num-btn::after { content: ""; position: absolute; inset: -10px; }
    .kf-num.kf-num-sm > .kf-num-btn::after { inset: -11px; }
    .kf-dp-trig { min-height: 2.75rem; font-size: 16px; }
    .kf-tags > :is(label, a, button) { min-height: 2.5rem; }
    .kf-dp .kf-cp-hexbox > input { font-size: 16px; }
}.hr8 { display: inline-flex; align-items: center; gap: 0.25rem; padding: 0 0.32rem; border: 1px solid var(--border);
    border-radius: 999px; background: transparent; color: var(--text-muted); font-size: 0.68rem; font-weight: 600;
    line-height: 1.35; white-space: nowrap; vertical-align: baseline; font-variant-numeric: tabular-nums; }
.hr8[hidden] { display: none; }
.hr8.is-odd::before { content: ""; flex: none; width: 0.4rem; height: 0.4rem; border-radius: 50%;
    background: color-mix(in srgb, var(--gold) 80%, transparent); }@media (prefers-reduced-motion: reduce) {
    .kf-num, .kf-num > .kf-num-btn, .kf-time, .kf-dp-trig, .kf-tags > *, .kf-cp-dot { transition: none !important; }
    .kf-tstep, .kf-tstep .kf-tstep-btn, .kf-tstep .kf-tstep-in, .kf-tstep .kf-tstep-ap > button, .kf-dayseg > .kf-dayseg-b { transition: none !important; }
    .kf-num > .kf-num-btn:active:not(:disabled), .kf-tags > :active, .kf-cp-dot:active { transform: none !important; }
    .kf-tstep .kf-tstep-btn:active:not(:disabled) { transform: none !important; }
}html.as-on [data-autosave-save], html:not(.as-on) [data-autosave-only] { display: none !important; }
.as-say { display: inline-flex; align-items: center; gap: 0.25rem; margin-left: 0.5rem; vertical-align: middle;
    font-size: 0.78rem; font-weight: 600; line-height: 1.3; color: var(--green); white-space: nowrap;
    opacity: 0; transform: translateY(2px); pointer-events: none;
    transition: opacity 200ms cubic-bezier(0.23, 1, 0.32, 1), transform 200ms cubic-bezier(0.23, 1, 0.32, 1); }
.as-say:empty { display: none; }
[data-autosave-say].as-say { margin-left: 0; }
.as-say .ui-ico { width: 0.85rem; height: 0.85rem; flex: none; }
.as-say.is-on { opacity: 1; transform: none; }
.as-say.is-bad { color: var(--red); white-space: normal; pointer-events: auto; max-width: 28rem; }
:is(input, select, textarea).as-bad { border-color: var(--red); box-shadow: 0 0 0 1px var(--red); }
input.switch.as-bad { box-shadow: 0 0 0 2px var(--red); }
@media (prefers-reduced-motion: reduce) {
    .as-say { transition: none; transform: none; }
}
