/* ============================================================
   MAINLINE HUB — DESIGN TOKENS v0.3 (dark-first, themeable)
   THE source of truth for all Mainline Hub surfaces — since
   v0.81 every page <link>s this file and none inline-copies the
   :root block (the old CLAUDE.md gotcha is retired; the guard is
   tools/check_colour_tokens.py).
   Derived from Mainline brand: mainlinefire.com, Cinespace
   proposal, invoice/quote templates, arc badge + MF monogram.
   Theme switch: <html data-theme="light"> flips the palette
   (App Settings, v0.81). No attribute = dark, always.
   ============================================================ */
:root{
  /* Ink — surfaces (dark theme) */
  --ink-0:#121010;             /* app background */
  --ink-1:#1A1817;             /* cards, list rows */
  --ink-2:#232020;             /* elevated: sheets, popovers */
  --ink-3:#2C2928;             /* hover / pressed */
  --hairline:rgba(255,255,255,.08);
  --hairline-strong:rgba(255,255,255,.14);

  /* Text */
  --text-1:#F5F2EF;            /* primary — warm white */
  --text-2:#A8A29D;            /* secondary */
  --text-3:#8D8781;            /* tertiary / placeholder */

  /* Brand */
  --flame:#D95B39;             /* primary accent — interactive ONLY, never status */
  --flame-hover:#E86F4C;
  --flame-pressed:#C04E2F;
  --flame-subtle:rgba(217,91,57,.14);
  /* flame when it colours TEXT. Same hex as --flame in dark; light darkens it
     one step for AA while fills keep the true brand orange with white text
     (mood board "The Mainline Look", approved 31 Aug 2026). Use this for any
     flame-tinted glyph or label; keep --flame for fills and borders. */
  --flame-text:#D95B39;
  --blue:#2E8FD8;              /* links, info, sync */
  --blue-subtle:rgba(46,143,216,.14);
  --shadow:rgba(0,0,0,.55);    /* popover/sheet drop shadows + overlay scrims */
  /* THE TRACK (v0.89.15, `.mh-seg` below): a recessed track, a raised thumb
     that glides to the chosen item, a soft light under the pointer. */
  --seg-track-top:#1C1918; --seg-track-bot:#141211;
  --seg-thumb-top:#36312F; --seg-thumb-bot:#272322;
  --seg-edge:rgba(255,255,255,.09);
  --seg-lift:rgba(0,0,0,.50);
  --seg-glow:rgba(255,255,255,.07);
  /* Sticky-bar glass: the translucent tint behind backdrop-filter:blur. Two
     tints because the bars sit on two grounds — page bars over --ink-0
     (home/admin/monthly/timeoff), the report bar over --ink-1 (topbar.css).
     Tokenised in v0.81 PR 2 so the bars re-theme with the switch. */
  --bar-tint:rgba(18,16,16,.94);
  --bar-tint-raised:rgba(26,24,23,.86);
  /* Native-widget scheme (date pickers, scrollbars) for pages that opt in via
     color-scheme:var(--scheme,dark) — flips with the theme. */
  --scheme:dark;

  /* Status — reserved for inspection state, never decorative */
  --pass:#4CC38A;  --pass-subtle:rgba(76,195,138,.14);
  --fail:#E5484D;  --fail-subtle:rgba(229,72,77,.15);
  --warn:#F2A33C;  --warn-subtle:rgba(242,163,60,.14);
  /* The middle of a THREE-STEP severity scale (v0.81.4, item 15). Minor /
     Moderate / Severe and Good / Fair / Poor are graded answers, and rendering
     every step of them in one grey said nothing at all. Inside the status
     family on purpose — these ARE findings about a building — and named for
     where it sits rather than for its hue: worse than --warn, not yet --fail.
     Chosen well clear of --flame (#D95B39) so a severity pill never reads as a
     brand accent. */
  --warn-strong:#EE8034; --warn-strong-subtle:rgba(238,128,52,.15);
  --na:#8A8580;    --na-subtle:rgba(138,133,128,.14);
  /* State — WORKFLOW, not inspection (rule 10, settled 14 Aug 2026; --state-active
     reverted to orange 25 Aug 2026, Aaron).
     "This report is in progress" is administrative: it is not a finding about a
     building, and it must never borrow a status colour. It is also deliberately
     QUIETER than status — amber got borrowed in the first place because somebody
     wanted in-progress to stand out, and paperwork does not compete with a
     building. Indigo is unused elsewhere in the palette and reads as "system"
     rather than "danger" — but technicians already read orange as "in progress"
     from every version before rule 10, and that standing habit outweighs the
     colour-reuse argument. --state-active goes back to the same amber as --warn
     on purpose: the two-things-on-one-screen problem rule 10 describes (Impairment
     is also amber) returns with it, and that trade is deliberate, not missed.
     --state-pending / --state-done keep the workflow family — this reverts ONE
     token's VALUE, not the rule that gave paperwork its own family.
     An OVERDUE obligation is the one exception and keeps --fail: a missed
     inspection is a live compliance failure, not a paperwork state. */
  --state-active:#F2A33C;  --state-active-subtle:rgba(242,163,60,.14);
  --state-pending:#8D8781; --state-pending-subtle:rgba(141,135,129,.12);
  --state-done:#A8A29D;    --state-done-subtle:rgba(168,162,157,.10);

  /* WHO — the per-person chip colour (Improvements 0.82 #7). The HUE is the
     person's, computed from their name (usermenu.js window.userHue) and set
     inline as --uh; these two carry the theme's share so the same hue stays
     legible on both grounds: pages paint hsl(var(--uh) var(--who-sat)
     var(--who-lit)). Deliberately softer than the status family — a person
     tag must never outshout a pass/fail. */
  --who-sat:45%; --who-lit:70%;

  /* CADENCE — the colour of an INTERVAL, never of a state (0.85 Part 5, Rob:
     "when viewing the list of upcoming inspections, let's differentiate the
     intervals — annual is different than monthly"). Identity hues on the
     same soft saturation as a person's tag, so a cadence rail can never be
     read as a pass, a fail or a warning: violet annual, teal semi-annual,
     gold quarterly, blue monthly, rose weekly, grey for the rest. Pages
     paint hsl(var(--cad-<x>) var(--who-sat) var(--who-lit)). */
  --cad-annual:268; --cad-semi:178; --cad-quarterly:40; --cad-monthly:212;
  --cad-weekly:335; --cad-other:0;
  --cad-other-sat:0%;

  /* MODULE MARK (v0.86.0): the Tasks module's identity hue, for its user-menu
     icon and its app mark only, never a state. Hue only, painted on the
     person-tag saturation/lightness like --cad-*, so it re-themes for free. */
  --tasks-h:172;

  /* SPRINKLER COMPONENTS (v0.87.0): the kind of a component (shut-off valve,
     inspector's test valve, auxiliary drain) and the water a main valve holds
     (wet, dry). IDENTITY hues on the person-tag saturation/lightness like
     --cad-*, never a status — red stays a Critical finding and nothing else,
     which is why the test valve is violet. Each kind also has its own SHAPE,
     so none of this is read by colour alone. */
  --sp-shut:190; --sp-itv:278; --sp-drain:146; --sp-wet:214; --sp-dry:38;

  /* STATUS BAR — how far the sticky top bar (or property's page) must sit
     below the top edge (Improvements 0.82 Part 12 #50E). 0 everywhere except
     an iOS/iPadOS home-screen install, where it is the real inset, with the
     24px floor ONLY when usermenu.js has measured the viewport as running
     under the status bar (html[data-statusbar="under"]) — iPadOS reports the
     inset as 0 there. Every page consumes var(--status-top); none carries the
     max(env(),24px) rule of its own any more, which put 24px of nothing above
     the bar on every window that was NOT under the status bar. */
  --status-top:0px;

  /* Type */
  --font-ui:"PP Neue Montreal",-apple-system,BlinkMacSystemFont,"SF Pro Text",Inter,sans-serif;
  --font-mono:"IBM Plex Mono",ui-monospace,"SF Mono",Menlo,monospace;

  /* Shape */
  --r-sm:6px; --r-md:10px; --r-lg:14px; --r-xl:20px;

  /* Space — 4pt grid */
  --space-1:4px; --space-2:8px; --space-3:12px; --space-4:16px;
  --space-5:24px; --space-6:32px; --space-7:48px; --space-8:64px;

  /* UTILITY GROUND (0.84 Part 4, the Section Editor). The two cards on the
     annual that are NOT inspection work — Sections and Deficiencies — and the
     Sections sub-page in Headquarters sit a step BELOW the page instead of
     raised above it, with a fine diagonal hatch drawn from --util-line. The
     hatch is deliberately faint (Aaron: "nice texture, make it lower
     opacity"); it says "surface, not form" and must never compete with a
     row. Sub-report cards stay --ink-1. */
  --util:#0E0C0C;
  --util-line:rgba(255,255,255,.035);

  /* Touch */
  --touch-min:44px;            /* Apple HIG floor */
  --touch-field:48px;          /* preferred for P/F/NA inspection taps */
  /* THE GAP BETWEEN ADJACENT ANSWER CONTROLS. One token because there were
     three values for one thing: .opts 5px, .taps 6px, .bigseg 8px — Yes/No/N-A
     pills, device Pass/Fail/N-A taps and the severity segment, all the same
     gesture at three different spacings, and the tightest of them sat under a
     44px target being tapped with a glove on. 8px is the widest of the three,
     which is the right direction to converge: a mis-tap on a regulatory answer
     costs more than eight pixels of width. */
  --gap-answer:8px;

  /* v0.86.4 design-audit tokens. --scrim is the sheet backdrop (it used to be
     --shadow on seven surfaces, .20 in light, and a fixed black .55-.66 on
     fifteen); --focus the keyboard ring; --progress every ring and bar (they
     were flame everywhere but the Staff Inbox, which was --pass); --paper the
     white a stored signature (ink #111) sits on; --pass-hover the hover of a
     pass-filled control (was filter:brightness); --disabled-alpha the one
     disabled opacity. */
  --scrim:rgba(0,0,0,.6);
  --focus:#D95B39;
  --progress:#D95B39;
  --paper:#FFFFFF; --paper-ink:#111111;
  --pass-hover:#5BD69A;
  --disabled-alpha:.4;
  /* text on an ALWAYS-DARK surface whatever the theme (the log book's building
     card): the dark palette's --text-2, 7:1 on #131313 (design audit COL-10). */
  --text-on-dark:#A8A29D;

  /* Z LADDER (0.84 audit). One order for every page: a toast fired over a
     modal used to land behind or in front purely by DOM order (five pages
     had both at 60), and the offline chip sat BELOW the shared dialog. */
  --z-sticky:39;     /* sub-bars, filter toolbars */
  --z-bar:40;        /* the sticky top bar */
  --z-float:45;      /* the floating account chip */
  --z-popover:60;    /* menus, search results, popovers */
  --z-modal:70;      /* page-owned modals and sheets */
  --z-toast:90;      /* transient messages (MH_NET chips, toasts) */
  --z-menu:95;       /* the account menu + App Settings */
  --z-dialog:100;    /* dialogs.js */
  --z-lightbox:110;  /* a photo, over everything */

  /* TYPE SCALE (0.84 audit). Twenty-one sizes and nine half-pixel steps were
     in use; these are the steps that survive Large text's 1.14 zoom cleanly.
     New code takes a step; existing rules move onto them as they are touched. */
  --fs-micro:10px;   /* mono chip text */
  --fs-kicker:11px;  /* mono labels, kickers */
  --fs-note:13px;    /* footnotes, hints, secondary rows */
  --fs-body:14px;    /* body, buttons */
  --fs-lead:16px;    /* inputs (the iOS floor), row titles */
  --fs-title:17px;   /* sheet and card titles */
  --fs-h2:20px;
  --fs-h1:24px;}

/* ── LIGHT THEME — brand-tuned to mainlinefire.com (mood board "The Mainline
   Look", approved 31 Aug 2026; palette spec = "App Settings & Headquarters
   v0.81 — MOCKUP.html"). A re-tuning, not an inversion: the site's barely-warm
   off-white ground with PURE WHITE cards, brand soot for ink, TRUE flame
   #DA5B38 on fills (white text, how the site itself uses it) with the darkened
   cut reserved for flame-as-text, and the site's dusk-slate secondary standing
   in for azure. Status hues are AA-deepened — the site has no pass/fail
   vocabulary, so accessibility governs those. Rule 10's meaning survives the
   flip because meaning lives in the token, not the hex.
   Values NOT in the mockup, derived here on the mockup's own logic:
   --flame-pressed one ramp step past hover; --na and --state-done follow the
   grey families they follow in dark (state-pending/text-2). ─────────────── */
[data-theme="light"]{
  --ink-0:#F7F6F4; --ink-1:#FFFFFF; --ink-2:#EFEDEA; --ink-3:#E3E0DB;
  --hairline:rgba(19,19,19,.10); --hairline-strong:rgba(19,19,19,.17);
  --text-1:#131313; --text-2:#4A4541; --text-3:#6E6862;
  --flame:#DA5B38; --flame-hover:#C24C2B; --flame-pressed:#AA4224;
  --flame-subtle:rgba(218,91,56,.10);
  --flame-text:#C24C2B;
  --blue:#43708E; --blue-subtle:rgba(67,112,142,.11);
  --pass:#177A50; --pass-subtle:rgba(23,122,80,.11);
  --fail:#C2373D; --fail-subtle:rgba(194,55,61,.10);
  --warn:#9A6210; --warn-subtle:rgba(154,98,16,.12);
  --warn-strong:#B4551A; --warn-strong-subtle:rgba(180,85,26,.12);
  --na:#6E6862;   --na-subtle:rgba(110,104,98,.12);
  --state-active:#9A6210;  --state-active-subtle:rgba(154,98,16,.12);
  --state-pending:#6E6862; --state-pending-subtle:rgba(110,104,98,.10);
  --state-done:#4A4541;    --state-done-subtle:rgba(74,69,65,.10);
  --shadow:rgba(19,19,19,.20);
  --seg-track-top:#E9E6E2; --seg-track-bot:#EFEDEA;
  --seg-thumb-top:#FFFFFF; --seg-thumb-bot:#FAF9F7;
  --seg-edge:rgba(255,255,255,.95);
  --seg-lift:rgba(19,19,19,.14);
  --seg-glow:rgba(255,255,255,.75);
  --bar-tint:rgba(247,246,244,.94);
  --bar-tint-raised:rgba(255,255,255,.86);
  --who-sat:50%; --who-lit:36%;   /* per-person hue, deepened for light ground */
  --cad-annual:268; --cad-semi:178; --cad-quarterly:40; --cad-monthly:212;
  --cad-weekly:335; --cad-other:0; --cad-other-sat:0%;
  --util:#F1EFEB; --util-line:rgba(19,19,19,.045);
  --scrim:rgba(19,19,19,.45);
  --focus:#DA5B38; --progress:#DA5B38; --pass-hover:#146A45;
  --scheme:light;
}

/* ── TEXT SIZE — the one-line root scale (App Settings, v0.81). The app's px
   sizes all scale together; no attribute = Normal. 1.14 is the mockup's
   approved step. `zoom` over font-size because the pages speak px, and a
   rem refactor across ten pages is exactly the hand-edit sprawl this file
   exists to prevent. ─────────────────────────────────────────────────── */
html[data-text="large"]{zoom:1.14}

/* ── THE FONT FACES — ONE declaration, here, for every page (v0.86.4, design
   audit TYP-01 / AST-05). Seventeen files used to carry their own @font-face
   copy in three incompatible variants, so a `font-weight:500` on PP Neue
   Montreal rendered from the Bold file on eight pages and from Regular on
   nine, and five pages (home, admin, property, monthly, set-password) declared
   no IBM Plex Mono Bold at all, so the account chip's badge and HQ's Overview
   tiles were browser-synthesised faux bold there. This block is the one
   source; tools/check_colour_tokens.py fails on any other @font-face. woff2
   first (about half the bytes), the otf/ttf as the fallback.
   Only Regular and Bold of PP Neue Montreal are shipped: Bold answers 500-700
   (the annual's, HQ's and the property page's long-standing look) until the
   Medium file lands, when 500 becomes a real Medium. */
@font-face{font-family:"PP Neue Montreal";src:url(/static/fonts/PPNeueMontreal-Regular.woff2) format("woff2"),url(/static/fonts/PPNeueMontreal-Regular.otf) format("opentype");font-weight:400;font-style:normal;font-display:swap}
@font-face{font-family:"PP Neue Montreal";src:url(/static/fonts/PPNeueMontreal-Bold.woff2) format("woff2"),url(/static/fonts/PPNeueMontreal-Bold.otf) format("opentype");font-weight:500 700;font-style:normal;font-display:swap}
@font-face{font-family:"IBM Plex Mono";src:url(/static/fonts/IBMPlexMono-Medium.woff2) format("woff2"),url(/static/fonts/IBMPlexMono-Medium.ttf) format("truetype");font-weight:400 500;font-style:normal;font-display:swap}
@font-face{font-family:"IBM Plex Mono";src:url(/static/fonts/IBMPlexMono-Bold.woff2) format("woff2"),url(/static/fonts/IBMPlexMono-Bold.ttf) format("truetype");font-weight:600 700;font-style:normal;font-display:swap}

/* Native widgets (date / time pickers, the calendar glyph, scrollbars) follow
   the theme on EVERY page (design audit COL-07): eleven pages declared no
   color-scheme and drew light pickers on dark inputs. --scheme flips with the
   theme above. */
html{color-scheme:var(--scheme,dark)}

/* ONE keyboard focus ring for every page (design audit BTN-01): twelve pages
   had no :focus-visible rule for a button at all, and the pages that did used
   ten recipes. A page's own input :focus ring (border + shadow) still wins on
   inputs, which is fine; this is the floor, not the ceiling. */
:is(button,a,summary,[role="button"],[tabindex]):focus-visible{outline:2px solid var(--focus);outline-offset:2px}

/* ONE disabled look (design audit BTN-05): ten opacities and three cursors
   were in use for one state. Pages may still override; this is the default. */
button:disabled,[aria-disabled="true"]{opacity:var(--disabled-alpha);cursor:not-allowed}

/* 0.85 Part 6 #7 (Rob): NO SPINNER ARROWS on any number box, on any page.
   "Nobody is going to tap 30 times to reach 30 seconds" — the pad types the
   number; the arrows only steal width and invite the thirty taps. Chrome
   draws them, WebKit on iPad does not, so this is the desktop office's view
   of the same control. Once, here, for every page (the tokens.css rule). */
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}
input[type="number"]{-moz-appearance:textfield;appearance:textfield}

/* `hidden` must hide, whatever display a class gave the element — the trap
   topbar.css and guide.css each fell into on their own. One rule, every page. */
[hidden]{display:none!important}

/* THE TRACK (v0.89.15, Aaron 23 Oct 2026): ONE control for choosing where you
   are, wherever the app offers it. Three levels, and only these:
     1. an app's own SECTIONS (HQ's top row): `.mh-seg.bar` inside
        `.mh-seg-scroll`, spanning the content column like the cards under it;
     2. a thing's own SUB-PAGES (HQ's property sub-nav, Time's Requests /
        Attendance, the property page, the Handbook): the flame UNDERLINE,
        not this;
     3. a VIEW of the same list (Tasks' Day / Week / Month / Quarter, Unresolved
        / Resolved, This inspection / Prior, Sections / Breakers, the Inbox's
        To sign / Signed): `.mh-seg`, compact.
   ANSWERS are never this (Pass / Fail / N/A, Yes / No, a setting's On / Off):
   they carry status colour, and a view switch must not look like a finding.
   The chosen item is the child button with `.on`; usermenu.js `mhSeg` adds
   the thumb and moves it, so a page only writes the class. Corners are the
   cards' family, concentric: track --r-lg, the 4px inset, thumb --r-md. */
.mh-seg{position:relative;isolation:isolate;display:inline-grid;grid-auto-flow:column;grid-auto-columns:1fr;
  gap:2px;padding:3px;border-radius:var(--r-lg);border:1px solid var(--hairline);
  background:linear-gradient(180deg,var(--seg-track-top),var(--seg-track-bot));
  box-shadow:inset 0 1px 2px var(--seg-lift),0 1px 0 var(--seg-edge)}
.mh-seg.fill{display:grid;width:100%}
.mh-seg::before{content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;z-index:0;
  background:radial-gradient(140px circle at var(--mx,50%) 50%,var(--seg-glow),transparent 70%);
  opacity:0;transition:opacity .25s ease}
@media(hover:hover){.mh-seg:hover::before{opacity:1}}
.mh-seg>.mh-seg-glide{position:absolute;top:3px;bottom:3px;left:0;width:0;border-radius:var(--r-md);
  z-index:0;pointer-events:none;
  background:linear-gradient(180deg,var(--seg-thumb-top),var(--seg-thumb-bot));
  box-shadow:inset 0 1px 0 var(--seg-edge),0 2px 8px var(--seg-lift),0 0 0 1px var(--hairline);
  opacity:0;transition:transform .42s cubic-bezier(.3,1.25,.45,1),width .42s cubic-bezier(.3,1.25,.45,1),opacity .2s}
.mh-seg>.mh-seg-glide.ready{opacity:1}
.mh-seg>.mh-seg-glide.still{transition:none}
.mh-seg>button{position:relative;z-index:1;min-width:max-content;min-height:40px;margin:0;padding:0 14px;
  border:0;border-radius:var(--r-md);background:transparent;box-shadow:none;color:var(--text-2);
  font:inherit;font-size:13px;font-weight:600;letter-spacing:0;text-transform:none;
  display:inline-flex;align-items:center;justify-content:center;gap:6px;white-space:nowrap;cursor:pointer;
  transition:color .2s ease,transform .12s ease}
/* 40px drawn, 44px to the finger (the row shortcut boxes' collar). */
.mh-seg>button::after{content:"";position:absolute;inset:-2px 0}
@media(hover:hover){.mh-seg>button:hover:not(:disabled){color:var(--text-1)}}
.mh-seg>button:active:not(:disabled){transform:scale(.97)}
.mh-seg>button.on{color:var(--text-1)}
.mh-seg>button .n{font-family:var(--font-mono);font-size:11px;font-weight:500;color:var(--text-3)}
.mh-seg>button.on .n{color:var(--text-2)}
/* Level 1: the app's sections. Full height, tabs share the column. */
.mh-seg.bar{display:flex;flex:1 0 auto;padding:4px}
.mh-seg.bar>.mh-seg-glide{top:4px;bottom:4px}
.mh-seg.bar>button{flex:1 1 0;min-height:var(--touch-min);padding:0 18px;font-size:13.5px}
.mh-seg.bar>button::after{content:none}
.mh-seg.bar.solo{flex:0 0 auto}
/* Wider than its column (a tablet, a phone): the scroller fades the edge that
   hides an item, and `mhSeg` brings the chosen one into view. */
.mh-seg-scroll{display:flex;overflow-x:auto;scrollbar-width:none}
.mh-seg-scroll::-webkit-scrollbar{display:none}
.mh-seg-scroll.fl{-webkit-mask-image:linear-gradient(90deg,transparent,#000 32px);mask-image:linear-gradient(90deg,transparent,#000 32px)}
.mh-seg-scroll.fr{-webkit-mask-image:linear-gradient(270deg,transparent,#000 32px);mask-image:linear-gradient(270deg,transparent,#000 32px)}
.mh-seg-scroll.fl.fr{-webkit-mask-image:linear-gradient(90deg,transparent,#000 32px,#000 calc(100% - 32px),transparent);
  mask-image:linear-gradient(90deg,transparent,#000 32px,#000 calc(100% - 32px),transparent)}

/* Nothing moves for a viewer who asked for reduced motion — once, for every
   page, instead of a per-file block that half the files forgot. */
@media (prefers-reduced-motion: reduce){
  *,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}
}

/* Part 12 #50E — see --status-top in :root. -webkit-touch-callout support =
   iOS/iPadOS only (Android insets its standalone windows itself). */
@supports (-webkit-touch-callout: none){
  @media (display-mode: standalone){
    :root{--status-top:env(safe-area-inset-top,0px)}
    html[data-statusbar="under"]{--status-top:max(env(safe-area-inset-top,0px),24px)}
  }
}
