/* Convertus MES: the one stylesheet. No build step, no framework.

   Layout, components, class names, spacing, breakpoints and states follow
   PULSE_BLUEPRINT.md section 5. Colours, typeface, logo and footer follow
   section 11, which wins wherever the two disagree.

   Adapted from Pulse EHS app/static/css/style.css @ aa321d3: the shell
   (rail, appbar, .content as the only scroller, the 720px phone drawer),
   panels, forms styled by exclusion, buttons, tables, pills, flashes and
   the sign-in card. Changed on the way in:
     - every colour is a token, and every hex value lives in :root only
       (Pulse EHS had about 190 raw colours outside its token blocks);
     - --accent-soft has a real value (Pulse EHS's default scheme pointed
       it at itself, so focus rings and quiet-button hovers drew nothing);
     - font sizes, spacing, radii and sizes are tokens too;
     - no selector is declared twice (Pulse EHS had .prose and .plain-list
       twice, the second silently overriding the first);
     - one scheme, "convertus", replaces the four Pulse EHS schemes;
     - a skip link, aria-current on the active nav item, visible
       :focus-visible on buttons and links, and a disabled button state.

   Rules that are easy to break:
     - .content has NO max-width. Lists and tables span the window.
       Forms wrap in .form-page, which caps at --form-max and centres.
     - .app is height:100vh, not min-height, or .content cannot scroll.
     - Never write a colour, font size or spacing value below :root.
       Add a token. tests/css_lint.py fails the build on a hex outside
       :root, on a var() that is not defined, and on a token that refers
       to itself.
     - Media queries cannot read custom properties, so the one phone
       breakpoint, 720px, is written as a number in each @media rule. */

/* ---------- Roboto, self-hosted (blueprint 11(b) rule 9) ----------
   Files and their source: app/static/fonts/roboto/SOURCE.txt.
   unicode-range values copied from @fontsource/roboto 5.3.0. */
@font-face {
    font-family: "Roboto"; font-style: normal; font-weight: 400; font-display: swap;
    src: url("../fonts/roboto/roboto-latin-400-normal.woff2") format("woff2");
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: "Roboto"; font-style: normal; font-weight: 400; font-display: swap;
    src: url("../fonts/roboto/roboto-latin-ext-400-normal.woff2") format("woff2");
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
    font-family: "Roboto"; font-style: normal; font-weight: 500; font-display: swap;
    src: url("../fonts/roboto/roboto-latin-500-normal.woff2") format("woff2");
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: "Roboto"; font-style: normal; font-weight: 500; font-display: swap;
    src: url("../fonts/roboto/roboto-latin-ext-500-normal.woff2") format("woff2");
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
    font-family: "Roboto"; font-style: normal; font-weight: 700; font-display: swap;
    src: url("../fonts/roboto/roboto-latin-700-normal.woff2") format("woff2");
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: "Roboto"; font-style: normal; font-weight: 700; font-display: swap;
    src: url("../fonts/roboto/roboto-latin-ext-700-normal.woff2") format("woff2");
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ---------- Manrope, self-hosted, for the menu only ----------
   Chris, 16 Oct 2026: menu items in Manrope (as Pulse EHS), Roboto for
   everything else and as its fallback. Files and source:
   app/static/fonts/manrope/SOURCE.txt; unicode-range as Roboto above. */
@font-face {
    font-family: "Manrope"; font-style: normal; font-weight: 400; font-display: swap;
    src: url("../fonts/manrope/manrope-latin-400-normal.woff2") format("woff2");
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: "Manrope"; font-style: normal; font-weight: 400; font-display: swap;
    src: url("../fonts/manrope/manrope-latin-ext-400-normal.woff2") format("woff2");
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
    font-family: "Manrope"; font-style: normal; font-weight: 500; font-display: swap;
    src: url("../fonts/manrope/manrope-latin-500-normal.woff2") format("woff2");
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: "Manrope"; font-style: normal; font-weight: 500; font-display: swap;
    src: url("../fonts/manrope/manrope-latin-ext-500-normal.woff2") format("woff2");
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
    font-family: "Manrope"; font-style: normal; font-weight: 600; font-display: swap;
    src: url("../fonts/manrope/manrope-latin-600-normal.woff2") format("woff2");
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: "Manrope"; font-style: normal; font-weight: 600; font-display: swap;
    src: url("../fonts/manrope/manrope-latin-ext-600-normal.woff2") format("woff2");
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
    font-family: "Manrope"; font-style: normal; font-weight: 700; font-display: swap;
    src: url("../fonts/manrope/manrope-latin-700-normal.woff2") format("woff2");
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: "Manrope"; font-style: normal; font-weight: 700; font-display: swap;
    src: url("../fonts/manrope/manrope-latin-ext-700-normal.woff2") format("woff2");
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ======================================================================
   TOKENS. The only place a colour, size or spacing value is written.
   ====================================================================== */
:root {
    /* --- Convertus palette, as the brand book names it (11(a)) ---
       Blue #4, Lightest Blue #5, Green #3, Green #4 and Lightest Green #5
       fail contrast on white: never use them for text on a light
       background (11(b) rule 6). They feed the colour bar, rail markers
       and decoration only. */
    --cv-blue-1: #1C2D5A;          /* Darkest Blue #1, Pantone 295 */
    --cv-navy: #172750;            /* the navy of the supplied logo's wordmark, a touch darker than Darkest Blue #1;
                                      Chris chose it for the menu, sign-in and headings (16 Oct 2026, blueprint 11(c)) */
    --cv-blue-2: #243C82;          /* Blue #2 */
    --cv-blue-3: #0074BC;          /* Blue #3 */
    --cv-blue-4: #14B1E6;          /* Blue #4: decoration only */
    --cv-blue-5: #44C8F5;          /* Lightest Blue #5: decoration, rail markers */
    --cv-green-1: #006734;         /* Darkest Green #1: the one status green */
    --cv-green-2: #00944A;         /* Green #2: large text, icons, chart lines */
    --cv-green-3: #0DB14B;         /* Green #3: decoration and chart fills */
    --cv-green-4: #8CC63F;         /* Green #4: decoration and chart fills */
    --cv-green-5: #D7DF23;         /* Lightest Green #5: decoration only */
    --white: #FFFFFF;

    /* --- Surfaces, lines and text: section 5 neutrals (11(c)) ---
       --muted is the section 5 Boab value, not Classic #69757f: Classic
       measures 4.2:1 on --bg, under the 4.5 needed for the small footer
       and hint text that sits there. #5b6678 measures 5.1:1 on --bg. */
    --bg: #EEF1F4;
    --panel: var(--white);
    --line: #D6DCE2;
    --line-soft: #E7EBEF;
    --ink: #1F2933;
    --muted: #5B6678;
    --heading: var(--cv-navy);   /* page titles and headings (11(c)) */
    --hover: #F6F8FA;              /* button and option hover surface */
    --row-hover: #FAFCFD;          /* table row hover */
    --thead: #F5F7F9;              /* table header band */
    --row-off-bg: var(--hover);    /* retired table rows */

    /* --- Accent (11(c)) --- */
    --accent: var(--cv-blue-3);
    --accent-dark: var(--cv-blue-2);
    --accent-soft: rgba(0, 116, 188, .14);   /* Blue #3 at 14%. A literal, never a self-reference */
    --on-accent: var(--white);               /* text on an accent or status fill */
    --on-sev: var(--white);                  /* the figure on a severity square: white in every scheme */

    /* --- Status (11(c)). Warn and danger are functional colours outside
       the brand palette, proposed and awaiting Convertus sign-off (11(d)). */
    --ok: var(--cv-green-1);
    --ok-soft: #E8F5E9;
    --ok-line: #B6DFB9;
    --warn: #B45309;
    --warn-soft: #FFF3E0;
    --warn-line: #FFCC80;
    --danger: #B42318;
    --danger-dark: #8F1C13;
    --danger-soft: #FDECEC;
    --danger-line: #F3C3C3;
    --info: var(--cv-blue-3);
    --info-soft: rgba(0, 116, 188, .08);
    --info-ink: var(--cv-blue-2);            /* --info text on --info-soft: Blue #3 there is 4.4:1, Blue #2 is over 9 */
    --neutral-ink: #546E7A;
    --neutral-soft: #ECEFF1;
    /* Incident severity 1-5 (slice 4): green to red, as Pulse EHS, each
       dark enough for white text (5:1 or more). Proposed, awaiting
       Convertus sign-off with the other status colours (11(d)). */
    --sev-1: var(--cv-green-1);
    --sev-2: #4D7C0F;
    --sev-3: #A16207;
    --sev-4: #C2410C;
    --sev-5: var(--danger);

    /* --- The navy rail (section 5 names, 11(c) values) --- */
    --rail: var(--cv-navy);
    --rail-2: var(--cv-navy);                /* brand band: same navy as the rail */
    --rail-text: var(--white);
    --rail-strong: var(--rail-text);
    --rail-ink: rgba(255, 255, 255, .86);
    --rail-ink-dim: rgba(255, 255, 255, .70); /* 7.3:1 on the rail */
    --rail-active: rgba(255, 255, 255, .10);
    --rail-hover: rgba(255, 255, 255, .06);
    --rail-line: rgba(255, 255, 255, .12);
    --rail-marker: var(--cv-blue-5);         /* 6.9:1 on the rail (11(a)) */
    --mark-dot: var(--cv-blue-5);

    /* --- Sign-in and error pages --- */
    --auth-bg: var(--cv-navy);               /* the card pages stay navy in every scheme (11(c)) */
    --overlay: rgba(15, 23, 32, .5);         /* phone drawer backdrop */
    --shadow-card: 0 18px 40px rgba(0, 0, 0, .28);
    --shadow-drawer: 0 0 44px rgba(0, 0, 0, .35);
    --shadow-menu: 0 8px 24px rgba(0, 0, 0, .25);
    --shadow-bar: 0 -6px 16px rgba(0, 0, 0, .08);   /* the sticky save bar, lifted off the page */

    /* --- The branded colour bar, in the brand book's order (11(a)) --- */
    --brand-bar: linear-gradient(to right,
        var(--cv-blue-5) 0 10%, var(--cv-blue-4) 10% 20%, var(--cv-blue-3) 20% 30%,
        var(--cv-blue-2) 30% 40%, var(--cv-blue-1) 40% 50%, var(--cv-green-1) 50% 60%,
        var(--cv-green-2) 60% 70%, var(--cv-green-3) 70% 80%, var(--cv-green-4) 80% 90%,
        var(--cv-green-5) 90% 100%);
    --brand-bar-h: 6px;
    /* On the navy sign-in and error pages the bar's Darkest Blue segment is
       the page colour and vanishes. A thin white line along the top edge
       closes the strip so every segment reads; the bar's colours and order
       are untouched (11(b) rule 11). */
    --brand-bar-edge: var(--white);
    --brand-bar-edge-w: 2px;
    /* On the Navy night page the same happens to the footer's bar: a faint
       ring round it there (scheme block), none elsewhere. */
    --brand-bar-ring: transparent;

    /* --- Type (11(c): Roboto, then Arial) --- */
    --font-body: "Roboto", Arial, sans-serif;
    --font-head: "Roboto", Arial, sans-serif;
    --font-menu: "Manrope", "Roboto", Arial, sans-serif;  /* menu items only (Chris, 16 Oct 2026) */
    --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    /* Sizes. Body is 14px; every rem is off the browser's 16px root,
       which is never changed (5.2). */
    --fs-micro: .68rem;            /* nav group headings, tags */
    --fs-label: .72rem;            /* pills, table heads, small caps labels */
    --fs-small: .8rem;             /* hints, footer, sub lines */
    --fs-field: .85rem;            /* field labels */
    --fs-control: .87rem;          /* buttons */
    --fs-body: 14px;
    --fs-panel: .95rem;            /* panel h2 */
    --fs-base: 1rem;
    --fs-title: 1.05rem;           /* appbar page title */
    --fs-heading: 1.3rem;          /* h1 on sign-in and error cards */
    --fs-stat: 1.9rem;
    --fs-stat-touch: 1.5rem;       /* phones: two tiles a row, "215.90 mt" on one line */
    --fs-stat-unit: .95rem;        /* the unit beside a tile's number */
    --fs-display: 3rem;            /* error code */
    --fs-mono: .93em;              /* monospace reads large beside Roboto */
    --fs-touch: 16px;              /* phone controls: stops iOS zooming */
    --fs-btn-touch: .92rem;
    --fw-regular: 400;
    --fw-medium: 500;
    --fw-bold: 700;
    --lh-tight: 1.15;
    --lh-body: 1.45;
    --ls-label: .05em;             /* uppercase small labels */
    --ls-suffix: .3em;             /* site suffix (11(b) rule 14) */
    --ls-ref: .06em;               /* ticket and record references: O and 0 stay apart */

    /* --- Spacing: the recurring section 5 steps --- */
    --sp-1: .15rem;
    --sp-2: .3rem;
    --sp-3: .5rem;
    --sp-4: .6rem;
    --sp-5: .7rem;
    --sp-6: .85rem;
    --sp-7: .9rem;
    --sp-8: 1rem;
    --sp-9: 1.1rem;
    --sp-10: 1.25rem;
    --sp-11: 1.6rem;
    --sp-12: 2rem;
    --sp-13: 2.5rem;

    /* --- Radius --- */
    --radius-sm: 4px;
    --radius: 7px;
    --radius-md: 8px;
    --radius-lg: 10px;
    --radius-xl: 12px;
    --radius-pill: 999px;

    /* --- Sizes --- */
    --sidebar-w: 232px;
    --appbar-h: 52px;
    --control-h: 37px;
    --control-h-touch: 40px;
    --tap: 40px;
    --tap-floor: 44px;             /* floor-use targets: gloves on a phone (5.8) */
    --check: 16px;                 /* checkbox and radio box, desktop (5.7) */
    --check-line: var(--muted);    /* an empty checkbox's edge: 5:1 on white, over the 3:1 a control needs */
    --check-tick-inset: 2px;       /* the tick's gap inside a checked box */
    /* The tick, drawn as a shape so no image or colour literal is needed */
    --check-tick-shape: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
    --check-touch: 20px;
    --icon: 18px;
    --icon-sm: 16px;
    --soon-mark: 64px;             /* the soft circle round a "Coming soon" page's icon */
    --soon-icon: 30px;             /* the icon inside it */
    --soon-max: 30rem;             /* the "Coming soon" line's measure */
    --nav-count-min: 20px;         /* a menu badge's least width: one digit is a circle */
    --nav-count-h: 20px;           /* a menu badge's height, as its line height */
    --chevron-shut: -90deg;        /* a closed menu section's chevron points right */
    --nav-row-h: 40px;             /* a top-level menu row: Dashboard and every section heading */
    --nav-sub-h: 34px;             /* a page inside a menu section */
    /* A page inside a section starts where its heading's text does: the
       row's padding, the heading icon and the gap after it. */
    --nav-indent: calc(var(--sp-8) + var(--icon) + var(--sp-4));
    --icon-stroke: 1.7;            /* one stroke width for every icon (5.3, 11(b) rule 10) */
    --focus-w: 3px;
    --outline-w: 2px;
    --marker-w: 3px;
    --form-max: 820px;
    --auth-max: 400px;
    --auth-max-narrow: 460px;
    --drawer-w: min(300px, 86vw);
    --switcher-max-h: 50vh;
    --stat-min: 150px;             /* flex-basis of a stat tile */
    --card-min: 240px;             /* flex-basis of a module card on a site home */
    --settings-card-min: 340px;    /* least width of a card on the Settings page before they stack */
    --field-min: 220px;            /* flex-basis of a field in a .row */
    --search-min: 240px;           /* flex-basis of a search box */
    --search-max: 520px;           /* a plain .searchbar; .searchbar-wide has no cap (5.5) */
    --date-w: 10rem;               /* a date input in a filter bar: fits dd/mm/yyyy and the picker */
    --search-min-touch: 140px;     /* phones: two filter controls per row at 390px */
    --amend-field-min: 150px;      /* field label column in an amendment entry (5.5) */
    --sev-size: 26px;              /* the severity square (5.5 badges) */
    --why-w: 22px;                 /* the number beside each of the 5 Whys */
    --checklist-pill-w: 7.5rem;    /* the Done / Missing pill on the Close page, one width */
    /* Pre-use inspection: answers are big targets for phones and gloves. */
    --choice-h: 56px;              /* an answer's height */
    --choice-min: 7.5rem;          /* flex-basis of an answer: two or three to a row on a phone */
    --choice-radio: 24px;          /* the radio dot inside an answer */
    --choice-line: 2px;            /* an answer's border */
    --obs-choice-min: 9.5rem;      /* Observations: answers with longer labels ("Maintenance shop") */
    --num-w: 7rem;                 /* a capacity box in the loader list */
    --thumb-w: 160px;              /* a photo thumbnail on an inspection */
    --video-w: 480px;              /* a video on an inspection, at most */
    /* WeighPoint: the comparison bar, two halves meeting in the middle. */
    --wp-bar-h: 12px;
    --wp-compare-min: 300px;       /* flex-basis of a comparison panel */
    --inv-value-w: 8rem;           /* an Inventory value box: "99,999.99" fits */
    --inv-seg-w: 3rem;             /* one half of the mt | % switch */
    --progress-h: 8px;             /* a slim progress bar (Bucket Check's Today strip) */
    --step-badge: 1.6rem;          /* a numbered circle beside a step's label (Bucket Check's weights 1, 2, 3) */
    /* A .btn's own height (one tight line of --fs-control, its padding and
       its 1px border), and the pull that centres it on one line of body
       text, so a button in a table action cell sits level with the row's
       text instead of pushing its line down. Negative by design. */
    --btn-h: calc(var(--fs-control) * var(--lh-tight) + 2 * var(--sp-3) + 2px);
    --grid-btn-pull: calc((var(--fs-body) * var(--lh-body) - var(--btn-h)) / 2);
    --opacity-disabled: .55;
    /* Logos. The rail logo is the secondary horizontal one, 658.81 x 108.88.
       Measured from a render of the file: the symbol fills the left 26.6%,
       the word "Convertus" runs from 31.3% to 97.3%, so its centre is at
       64.3%. The site suffix is centred there, under the word, not under
       the whole logo (Chris, 11 Oct 2026). */
    --logo-rail-h: 22px;
    --logo-rail-w: calc(var(--logo-rail-h) * 6.0508);
    --logo-word-mid: 64.3%;
    --logo-auth-w: 200px;

    /* --- Compact list (the standard page style, Chris 16 Oct 2026; blueprint 11(e)) ---
       Laptop density for list pages: a page header in the appbar, key
       figures, a filter row and dense tables. Phones keep touch sizes. */
    --fs-dense: 13px;              /* dense table cells, the count line, dense buttons */
    --fs-dense-head: 12px;         /* dense table headings, the Edited note */
    --fs-kpi: 18px;                /* a key figure */
    --btn-h-dense: 32px;           /* a dense button and filter control on a laptop */
    --r-dense: var(--radius-sm);   /* 4 px corners on dense surfaces and buttons */
    --dt-pad-y: 8px;               /* dense cell padding */
    --dt-pad-x: 12px;
    --dt-hover: var(--info-soft);  /* row hover and focus tint */
    --dt-dot: 8px;                 /* the status dot */
    --dt-dot-ring: 1.5px;          /* the hollow dot of "none" */
    --dt-chev-w: 1.5rem;           /* the chevron column */
    --kpi-min: 9rem;               /* least width of a key figure: four across a laptop, two on a phone */
    --fig-field-min: 9rem;         /* least width of a figure field in a .rec-figs grid: two across a phone */
    --kpi-gap: 1px;                /* the hairline between key figures */
    /* Subtle depth and colour on the compact pages (Chris, 15 Oct 2026:
       "subtle depth/colour ... just subtle"): a soft shadow under panels
       and tables, a faint brand tint under key figures, a faint grey-blue
       table header, a thin brand-blue accent on top of a header surface. */
    --shadow-1: 0 1px 2px rgba(23, 39, 80, .06), 0 1px 3px rgba(23, 39, 80, .04);  /* --cv-navy, very low alpha */
    --surface-tint: #F5F9FC;       /* Blue #3 at about 4% on white: key figures, weight boxes */
    --dt-thead: #F2F5F9;           /* a dense table's header band */
    --accent-top-w: 2px;           /* the brand-blue accent on top of a key-figures strip or object header */
    --dt-dot-halo: 2px;            /* the soft ring round a status dot */

    /* --- Compact record (the standard page style, Chris 16 Oct 2026; blueprint 11(e)) ---
       A record's page and its edit and delete pages, compact on a laptop. */
    --field-h-dense: 34px;         /* a field on a compact edit page, laptop */
    --rec-col-min: 22rem;          /* a record page's main and side columns sit side by side above twice this */
    --acc-col-min: 16rem;          /* least width of a module tick on the Access card and a role page: two or three across a laptop, one on a phone */
    --rmx-mod-w: 13rem;            /* the roles matrix's module column */
    --rmx-role-w: 7rem;            /* the least width of one role's column in the matrix */
    --rmx-tick: 22px;              /* a matrix tick box on a laptop (the cell's button is the 44 px target on a phone) */
    --rmx-mark-w: 5px;             /* the tick mark drawn inside a ticked box */
    --rmx-mark-h: 10px;
    --rb-side-w: 20rem;            /* a role page's side column (menu preview, people) on a laptop */
    --cdialog-w: 34rem;            /* a compact dialog's width on a laptop */
    --cdialog-sheet-h: 88dvh;      /* the tallest a dialog's bottom sheet grows on a phone */
    --cdialog-what-min: 12rem;     /* a dialog row's words before its button wraps under them */
    --shadow-dialog: 0 8px 28px rgba(23, 39, 80, .18);   /* --cv-navy: a dialog lifted off the dimmed page */

    /* --- Appearance: each person's own scheme and text size (Chris, 16 Oct
       2026; blueprint 11(c)). The scheme blocks after :root redefine only
       these tokens' neighbours; these are the switches they flip. --- */
    --color-scheme: light;         /* native controls (date pickers, scrollbars) */
    --rail-edge: transparent;      /* a hairline down the rail's right edge: drawn only on the white rail */
    --rail-logo-white: block;      /* the secondary white logo on a dark rail */
    --rail-logo-colour: none;      /* the full-colour primary logo, on the white rail only (11(b) rule 3) */
    --logo-stack-h: 56px;          /* the primary stacked logo in the white rail, 453 x 244 */
    --logo-stack-w: calc(var(--logo-stack-h) * 1.8566);
    --lockup-w: var(--logo-rail-w); /* the rail lockup's column: the logo in view */
    /* The preview cards on the Appearance page: each scheme's rail, its
       marker, page, panel and accent, fixed so a card shows its scheme
       whatever the page wears. Brand colours, and the neutral greys and
       navy shades the schemes below use. */
    --sw-convertus-rail: var(--cv-navy);
    --sw-convertus-mark: var(--cv-blue-5);
    --sw-convertus-page: #EEF1F4;
    --sw-convertus-panel: var(--white);
    --sw-convertus-accent: var(--cv-blue-3);
    --sw-navy-rail: var(--cv-navy);
    --sw-navy-mark: var(--cv-blue-5);
    --sw-navy-page: var(--cv-blue-1);
    --sw-navy-panel: #22366B;
    --sw-navy-accent: var(--cv-blue-5);
    --sw-green-rail: var(--cv-green-1);
    --sw-green-mark: var(--cv-green-5);
    --sw-green-page: #EEF1F4;
    --sw-green-panel: var(--white);
    --sw-green-accent: var(--cv-blue-3);
    --sw-light-rail: var(--white);
    --sw-light-mark: var(--cv-blue-3);
    --sw-light-page: #F3F5F8;
    --sw-light-panel: var(--white);
    --sw-light-accent: var(--cv-blue-3);
    --sw-line-opacity: .45;        /* the menu lines after the first in a preview's rail */
    --sw-rail-w: 28%;              /* a preview's rail */
    --sw-btn-w: 45%;               /* a preview's button */
    --sw-h: 64px;                  /* a preview's height */
    --scheme-card-min: 10.5rem;      /* least width of a scheme card: four across a laptop form, one or two on a phone */

    /* --- Motion and layers --- */
    --t-drawer: .22s;
    --z-appbar: 5;
    --z-menu: 10;
    --z-backdrop: 25;
    --z-drawer: 30;
    --z-skip: 100;

    /* --- ScadaView (docs/plans/scadaview.md section 8) --- */
    --sc-tile-min: 250px;          /* flex-basis of a board tile: four across a laptop */
    --sc-tank-min: 240px;
    --sc-tile-min-display: 300px;  /* the boardroom screen: four across at 1366 */          /* a water tank tile: the drawing and its figures */
    --sc-tank-w: 52px;             /* the tank drawing */
    --sc-tank-h: 132px;
    --sc-state-w: 4px;             /* the coloured edge on top of a tile */
    --sc-dot: 10px;                /* the LIVE dot, a pump or valve light */
    --fs-sc-main: 2.1rem;          /* a tile's main reading */
    --fs-sc-main-touch: 1.75rem;
    --fs-sc-display-main: 2.6rem;  /* on the boardroom screen */
    --sc-chart-h: 270px;
    --sc-chart-h-touch: 220px;
    --sc-line-w: 2px;
    --sc-stale-opacity: .55;       /* the board greys while the data is stale */
    --sc-detail-tile: 340px;       /* a tunnel's tile beside its charts */
    --sc-display-max: 1800px;
    --sc-secret-min: 28rem;
    /* Chart colours: brand blues and greens (lines and fills are
       decoration, 11(b) rule 6), and neutral for setpoints. */
    --sc-s1: var(--cv-blue-3);
    --sc-s2: var(--cv-green-2);
    --sc-s3: var(--cv-blue-1);
    --sc-s4: var(--cv-blue-4);
    --sc-s5: var(--neutral-ink);
    --sc-range-fill: rgba(0, 116, 188, .12);  /* Blue #3 at 12%: min to max of a rollup */
    --sc-band-fill: rgba(0, 103, 52, .09);    /* Darkest Green #1 at 9%: the target band */
    --sc-tank-fill: var(--cv-blue-4);
    --sc-dash: 5 4;
}

/* ======================================================================
   SCHEMES: each person's own appearance (Chris, 16 Oct 2026; blueprint
   11(c); app/appearance.py). A block per scheme on <html data-scheme>,
   redefining tokens only, never a component. Every colour is one of the
   ten brand colours, white, or a neutral grey or navy shade. The status
   colours keep their meaning in every scheme and the brand colour bar is
   the same everywhere. Contrast ratios are listed per block (WCAG,
   calculated). tests/css_lint.py allows these blocks: custom properties
   only, each one already defined in :root.

   Adapted from Pulse EHS app/static/css/style.css html[data-scheme] blocks
   @ aa321d3 (one token block per scheme). Changed: Convertus colours; a
   dark scheme; the rail logo switched by token, never recoloured.
   ====================================================================== */

/* Navy night: dark navy pages and panels, light text. Ink 9.6:1 on the
   panel, muted 5.8:1, headings (white) 11.6:1, links (Lightest Blue #5)
   6.0:1 on the panel and 6.9:1 on the page; a filled button is navy on
   Lightest Blue #5, 7.5:1. Status text is lightened to read on navy:
   ok (Green #4) 5.7:1, warn 7.0:1, danger 6.1:1 on the panel; on their
   soft bands 4.8, 5.4 and 4.7:1. Green #4 is text here only on dark,
   never on light (11(b) rule 6); warn and danger tints are functional,
   with the others awaiting sign-off (11(d)). */
html[data-scheme="navy"] {
    --color-scheme: dark;
    --bg: var(--cv-blue-1);
    --panel: #22366B;
    --line: #52669C;
    --line-soft: #2E4278;
    --ink: #E6EAF2;
    --muted: #AEB8CC;
    --heading: var(--white);
    --hover: #2A3F77;
    --row-hover: #263A70;
    --thead: #283D74;
    --accent: var(--cv-blue-5);
    --accent-dark: var(--cv-blue-4);
    --accent-soft: rgba(68, 200, 245, .12);
    --on-accent: var(--cv-navy);
    --ok: var(--cv-green-4);
    --ok-soft: rgba(140, 198, 63, .10);
    --ok-line: rgba(140, 198, 63, .45);
    --warn: #FBBF24;
    --warn-soft: rgba(251, 191, 36, .12);
    --warn-line: rgba(251, 191, 36, .45);
    --danger: #FCA5A5;
    --danger-dark: #FECACA;
    --danger-soft: rgba(252, 165, 165, .12);
    --danger-line: rgba(252, 165, 165, .45);
    --info: var(--cv-blue-5);
    --info-soft: rgba(68, 200, 245, .10);
    --info-ink: var(--cv-blue-5);
    --neutral-ink: #AEB8CC;
    --neutral-soft: rgba(255, 255, 255, .06);
    --sev-5: #B42318;
    --surface-tint: #263A70;
    --dt-thead: #283D74;
    --shadow-1: 0 1px 2px rgba(0, 0, 0, .25), 0 1px 3px rgba(0, 0, 0, .18);
    --sc-s3: var(--cv-green-4);
    --sc-range-fill: rgba(68, 200, 245, .16);
    --sc-band-fill: rgba(140, 198, 63, .12);
    --brand-bar-ring: rgba(255, 255, 255, .35);
}

/* Convertus green: the menu in Darkest Green #1, white pages as the
   default, Green #2 in the faint tints. White on the rail 7.0:1, the
   rail's ink 5.7:1 and its dim labels 5.0:1; markers in Lightest Green #5,
   4.8:1 on the rail (decoration on a dark surface, 11(b) rule 6). */
html[data-scheme="green"] {
    --rail: var(--cv-green-1);
    --rail-2: var(--cv-green-1);
    --rail-ink-dim: rgba(255, 255, 255, .78);
    --rail-marker: var(--cv-green-5);
    --mark-dot: var(--cv-green-5);
    --surface-tint: #F2F8F4;
    --dt-thead: #EEF5F1;
    --dt-hover: rgba(0, 148, 74, .08);
}

/* Light: a white menu with the full-colour primary logo (the only logo
   allowed on white, 11(b) rule 3), soft grey pages, Blue #3 accent, navy
   headings. Menu text navy 9.5:1 (dim 5.6:1) on white; marker Blue #3
   5.0:1; links Blue #3 4.6:1 on the page; muted 5.3:1 on the page. */
html[data-scheme="light"] {
    --bg: #F3F5F8;
    --rail: var(--white);
    --rail-2: var(--white);
    --rail-text: var(--cv-navy);
    --rail-strong: var(--cv-navy);
    --rail-ink: rgba(23, 39, 80, .86);
    --rail-ink-dim: rgba(23, 39, 80, .70);
    --rail-active: rgba(0, 116, 188, .10);
    --rail-hover: rgba(23, 39, 80, .05);
    --rail-line: #D6DCE2;
    --rail-edge: #D6DCE2;
    --rail-marker: var(--cv-blue-3);
    --mark-dot: var(--cv-blue-3);
    --rail-logo-white: none;
    --rail-logo-colour: block;
    --lockup-w: var(--logo-stack-w);
    --logo-word-mid: 50%;
}

/* Larger text: the base type tokens about an eighth bigger, nothing else.
   The page's root size (16px) is never changed (5.2). */
html[data-text="large"] {
    --fs-body: 15.75px;
    --fs-dense: 14.5px;
    --fs-dense-head: 13.5px;
    --fs-kpi: 20px;
    --fs-micro: .76rem;
    --fs-label: .8rem;
    --fs-small: .9rem;
    --fs-field: .95rem;
    --fs-control: .97rem;
    --fs-panel: 1.06rem;
    --fs-base: 1.12rem;
    --fs-title: 1.18rem;
}

/* ======================================================================
   BASE
   ====================================================================== */
* { box-sizing: border-box; }

/* The hidden attribute always hides, whatever display a class sets. */
[hidden] { display: none !important; }

html, body { height: 100%; }
html { color-scheme: var(--color-scheme); }
body {
    margin: 0;
    font-family: var(--font-body);
    font-size: var(--fs-body);
    line-height: var(--lh-body);
    background: var(--bg);
    color: var(--ink);
}

/* Headings get explicit sizes (5.2 gap: Pulse EHS fell back to browser
   defaults). The page title lives in the appbar, not an h1 in content. */
h1, h2, h3, h4 { font-family: var(--font-head); color: var(--heading); line-height: var(--lh-tight); }
h1 { font-size: var(--fs-heading); margin: 0 0 var(--sp-3); }
h2 { font-size: var(--fs-panel); margin: 0 0 var(--sp-4); }
h3 { font-size: var(--fs-control); margin: 0 0 var(--sp-3); }
h4 { font-size: var(--fs-field); margin: 0 0 var(--sp-2); }

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

/* Visible focus for keyboard users on every link and button (5.6 gap). */
a:focus-visible, button:focus-visible, summary:focus-visible, .btn:focus-visible {
    outline: var(--outline-w) solid var(--accent);
    outline-offset: var(--outline-w);
}

.muted { color: var(--muted); }
.mono, code { font-family: var(--font-mono); font-size: var(--fs-mono); }
/* A reference people read out and type: a scale ticket "O44234", an incident
   "INC-2026-0001". The UI face with lining, equal-width figures and a little
   tracking, so the letter O and the figure 0 stay apart (monospace made
   "O44234" read as "044234"). */
.ref { font-variant-numeric: lining-nums tabular-nums slashed-zero; letter-spacing: var(--ls-ref); font-weight: var(--fw-medium); }
.sr-only {
    position: absolute; width: 1px; height: 1px; overflow: hidden;
    clip: rect(0 0 0 0); white-space: nowrap;
}

/* Skip link: off screen until focused, then the first thing you see. */
.skip-link {
    position: absolute; left: var(--sp-3); top: var(--sp-3);
    transform: translateY(-200%);
    z-index: var(--z-skip);
    background: var(--panel); color: var(--accent);
    padding: var(--sp-3) var(--sp-6);
    border-radius: var(--radius);
}
.skip-link:focus { transform: none; box-shadow: var(--shadow-menu); }

/* ======================================================================
   SHELL: rail, appbar, content, footer
   ====================================================================== */
/* 100dvh after 100vh: iPad Safari counts the area behind its toolbar in
   100vh, which hid the bottom of the menu (Sign out); dvh is the visible
   height (Chris, 16 Oct 2026). Older browsers keep 100vh. */
.app { display: flex; height: 100vh; height: 100dvh; overflow: hidden; }

.sidebar {
    width: var(--sidebar-w);
    flex: 0 0 var(--sidebar-w);
    background: var(--rail);
    color: var(--rail-ink);
    display: flex;
    flex-direction: column;
    position: sticky;
    top: 0;
    height: 100vh;
    height: 100dvh;
    border-right: 1px solid var(--rail-edge);
}
/* Rail focus rings in the marker colour; accent blue is too dark on navy. */
.sidebar a:focus-visible, .sidebar button:focus-visible, .sidebar summary:focus-visible {
    outline-color: var(--rail-marker);
}

/* Brand, user block and switcher never shrink; only the nav gives. */
.sidebar-brand {
    flex: 0 0 auto;
    display: flex; align-items: center;
    min-height: var(--appbar-h);
    padding: var(--sp-6) var(--sp-7);
    background: var(--rail-2);
    border-bottom: 1px solid var(--rail-line);
}

/* The logo alone (no "MES" beside it), and the site in view
   under it as text (11(b) rules 3, 4 and 14). The logo is an <img> of the
   supplied file, never inline SVG, never recoloured. */
.brand-lockup {
    display: grid;
    grid-template-columns: var(--lockup-w);
    row-gap: var(--sp-2);
    justify-content: start;
    color: var(--rail-strong);
    text-decoration: none;
}
a.brand-lockup:hover { text-decoration: none; color: var(--rail-strong); }
.brand-lockup .brand-logo { display: var(--rail-logo-white); width: var(--logo-rail-w); height: var(--logo-rail-h); }
/* The Light scheme's white rail shows the full-colour primary logo instead
   (tokens --rail-logo-white and --rail-logo-colour; 11(b) rule 3). */
.brand-lockup .brand-logo-colour { display: var(--rail-logo-colour); width: var(--logo-stack-w); height: var(--logo-stack-h); }
/* Location suffix: capitals, bold, wide tracking, centred under the logo.
   White on the navy rail, --heading on white (the sign-in card). */
.site-suffix {
    display: block;
    font-family: var(--font-head);
    font-weight: var(--fw-bold);
    text-transform: uppercase;
    letter-spacing: var(--ls-suffix);
    text-align: center;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    color: var(--heading);
    font-size: var(--fs-small);
}
/* In the rail the suffix is centred on the word "Convertus", not on the
   whole logo: moved to the word's centre, then back by half its own width.
   The tracking adds space after the last letter, so half of it is added
   back to keep the letters themselves centred. It may run wider than the
   word ("ALL SITES"), so it is not clipped. */
.brand-lockup .site-suffix {
    justify-self: start;
    position: relative;
    left: var(--logo-word-mid);
    width: max-content;
    overflow: visible;
    transform: translateX(calc(-50% + var(--ls-suffix) / 2));
    color: var(--rail-strong);
    font-size: var(--fs-micro);
}

/* Site switcher: a <details> list of links, so it works with no script.
   At the foot of the rail, just above the person's name (Chris, 13 Oct
   2026); opening it takes room from the menu, the only part that shrinks. */
.site-switcher {
    flex: 0 0 auto;
    border-top: 1px solid var(--rail-line);
}
.site-switcher > summary {
    display: flex; align-items: center; gap: var(--sp-3);
    padding: var(--sp-4) var(--sp-8);
    cursor: pointer;
    list-style: none;
    color: var(--rail-ink);
    font-size: var(--fs-small);
}
.site-switcher > summary::-webkit-details-marker { display: none; }
.site-switcher > summary:hover { background: var(--rail-hover); color: var(--rail-strong); }
.site-switcher .switcher-chevron { margin-left: auto; display: inline-flex; }
.site-switcher[open] .switcher-chevron { transform: rotate(180deg); }
.site-list {
    list-style: none; margin: 0; padding: 0 0 var(--sp-3);
    max-height: var(--switcher-max-h); overflow-y: auto;
}
.site-list a {
    display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-3);
    padding: var(--sp-3) var(--sp-8) var(--sp-3) calc(var(--sp-8) + var(--icon) + var(--sp-3));
    color: var(--rail-ink);
    border-left: var(--marker-w) solid transparent;
}
.site-list a:hover { background: var(--rail-hover); color: var(--rail-strong); text-decoration: none; }
.site-list a[aria-current] {
    color: var(--rail-strong);
    background: var(--rail-active);
    border-left-color: var(--rail-marker);
}
.site-list .site-role {
    font-size: var(--fs-micro); text-transform: uppercase; letter-spacing: var(--ls-label);
    color: var(--rail-ink-dim);
}

.sidebar-nav {
    flex: 1 1 0; min-height: 0; padding: var(--sp-7) 0; overflow-y: auto;
    font-family: var(--font-menu);
    display: flex; flex-direction: column;
}
.sidebar-nav > * { flex: 0 0 auto; }
/* Menu rows. Dashboard and every section heading (.nav-summary, below)
   are the same full row: icon, name, medium weight, in the rail's strong
   ink. Pages inside a section are a step quieter: no icon, regular
   weight, indented to the heading's text (.nav-section > .nav-link). */
.nav-link {
    display: flex; align-items: center; gap: var(--sp-4);
    min-height: var(--nav-row-h);
    padding: var(--sp-2) var(--sp-8);
    color: var(--rail-strong); font-weight: var(--fw-medium); text-decoration: none;
    border-left: var(--marker-w) solid transparent;
}
.nav-link:hover { background: var(--rail-hover); color: var(--rail-strong); text-decoration: none; }
.nav-link.active, .nav-link[aria-current] {
    background: var(--rail-active);
    color: var(--rail-strong);
    border-left-color: var(--rail-marker);
}
.nav-ico { display: inline-flex; width: var(--icon); height: var(--icon); flex: 0 0 var(--icon); }
/* A menu badge: what this person has waiting in a module (Module.badge).
   Adapted from Pulse EHS app/static/css/style.css .nav-count @ aa321d3.
   Danger when something is late or a defect is flagged: a different
   colour, not just a bigger number. */
.nav-count {
    margin-left: auto; flex: 0 0 auto;
    min-width: var(--nav-count-min); padding: 0 var(--sp-3);
    border-radius: var(--radius-pill);
    background: var(--accent); color: var(--on-accent);
    font-size: var(--fs-label); font-weight: var(--fw-bold);
    line-height: var(--nav-count-h); text-align: center;
}
.nav-count-alert { background: var(--danger); }

/* Icons: Lucide, outline, one stroke width, colour from the text. */
.icon {
    width: 100%; height: 100%;
    fill: none; stroke: currentColor; stroke-width: var(--icon-stroke);
    stroke-linecap: round; stroke-linejoin: round;
}
.icon-inline { display: inline-flex; width: var(--icon-sm); height: var(--icon-sm); flex: 0 0 var(--icon-sm); }

.sidebar-user {
    flex: 0 0 auto;
    padding: var(--sp-6) var(--sp-8);
    border-top: 1px solid var(--rail-line);
}
.su-name { color: var(--rail-strong); font-weight: var(--fw-medium); display: block; }
.su-role {
    font-size: var(--fs-label); color: var(--rail-ink-dim);
    text-transform: uppercase; letter-spacing: var(--ls-label);
}
.su-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-3) var(--sp-5); margin-top: var(--sp-3); }
.su-link {
    display: inline-flex; align-items: center; gap: var(--sp-2);
    color: var(--rail-ink); font-size: var(--fs-small);
}
.su-link:hover { color: var(--rail-strong); }
.su-signout {
    display: inline-flex; align-items: center; gap: var(--sp-2);
    background: none; border: 1px solid var(--rail-line);
    color: var(--rail-ink); border-radius: var(--radius);
    padding: var(--sp-2) var(--sp-5);
    font: inherit; font-size: var(--fs-small); cursor: pointer;
}
.su-signout:hover { background: var(--rail-hover); color: var(--rail-strong); }

.main { flex: 1; min-width: 0; display: flex; flex-direction: column; }

.appbar {
    height: var(--appbar-h);
    flex: 0 0 auto;
    background: var(--panel);
    border-bottom: 1px solid var(--line);
    display: flex; align-items: center; justify-content: space-between; gap: var(--sp-8);
    padding: 0 var(--sp-10);
    position: sticky; top: 0; z-index: var(--z-appbar);
}
.appbar-title {
    font-family: var(--font-head);
    font-size: var(--fs-title);
    font-weight: var(--fw-bold);
    color: var(--heading);
    min-width: 0;
}
.appbar-actions { display: flex; gap: var(--sp-3); align-items: center; }

/* .content is the only scroller. Its body grows so the footer sits at the
   bottom of a short page and after the last line of a long one. */
.content {
    flex: 1;
    display: flex; flex-direction: column;
    padding: var(--sp-10);
    overflow-y: auto;
}
.content:focus { outline: none; }
.content-body { flex: 1 0 auto; }
.form-page { max-width: var(--form-max); margin: 0 auto; }

/* Footer on every signed-in page: the colour bar on top, then
   "Convertus MES · site · Powered by Pulse <version>" (11(c)). */
.app-footer { flex: 0 0 auto; margin-top: var(--sp-12); }
.app-footer p { margin: var(--sp-3) 0 0; font-size: var(--fs-small); color: var(--muted); }
.brand-bar { height: var(--brand-bar-h); background: var(--brand-bar); border-radius: var(--radius-sm); box-shadow: 0 0 0 1px var(--brand-bar-ring); }

/* Phones only: the hamburger, the backdrop, and the page's actions
   repeated under the appbar. Hidden above 720px. */
.nav-toggle-box, .nav-toggle, .nav-backdrop, .page-actions { display: none; }

/* ======================================================================
   FLASHES, ERRORS, NOTES
   ====================================================================== */
.flashes { margin-bottom: var(--sp-8); display: flex; flex-direction: column; gap: var(--sp-3); }
.flash { padding: var(--sp-4) var(--sp-6); border-radius: var(--radius); border: 1px solid; }
.flash-ok { background: var(--ok-soft); border-color: var(--ok-line); color: var(--ok); }
.flash-error { background: var(--danger-soft); border-color: var(--danger-line); color: var(--danger-dark); }

.errors {
    background: var(--danger-soft); border: 1px solid var(--danger-line); color: var(--danger-dark);
    padding: var(--sp-5) var(--sp-7); border-radius: var(--radius); margin-bottom: var(--sp-8);
}
.errors ul { margin: var(--sp-2) 0 0; padding-left: var(--sp-9); }

.warn-note {
    background: var(--warn-soft); border: 1px solid var(--warn-line); color: var(--ink);
    padding: var(--sp-4) var(--sp-6); border-radius: var(--radius);
    margin: 0 0 var(--sp-9);
}
.panel .warn-note { color: var(--ink); margin: var(--sp-7) 0; }
.panel .warn-note:first-child { margin-top: 0; }
.panel .warn-note:last-child { margin-bottom: 0; }

/* ======================================================================
   PANELS, EMPTY STATES, DETAIL LISTS, STATS
   ====================================================================== */
.panel {
    background: var(--panel);
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    padding: var(--sp-9) var(--sp-10);
    margin-bottom: var(--sp-9);
}
.panel-quiet { background: transparent; border-style: dashed; }
.panel-danger { border-left: var(--marker-w) solid var(--danger); }
.panel-danger > h2:first-child { color: var(--danger); }
.panel p { margin: 0 0 var(--sp-5); color: var(--muted); }
.panel > :last-child { margin-bottom: 0; }
.lede { font-size: var(--fs-title); margin: 0 0 var(--sp-8); }
.rule { border: 0; border-top: 1px solid var(--line-soft); margin: var(--sp-9) 0; }

.empty {
    background: var(--panel); border: 1px dashed var(--line);
    border-radius: var(--radius-lg); padding: var(--sp-13) var(--sp-10);
    text-align: center; color: var(--muted);
}
.empty p { margin: 0 0 var(--sp-8); }
.empty > :last-child { margin-bottom: 0; }

.detail-head {
    display: flex; align-items: center; gap: var(--sp-7);
    background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius-lg);
    padding: var(--sp-8) var(--sp-10); margin-bottom: var(--sp-9);
}
.detail-head > div { min-width: 0; }
.detail-title { font-size: var(--fs-title); font-weight: var(--fw-bold); color: var(--heading); overflow-wrap: anywhere; }
/* The muted second line under a title or a table cell's main text. */
.sub { font-size: var(--fs-small); color: var(--muted); margin-top: var(--sp-1); overflow-wrap: anywhere; }
/* "Originally: ..." or "Starting form: ...", what a renamed entry or a
   changed question first said, with its Reset button beside it. */
.original-note {
    display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2) var(--sp-3);
    font-size: var(--fs-small); color: var(--muted); margin-top: var(--sp-1); overflow-wrap: anywhere;
}
.detail-status { margin-left: auto; }
.detail-list { display: grid; grid-template-columns: auto 1fr; gap: var(--sp-2) var(--sp-9); margin: 0 0 var(--sp-6); }
.detail-list dt { color: var(--muted); font-size: var(--fs-field); }
.detail-list dd { margin: 0; }

.stat-row { display: flex; gap: var(--sp-7); flex-wrap: wrap; margin-bottom: var(--sp-9); }
.stat {
    flex: 1 1 var(--stat-min);
    background: var(--panel); border: 1px solid var(--line);
    border-radius: var(--radius-lg); padding: var(--sp-7) var(--sp-9);
}
.stat-num { font-size: var(--fs-stat); font-weight: var(--fw-bold); line-height: var(--lh-tight); font-family: var(--font-head); color: var(--heading); white-space: nowrap; }
.stat-unit { font-size: var(--fs-stat-unit); font-weight: var(--fw-medium); color: var(--muted); margin-left: var(--sp-1); }
.stat-label { font-size: var(--fs-label); text-transform: uppercase; letter-spacing: var(--ls-label); color: var(--muted); margin-top: var(--sp-1); }
.stat-sub { font-size: var(--fs-small); color: var(--muted); margin-top: var(--sp-2); }
a.stat-link { color: var(--ink); text-decoration: none; display: block; }
a.stat-link:hover { border-color: var(--accent); }

/* Modules (blueprint 13(j)). On a site home: one heading per section and a
   card per switched-on module. On the platform site page: a checklist per
   section. On the confirm page: what is being turned off, with counts. */
.module-section { margin-bottom: var(--sp-9); }
.module-grid { display: flex; gap: var(--sp-7); flex-wrap: wrap; }
.module-card {
    flex: 1 1 var(--card-min); min-width: 0;
    display: flex; flex-direction: column; gap: var(--sp-1);
    background: var(--panel); border: 1px solid var(--line);
    border-radius: var(--radius-lg); padding: var(--sp-7) var(--sp-9);
    color: var(--ink); text-decoration: none;
}
.module-card:hover { border-color: var(--accent); color: var(--ink); text-decoration: none; }
.module-card-title { font-weight: var(--fw-bold); color: var(--heading); overflow-wrap: anywhere; }
.module-checklist { border: 0; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--sp-4); }
.module-checklist legend { padding: 0; }
.module-checklist .sub { display: block; }

/* The site's Settings page (app/templates/site_admin/settings.html; Chris,
   14 Oct 2026): a card per area, side by side while each can be at least
   --settings-card-min wide, stacked on a phone. Each card is a heading and
   a list of setup pages, one row each: icon, label, a muted line, and a
   chevron; every row at least --tap-floor tall. */
.settings-grid {
    display: grid; gap: var(--sp-9); align-items: start;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, var(--settings-card-min)), 1fr));
    margin-bottom: var(--sp-9);
}
.settings-card {
    background: var(--panel); border: 1px solid var(--line);
    border-radius: var(--radius-lg); overflow: hidden; min-width: 0;
}
.settings-card-title {
    display: flex; align-items: center; gap: var(--sp-4);
    margin: 0; padding: var(--sp-7) var(--sp-9);
    color: var(--heading); border-bottom: 1px solid var(--line-soft);
}
.settings-card-ico { display: inline-flex; width: var(--icon); height: var(--icon); flex: 0 0 var(--icon); color: var(--accent); }
.settings-list { list-style: none; margin: 0; padding: 0; }
.settings-list li + li { border-top: 1px solid var(--line-soft); }
.settings-item {
    display: flex; align-items: center; gap: var(--sp-6);
    min-height: var(--tap-floor); padding: var(--sp-4) var(--sp-9);
    color: var(--ink); text-decoration: none;
}
.settings-item:hover { background: var(--hover); color: var(--ink); text-decoration: none; }
.settings-item:hover .settings-item-label { color: var(--accent-dark); }
.settings-item-ico { display: inline-flex; width: var(--icon); height: var(--icon); flex: 0 0 var(--icon); color: var(--muted); }
.settings-item-text { display: flex; flex-direction: column; flex: 1 1 auto; min-width: 0; }
.settings-item-label { font-weight: var(--fw-medium); color: var(--heading); overflow-wrap: anywhere; }
.settings-item-go { display: inline-flex; width: var(--icon-sm); height: var(--icon-sm); flex: 0 0 var(--icon-sm); color: var(--muted); }

/* A module's lists on one page (app/templates/site_lists/; Chris, 14 Oct
   2026, the Tunnel lists page): links to each list's section at the top,
   then a section per list under its own heading. The sections are the
   scroll targets; on a phone the sticky appbar would cover a heading, so
   each keeps clear of it. */
.jump-links { display: flex; flex-wrap: wrap; gap: var(--sp-3); margin-bottom: var(--sp-9); }
.jump-links a {
    display: inline-flex; align-items: center; min-height: var(--tap-floor);
    padding: 0 var(--sp-8); border: 1px solid var(--accent); border-radius: var(--radius-pill);
    background: var(--panel); color: var(--accent); font-weight: var(--fw-medium);
    text-decoration: none;
}
.jump-links a:hover { background: var(--accent-soft); color: var(--accent-dark); text-decoration: none; }
.list-section { scroll-margin-top: calc(var(--appbar-h) + var(--sp-7)); }
.list-section + .list-section { margin-top: var(--sp-12); padding-top: var(--sp-9); border-top: 1px solid var(--line); }
.list-section-title { font-size: var(--fs-title); color: var(--heading); margin: 0 0 var(--sp-7); }
.list-section > form.stack { margin-bottom: var(--sp-9); }

/* The site dashboard (docs/plans/dashboard.md, app/templates/site/).
   The range bar: .pill links on a laptop (the current one filled with the
   accent); on a phone one row, a native select and Go (13(j) rule 7). A
   container is a .panel of groups, each a heading and a .stat-row; groups
   sit side by side on a laptop and stack on a phone, where the tiles are
   two to a row (.stat in the 720px rule). */
.range-bar { display: flex; align-items: center; gap: var(--sp-3); margin-bottom: var(--sp-3); }
.range-pills { display: flex; flex-wrap: wrap; gap: var(--sp-3); }
a.range-pill {
    padding: var(--sp-2) var(--sp-4);
    background: var(--panel); border: 1px solid var(--line);
    color: var(--neutral-ink); text-decoration: none;
}
a.range-pill:hover { border-color: var(--accent); color: var(--accent-dark); text-decoration: none; }
a.range-pill[aria-current] { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.range-select { display: none; }
.range-custom { margin-bottom: var(--sp-3); }
.range-label { margin: 0 0 var(--sp-7); }
.dash-groups { display: flex; flex-wrap: wrap; gap: var(--sp-9) var(--sp-10); }
.dash-group { flex: 1 1 calc(2 * var(--stat-min) + var(--sp-7)); min-width: 0; }
.dash-group-wide { flex: 2 1 calc(4 * var(--stat-min) + 3 * var(--sp-7)); }
.dash-group .stat-row { margin-bottom: 0; }

/* ======================================================================
   FORMS
   ====================================================================== */
.stack { display: flex; flex-direction: column; gap: var(--sp-7); }
.row { display: flex; gap: var(--sp-7); flex-wrap: wrap; }
.row .field { flex: 1 1 var(--field-min); min-width: 0; }
.field { display: flex; flex-direction: column; }
.field label, .field .field-label {
    display: block;
    font-weight: var(--fw-medium); font-size: var(--fs-field);
    margin-bottom: var(--sp-2);
}
.field .check-block + .check-block { margin-top: var(--sp-2); }
.opt { font-weight: var(--fw-regular); color: var(--muted); font-size: var(--fs-small); }
.hint { font-size: var(--fs-small); color: var(--muted); margin: var(--sp-2) 0 0; }
.hint .mono { white-space: nowrap; }
.field-error { color: var(--danger); font-size: var(--fs-small); font-weight: var(--fw-medium); margin-top: var(--sp-2); }

/* Fields placed straight into a panel get the gap a .stack would give. */
.panel > :not(h2) + .field, .panel > :not(h2) + .row,
.panel > .field + .form-actions, .panel > .row + .form-actions { margin-top: var(--sp-7); }

/* Style controls by EXCLUSION, not by listing types (5.7): new input
   types are right by default. Single-line controls share one height so
   mixed rows line up; textarea sizes to its rows. */
input:not([type=checkbox]):not([type=radio]):not([type=file]):not([type=submit]):not([type=button]):not([type=hidden]):not([type=range]):not([type=color]),
select,
textarea {
    font: inherit;
    padding: var(--sp-3) var(--sp-5);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--panel);
    color: var(--ink);
    width: 100%;
}
input:not([type=checkbox]):not([type=radio]):not([type=file]):not([type=submit]):not([type=button]):not([type=hidden]):not([type=range]):not([type=color]),
select { height: var(--control-h); }
textarea { resize: vertical; }
input:not([type=checkbox]):not([type=radio]):not([type=file]):focus,
select:focus,
textarea:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 var(--focus-w) var(--accent-soft);
}
/* Checkboxes are drawn here, not by the browser: a box with a --check-line
   edge, filled --accent with an --on-accent tick when on. Native drawing
   was left to WebKit, and the system WebKit on macOS paints a checked box
   in a window that is not in front as a bare tick with no box (seen in
   the Modules panel at 1366px). Explicit size and flex: none so a box
   beside long label text never shrinks. No display here: the hidden
   #nav-toggle checkbox keeps its display: none. Radios stay native. */
input[type=checkbox], input[type=radio] {
    accent-color: var(--accent);
    width: var(--check); height: var(--check);
    flex: none;
}
input[type=checkbox] {
    -webkit-appearance: none; appearance: none;
    position: relative;
    margin: 0;
    border: 1px solid var(--check-line);
    border-radius: var(--radius-sm);
    background: var(--panel);
    cursor: pointer;
}
input[type=checkbox]::before {
    content: "";
    position: absolute; inset: var(--check-tick-inset);
    background: var(--on-accent);
    clip-path: var(--check-tick-shape);
    visibility: hidden;
}
input[type=checkbox]:checked { background: var(--accent); border-color: var(--accent); }
input[type=checkbox]:checked::before { visibility: visible; }
input[type=checkbox]:disabled { opacity: var(--opacity-disabled); cursor: not-allowed; }
/* Windows high contrast: hand the box back to the system. */
@media (forced-colors: active) {
    input[type=checkbox] { -webkit-appearance: auto; appearance: auto; }
    input[type=checkbox]::before { content: none; }
}
input[type=checkbox]:focus-visible, input[type=radio]:focus-visible {
    outline: var(--outline-w) solid var(--accent); outline-offset: var(--outline-w);
}
input[aria-invalid=true], select[aria-invalid=true], textarea[aria-invalid=true] { border-color: var(--danger); }

.check { display: flex; align-items: center; gap: var(--sp-2); font-size: var(--fs-control); }
.check input:not([type=checkbox]):not([type=radio]) { width: auto; }
.check-block { align-items: flex-start; }
.check-block input { margin-top: var(--sp-1); }
.check-block .sub { display: block; }
/* A form editor setting: its name, then a one-line hint under it (forms/field.html). */
.check-setting .sub { display: block; font-weight: var(--fw-regular); }
/* The form editor's Required / Optional switch, one per question
   (forms/editor.html req_switch): two buttons in one small form, the
   current one filled with the accent; "Always required" is a single
   greyed half. Dense on a laptop, 44 px on a phone. New for Convertus MES. */
.req-cell { white-space: nowrap; }
.req-seg { display: inline-flex; border: 1px solid var(--accent); border-radius: var(--r-dense); overflow: hidden; vertical-align: middle; }
.req-seg .req-opt {
    min-height: var(--btn-h-dense); padding: 0 var(--sp-4); margin: 0; border: 0; border-radius: 0;
    display: inline-flex; align-items: center; cursor: pointer;
    font: inherit; font-size: var(--fs-dense); font-weight: var(--fw-medium);
    color: var(--accent); background: var(--panel);
}
.req-seg .req-opt + .req-opt { border-left: 1px solid var(--accent); }
.req-seg .req-opt:hover { background: var(--hover); }
.req-seg .req-opt.is-on { background: var(--accent); color: var(--on-accent); font-weight: var(--fw-bold); }
.req-seg .req-opt:focus-visible { outline: var(--outline-w) solid var(--accent); outline-offset: calc(-2 * var(--outline-w)); box-shadow: inset 0 0 0 var(--focus-w) var(--accent-soft); }
.req-seg.is-fixed { border-color: var(--line); }
.req-seg.is-fixed .req-opt, .req-seg.is-fixed .req-opt:hover { cursor: default; color: var(--muted); background: var(--hover); font-weight: var(--fw-medium); }
.req-word { font-size: var(--fs-dense); }
@media (max-width: 720px) {
    .req-seg .req-opt { min-height: var(--tap-floor); }
}

.form-actions { display: flex; gap: var(--sp-3); align-items: center; flex-wrap: wrap; }
/* The one sentence a form says before its buttons: what happens next, in the
   same place on every form. A .stack puts it the usual gap above the buttons. */
.form-note { margin: 0; }
/* A form that submits itself when a control changes (app.js, data-autosubmit)
   keeps its button for people without script; with script it is not needed. */
html:not(.no-js) [data-autosubmit] [data-autosubmit-button] { display: none; }
.inline-form { display: inline; }

/* Filter bars (5.5, 6.11 item 10): a GET form of selects, dates, one
   "Filter" button and a "Clear" link. .searchbar-wide lifts the width cap
   for list pages. Controls keep their shared height so the row lines up;
   on phones they wrap two to a row rather than shrink (5.8).
   Adapted from Pulse EHS app/static/css/style.css @ aa321d3 (.searchbar,
   .searchbar-wide, .date-pair), with tokens in place of raw values. */
.searchbar {
    display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-3);
    margin-bottom: var(--sp-8);
    max-width: var(--search-max);
}
.searchbar-wide { max-width: none; }
.searchbar input[type=search] { flex: 1 1 var(--search-min); width: auto; }
/* The shared control rule (by exclusion) outranks any class rule on
   inputs and sets width:100%, so filter inputs get an explicit flex
   basis rather than relying on width:auto. */
.searchbar select { width: auto; flex: 0 0 auto; }
.searchbar input[type=date] { flex: 0 1 var(--date-w); min-width: 0; }
.searchbar label:not(.check) {
    display: inline-flex; align-items: center; gap: var(--sp-2);
    font-size: var(--fs-field); color: var(--muted);
}

.disclose { margin-top: var(--sp-7); border-top: 1px solid var(--line-soft); padding-top: var(--sp-5); }
.disclose summary { cursor: pointer; font-weight: var(--fw-medium); }
.disclose > .field, .disclose > .row, .disclose > .stack { margin-top: var(--sp-7); }

/* ======================================================================
   BUTTONS
   One hierarchy, every page (blueprint 13(j) rule 7, Chris 13 Oct 2026):
     .btn-primary    filled accent. THE action of a form or page, one per
                     form: Save, Add the loader, Add the task, Apply.
     .btn-secondary  accent border and accent text, visible on a white
                     panel and on the grey page: every other real action
                     (Filter, Go, + Add another ..., Submit another,
                     Reset ..., Sign out).
     .btn-quiet      borderless accent text, link-like: Cancel, Back.
     .btn-danger / .btn-danger-quiet  destructive or destructive-looking.
   A bare .btn draws as .btn-secondary, so a button that was given no
   variant still reads as a button; never a plain white button. Templates
   always name the variant anyway.
   ====================================================================== */
.btn {
    display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2);
    font: inherit; font-size: var(--fs-control); line-height: var(--lh-tight);
    padding: var(--sp-3) var(--sp-6);
    border: 1px solid var(--accent);
    background: var(--panel); color: var(--accent);
    border-radius: var(--radius); cursor: pointer;
    text-decoration: none; white-space: nowrap;
}
.btn:hover {
    background: var(--accent-soft); border-color: var(--accent-dark);
    color: var(--accent-dark); text-decoration: none;
}
.btn-primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.btn-primary:hover { background: var(--accent-dark); border-color: var(--accent-dark); color: var(--on-accent); }
.btn-quiet { background: transparent; border-color: transparent; color: var(--accent); }
.btn-quiet:hover { background: var(--accent-soft); border-color: transparent; color: var(--accent-dark); }
/* The same look as a bare .btn, named so the template says what it means. */
.btn-secondary { border-color: var(--accent); color: var(--accent); }
.btn-secondary:hover { border-color: var(--accent-dark); color: var(--accent-dark); }
.btn-danger { background: var(--danger); border-color: var(--danger); color: var(--on-accent); }
.btn-danger:hover { background: var(--danger-dark); border-color: var(--danger-dark); color: var(--on-accent); }
.btn-danger-quiet { background: transparent; border-color: transparent; color: var(--danger); }
.btn-danger-quiet:hover { background: var(--danger-soft); border-color: transparent; color: var(--danger-dark); }
.btn-block { display: flex; width: 100%; padding: var(--sp-4); font-size: var(--fs-panel); }
.btn-xs { font-size: var(--fs-label); padding: var(--sp-1) var(--sp-3); }
.btn:disabled, .btn[aria-disabled=true] { opacity: var(--opacity-disabled); cursor: not-allowed; }
.linkish {
    background: none; border: 0; padding: 0; font: inherit; color: var(--accent);
    text-decoration: underline; cursor: pointer;
}
.linkish:hover { color: var(--accent-dark); }

/* ======================================================================
   TABLES. Every .grid sits inside .table-scroll (5.5).
   ====================================================================== */
.table-scroll { overflow-x: auto; margin-bottom: var(--sp-9); }
.table-scroll > .grid { margin-bottom: 0; }
.grid {
    width: 100%; border-collapse: separate; border-spacing: 0;
    background: var(--panel);
    border: 1px solid var(--line); border-radius: var(--radius-lg);
    overflow: hidden;
}
.grid th, .grid td {
    padding: var(--sp-4) var(--sp-6); text-align: left;
    border-bottom: 1px solid var(--line-soft); vertical-align: top;
}
.grid.tight th, .grid.tight td { padding: var(--sp-2) var(--sp-5); }
.grid thead th {
    background: var(--thead); font-size: var(--fs-label); text-transform: uppercase;
    letter-spacing: var(--ls-label); color: var(--muted); font-weight: var(--fw-bold);
    white-space: nowrap;
}
.grid tbody tr:last-child td { border-bottom: 0; }
.grid tbody tr:hover { background: var(--row-hover); }
.grid .num { text-align: right; }
.grid .actions { text-align: right; white-space: nowrap; }
/* Buttons and button-style links in an action cell: centred on the row's
   first line of text and pulled into the cell padding, so they no longer
   push that line down below the text in the other cells. Two side by side
   (Up / Down) keep a small gap even with no space between the forms. */
.grid .actions .btn { vertical-align: middle; margin-block: var(--grid-btn-pull); }
.grid .actions .inline-form + .inline-form,
.grid .actions .btn + .inline-form,
.grid .actions .inline-form + .btn { margin-left: var(--sp-1); }
.grid .strong { font-weight: var(--fw-medium); color: var(--ink); }
/* Retired rows: muted text on a faint band, not opacity. Opacity (Pulse
   EHS used .58) took body text to about 3:1 and muted sub-lines lower,
   under the 4.5 needed; --muted on --row-off-bg stays above 5:1. The
   status pill still says Retired. */
.grid .row-off td { background: var(--row-off-bg); color: var(--muted); }
.grid .row-off .strong { color: var(--muted); }

/* ======================================================================
   AMENDMENT TRAILS (5.5 "Timelines and amendment trails"): one .amendment
   per log entry, a left rule, the head line (who · when · site · area),
   the reason in italics, then each changed field as label, old, arrow,
   new. A create shows only the new value.
   Adapted from Pulse EHS app/static/css/style.css @ aa321d3 (.amendment*).
   Changed: the old value is muted and struck rather than red, and the new
   value is navy rather than green. A configuration change is neither an
   error nor a success, and green means success or running (11(b) rule 7).
   ====================================================================== */
.amendment {
    border-left: var(--marker-w) solid var(--line);
    padding: var(--sp-1) 0 var(--sp-1) var(--sp-6);
    margin-bottom: var(--sp-7);
    overflow-wrap: anywhere;
}
.amendment:last-child { margin-bottom: 0; }
.amendment-head { font-size: var(--fs-field); color: var(--muted); }
.amendment-head strong { color: var(--ink); font-weight: var(--fw-medium); }
.amendment .amendment-reason { margin: var(--sp-2) 0 0; color: var(--muted); font-style: italic; }
.amendment-fields { list-style: none; margin: var(--sp-2) 0 0; padding: 0; }
.amendment-fields li {
    display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--sp-1) var(--sp-3);
    font-size: var(--fs-control);
}
.amendment-fields li + li { margin-top: var(--sp-1); }
.amendment-field { font-weight: var(--fw-medium); min-width: var(--amend-field-min); }
.amendment-fields .was { color: var(--muted); text-decoration: line-through; }
.amendment-fields .arrow { color: var(--muted); }
.amendment-fields .now { color: var(--heading); font-weight: var(--fw-medium); }

/* ======================================================================
   INCIDENTS (slice 4): severity squares and picker, the "Still needed"
   panel, root causes, the 5 Whys, the end-of-page finish panel, and
   free text kept as typed.
   Adapted from Pulse EHS app/static/css/style.css @ aa321d3 (.prose,
   .clamp, .sev*, .panel-gaps, .gap-list, .cause*, .why-field,
   .panel-finish), with tokens in place of raw values.
   ====================================================================== */
.prose { white-space: pre-wrap; color: var(--ink); margin: 0 0 var(--sp-6); line-height: var(--lh-body); overflow-wrap: anywhere; }
.panel p.prose { color: var(--ink); }
.clamp { display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; overflow-wrap: anywhere; }

/* Severity 1-5. The colour means the same everywhere; the wording beside
   it is the site's. */
.sev {
    display: inline-flex; align-items: center; justify-content: center;
    width: var(--sev-size); height: var(--sev-size); flex: 0 0 var(--sev-size);
    border-radius: var(--radius-sm);
    font-weight: var(--fw-bold); font-size: var(--fs-field); color: var(--on-sev);
}
.sev-1 { background: var(--sev-1); }
.sev-2 { background: var(--sev-2); }
.sev-3 { background: var(--sev-3); }
.sev-4 { background: var(--sev-4); }
.sev-5 { background: var(--sev-5); }
.sev-fieldset { border: 0; margin: 0; padding: 0; min-width: 0; }
.sev-fieldset legend { padding: 0; }
.sev-picker { display: flex; flex-direction: column; gap: var(--sp-2); }
.sev-choice {
    display: flex; align-items: center; gap: var(--sp-4);
    padding: var(--sp-2) var(--sp-4); border: 1px solid var(--line);
    border-radius: var(--radius); cursor: pointer; background: var(--panel);
}
.sev-choice:hover { background: var(--hover); }
.sev-choice input { margin: 0; }
.sev-choice .sev-label { font-size: var(--fs-control); }
.sev-choice:has(input:checked) { border-color: var(--accent); box-shadow: 0 0 0 var(--outline-w) var(--accent-soft); }

.panel-gaps { border-left: var(--marker-w) solid var(--warn); background: var(--warn-soft); }
.panel-gaps > h2:first-child { color: var(--warn); }
.gap-list { margin: var(--sp-1) 0 var(--sp-5); padding-left: var(--sp-9); color: var(--ink); }
.gap-list li + li { margin-top: var(--sp-1); }

.cause-list { display: flex; flex-direction: column; gap: var(--sp-5); margin-bottom: var(--sp-8); }
.cause { border: 1px solid var(--line-soft); border-radius: var(--radius-md); padding: var(--sp-5) var(--sp-6); background: var(--row-hover); }
.cause-head { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); margin-bottom: var(--sp-3); }
.cause-label { font-weight: var(--fw-medium); overflow-wrap: anywhere; }
.cause .stack { gap: var(--sp-3); }

.why-field { flex-direction: row; align-items: center; gap: var(--sp-4); }
.disclose > .why-field + .why-field { margin-top: var(--sp-3); }
.why-field label {
    flex: 0 0 var(--why-w); margin: 0; text-align: center;
    background: var(--line-soft); border-radius: var(--radius-sm); padding: var(--sp-1) 0;
    font-size: var(--fs-small); color: var(--muted);
}

/* End of a long record page: quieter than a working panel, so it reads
   as a full stop rather than another thing to fill in (5.5). */
.panel-finish { background: transparent; border-style: dashed; }
.panel-finish > h2:first-child { color: var(--muted); }

/* ======================================================================
   PILLS AND TAGS. One status family: .pill + a meaning (6.11 item 5).
   ====================================================================== */
.pill {
    display: inline-block; font-size: var(--fs-label); font-weight: var(--fw-bold);
    padding: var(--sp-1) var(--sp-3); border-radius: var(--radius-pill);
    text-transform: uppercase; letter-spacing: var(--ls-label); line-height: var(--lh-tight);
    white-space: nowrap;
}
.pill-on { background: var(--ok-soft); color: var(--ok); }
.pill-off { background: var(--neutral-soft); color: var(--neutral-ink); }
.pill-warn { background: var(--warn-soft); color: var(--warn); }
.pill-danger { background: var(--danger-soft); color: var(--danger); }
.pill-info { background: var(--info-soft); color: var(--info-ink); }

.tag {
    display: inline-block; margin-left: var(--sp-2);
    font-size: var(--fs-micro); text-transform: uppercase; letter-spacing: var(--ls-label);
    background: var(--line-soft); color: var(--muted);
    padding: var(--sp-1) var(--sp-2); border-radius: var(--radius-sm);
}

/* ======================================================================
   SIGN-IN AND ERROR PAGES: a white card on the navy page, the stacked
   full-colour logo on white, the colour bar along the bottom (11(b) 11).
   ====================================================================== */
.auth-page {
    min-height: 100vh;
    display: flex; flex-direction: column;
    background: var(--auth-bg);
}
.auth-main {
    flex: 1 0 auto;
    display: flex; flex-direction: column;
    align-items: center; justify-content: center;
    padding: var(--sp-12) var(--sp-8);
}
.auth-card {
    background: var(--panel);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-card);
    padding: var(--sp-12) var(--sp-12) var(--sp-11);
    width: 100%; max-width: var(--auth-max);
}
.auth-card-narrow { max-width: var(--auth-max-narrow); text-align: center; }
.auth-brand { display: flex; flex-direction: column; align-items: center; margin-bottom: var(--sp-11); }
.auth-logo { display: block; width: var(--logo-auth-w); max-width: 100%; height: auto; }
.auth-brand .site-suffix {
    margin-top: var(--sp-5);
    max-width: var(--logo-auth-w);
    margin-right: calc(-1 * var(--ls-suffix));
}
.auth-card h1 { text-align: center; }
.auth-sub { text-align: center; color: var(--muted); margin: 0 0 var(--sp-9); }
.auth-form { display: flex; flex-direction: column; }
.auth-form label { font-weight: var(--fw-medium); font-size: var(--fs-field); margin-bottom: var(--sp-2); }
.auth-form input { margin-bottom: var(--sp-7); }
.auth-form .btn-block { margin-top: var(--sp-1); }
/* A hint under a field (reset password): pulled up to its field, then the usual gap. */
.auth-form input + .hint { margin: calc(-1 * var(--sp-5)) 0 var(--sp-7); }
.auth-foot { text-align: center; margin: var(--sp-9) 0 0; font-size: var(--fs-field); color: var(--muted); }
.auth-legal { text-align: center; color: var(--rail-ink-dim); font-size: var(--fs-small); margin: var(--sp-9) 0 0; }
.auth-page > .brand-bar {
    flex: 0 0 auto; border-radius: 0;
    box-shadow: 0 calc(-1 * var(--brand-bar-edge-w)) 0 var(--brand-bar-edge);
}

.error-code { font-size: var(--fs-display); font-weight: var(--fw-bold); color: var(--line); line-height: 1; }
.error-title { margin: var(--sp-2) 0 var(--sp-3); }
.error-message { color: var(--muted); margin: 0; }
.error-who { color: var(--muted); font-size: var(--fs-field); margin: var(--sp-7) 0 0; }
.error-actions { display: flex; gap: var(--sp-3); justify-content: center; align-items: center; flex-wrap: wrap; margin-top: var(--sp-8); }

/* ======================================================================
   PHONES: one breakpoint, 720px (5.8). The rail becomes a drawer opened
   by the #nav-toggle checkbox, so it works with no script; app.js adds
   the keyboard. Controls go to 16px and 40px tall.
   ====================================================================== */
@media (max-width: 720px) {
    .app { display: block; height: auto; min-height: 100vh; overflow: visible; }
    .main { min-height: 100vh; }
    .content { overflow-y: visible; padding: var(--sp-7); min-height: calc(100vh - var(--appbar-h)); }

    .sidebar {
        position: fixed; top: 0; left: 0; bottom: 0;
        height: 100vh; height: 100dvh;
        width: var(--drawer-w); flex: none;
        transform: translateX(-104%);
        visibility: hidden;
        transition: transform var(--t-drawer) ease, visibility 0s linear var(--t-drawer);
        z-index: var(--z-drawer);
        padding-top: env(safe-area-inset-top);
    }
    .nav-toggle-box:checked ~ .sidebar {
        transform: none; visibility: visible;
        transition: transform var(--t-drawer) ease;
        box-shadow: var(--shadow-drawer);
    }
    .nav-backdrop {
        display: block; position: fixed; inset: 0; z-index: var(--z-backdrop);
        background: var(--overlay);
        opacity: 0; pointer-events: none; transition: opacity var(--t-drawer) ease;
    }
    .nav-toggle-box:checked ~ .nav-backdrop { opacity: 1; pointer-events: auto; }
    body:has(.nav-toggle-box:checked) { overflow: hidden; }

    /* Menu targets in the drawer: 44px, for gloves (--tap-floor, 5.8). */
    .nav-link { padding-top: var(--sp-3); padding-bottom: var(--sp-3); font-size: var(--fs-panel); min-height: var(--tap-floor); }
    .site-switcher > summary { min-height: var(--tap-floor); font-size: var(--fs-field); }
    .site-list a { min-height: var(--tap-floor); align-items: center; }
    .su-link { min-height: var(--tap-floor); font-size: var(--fs-field); }
    .su-signout { min-height: var(--tap-floor); padding: var(--sp-2) var(--sp-7); font-size: var(--fs-field); }

    .nav-toggle {
        display: inline-flex; flex: 0 0 var(--tap);
        justify-content: center; align-items: center;
        width: var(--tap); height: var(--tap);
        margin-left: calc(-1 * var(--sp-2));
        border-radius: var(--radius-md); cursor: pointer;
        color: var(--ink);
    }
    .nav-toggle .nav-ico { width: var(--icon); height: var(--icon); }
    .nav-toggle:hover, .nav-toggle:focus-visible { background: var(--bg); outline: none; }
    .nav-toggle:focus-visible { box-shadow: 0 0 0 var(--focus-w) var(--accent-soft); }

    .appbar { gap: var(--sp-3); padding: 0 var(--sp-7); }
    .appbar-title { flex: 1 1 auto; font-size: var(--fs-base); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .appbar-actions { display: none; }
    /* The page's actions as a toolbar band joined to the appbar: white, a
       line under it, buttons with edges, so they read as part of the header
       rather than a stray link floating over the page. */
    .page-actions {
        display: flex; flex-wrap: wrap; gap: var(--sp-3);
        padding: var(--sp-3) var(--sp-7);
        background: var(--panel); border-bottom: 1px solid var(--line);
    }
    .page-actions .btn { flex: 1 1 auto; }
    .page-actions .btn-quiet { border-color: var(--line); }

    input:not([type=checkbox]):not([type=radio]):not([type=file]):not([type=submit]):not([type=button]):not([type=hidden]):not([type=range]):not([type=color]),
    select, textarea { font-size: var(--fs-touch); }
    input:not([type=checkbox]):not([type=radio]):not([type=file]):not([type=submit]):not([type=button]):not([type=hidden]):not([type=range]):not([type=color]),
    select { height: var(--control-h-touch); }
    .btn { min-height: var(--tap); padding: var(--sp-3) var(--sp-7); font-size: var(--fs-btn-touch); }
    .btn-xs { min-height: var(--control-h); }
    .form-actions .btn-primary { flex: 1 1 auto; }
    input[type=checkbox], input[type=radio] { width: var(--check-touch); height: var(--check-touch); }

    /* Action buttons stay on one line on phones too; a wide row scrolls
       inside .table-scroll rather than stacking Up, Down and Edit.
       Phone buttons are 40px, so the desktop pull is not used here. */
    .grid .actions .btn { margin-block: 0; }
    /* A person's sites and roles: the role button and Revoke stack in the
       cell, so both stay on screen at 390 px with no sideways scroll. */
    .grid.access-grid .actions .inline-form { display: block; }
    .grid.access-grid .actions .inline-form + .inline-form { margin-left: 0; margin-top: var(--sp-2); }
    .grid.access-grid .actions { white-space: normal; }
    .grid.access-grid .actions .btn { width: 100%; white-space: normal; }
    .searchbar > select, .searchbar > input, .searchbar > label:not(.check),
    .searchbar input[type=date] { flex: 1 1 var(--search-min-touch); }
    .searchbar label > input, .searchbar label > select { flex: 1 1 0; min-width: 0; }
    .searchbar .btn { flex: 1 1 auto; }
    .grid th, .grid td { padding: var(--sp-4) var(--sp-4); }
    .stat { flex-basis: calc(50% - var(--sp-7)); }
    .stat-num { font-size: var(--fs-stat-touch); }
    .sev-choice { min-height: var(--tap-floor); }
    .check-block, label.check { min-height: var(--tap); }
    .detail-head { flex-wrap: wrap; }
    .detail-status { margin-left: 0; }
    .panel { padding: var(--sp-7) var(--sp-8); }
    .range-pills { display: none; }
    .range-select { display: flex; align-items: center; gap: var(--sp-3); flex: 1 1 auto; min-width: 0; }
    .range-select select { flex: 1 1 auto; min-width: 0; height: var(--tap-floor); }
    .range-select .btn { flex: 0 0 auto; min-height: var(--tap-floor); }
    .range-custom label.check { flex: 1 1 0; min-width: 0; }
    .range-custom .btn { flex: 1 1 100%; }

    .auth-main { justify-content: flex-start; padding: var(--sp-11) var(--sp-7); }
    .auth-card { padding: var(--sp-11) var(--sp-10) var(--sp-10); }
}

@media (prefers-reduced-motion: reduce) {
    .sidebar, .nav-backdrop { transition: none !important; }
}

@media print {
    .app > .sidebar, .appbar, .page-actions, .nav-backdrop, .skip-link { display: none !important; }
    .app { display: block; height: auto; overflow: visible; }
    .content { overflow: visible; }
}

/* ======================================================================
   INCIDENT LISTS (app/templates/incident_lists/list.html). The name and
   "counts as" boxes in an editable list keep a usable width; on a narrow
   screen the table scrolls inside .table-scroll instead of crushing them.
   ====================================================================== */
.list-edit input, .list-edit select { min-width: var(--field-min); }

/* ======================================================================
   CORRECTIVE ACTIONS (app/templates/actions/*, and the actions panel on
   incidents/detail.html). A due date that has passed while the work is
   not done reads in the danger colour; so does the Overdue figure on the
   list when it is not zero. Adapted from Pulse EHS style.css .overdue and
   .stat-alert @ aa321d3, with tokens.
   ====================================================================== */
.overdue { color: var(--danger); font-weight: var(--fw-medium); }
.overdue .sub { color: var(--danger); }
.stat-alert .stat-num { color: var(--danger); }
/* The one key figure of a record (Bucket Check's average, Inventory's total): brand blue. */

/* ======================================================================
   PRE-USE INSPECTION (app/templates/pre_use/*, app/static/js/pre_use.js).
   Answers are radio buttons drawn as large choices: the whole box is the
   target, the chosen one is outlined in the accent, a chosen bad answer in
   the warning colour. Comment boxes are visible with no script; the
   script hides each until its bad answer is chosen ([hidden]).
   New for Convertus MES.
   ====================================================================== */
.pu-checklist { border: 0; margin: 0; padding: 0; min-width: 0; }
.pu-checklist:disabled { opacity: var(--opacity-disabled); }
.pu-group { border: 0; margin: 0; padding: 0; min-width: 0; }
.pu-group > legend {
    padding: 0; margin-top: var(--sp-8);
    font-size: var(--fs-label); font-weight: var(--fw-bold); text-transform: uppercase;
    letter-spacing: var(--ls-label); color: var(--muted);
}
.pu-question {
    border: 0; margin: 0; min-width: 0;
    padding: var(--sp-7) 0; border-bottom: 1px solid var(--line-soft);
}
.pu-legend { padding: 0; margin-bottom: var(--sp-3); font-weight: var(--fw-medium); color: var(--heading); font-size: var(--fs-base); }
.pu-number { padding: var(--sp-7) 0; border-bottom: 1px solid var(--line-soft); }
.choice-group { display: flex; flex-wrap: wrap; gap: var(--sp-3); }
.choice {
    flex: 1 1 var(--choice-min); min-width: 0;
    display: flex; align-items: center; gap: var(--sp-4);
    min-height: var(--choice-h); padding: var(--sp-3) var(--sp-7);
    border: var(--choice-line) solid var(--line); border-radius: var(--radius-md);
    background: var(--panel); cursor: pointer;
    font-size: var(--fs-base); font-weight: var(--fw-medium); color: var(--ink);
}
.choice:hover { background: var(--hover); }
.choice input[type=radio] { width: var(--choice-radio); height: var(--choice-radio); margin: 0; cursor: pointer; }
.choice:has(input:checked) { border-color: var(--accent); background: var(--accent-soft); }
.choice-bad:has(input:checked) { border-color: var(--warn); background: var(--warn-soft); }
.choice:has(input:focus-visible) { outline: var(--outline-w) solid var(--accent); outline-offset: var(--outline-w); }
.pu-comment { display: flex; flex-direction: column; margin-top: var(--sp-5); }
.pu-comment label { font-weight: var(--fw-medium); font-size: var(--fs-field); margin-bottom: var(--sp-2); }
.pu-flag-note { margin: var(--sp-3) 0 0; display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; }
.pu-summary { border-left: var(--marker-w) solid var(--warn); background: var(--warn-soft); }
.pu-summary-list { margin: 0 0 var(--sp-3); padding: 0; list-style: none; display: flex; gap: var(--sp-3); flex-wrap: wrap; }
.pu-file-inputs { display: flex; flex-direction: column; gap: var(--sp-4); }
/* Camera-first file slot (Chris, 12 Oct 2026): the whole label is one big
   tap target with a camera icon; the native input stays inside it, so the
   picker opens on a tap anywhere, the chosen name shows, and no script is
   needed. On phones the browser's own sheet offers the camera first. */
.pu-file { display: flex; flex-direction: column; gap: var(--sp-2); padding: var(--sp-4) var(--sp-5);
    border: 2px dashed var(--line); border-radius: var(--radius); background: var(--panel); cursor: pointer; }
.pu-file:focus-within { outline: 2px solid var(--accent); outline-offset: 2px; }
.pu-file-cta { display: flex; align-items: center; gap: var(--sp-3); min-height: var(--tap-floor);
    font-weight: var(--fw-medium); color: var(--accent); }
.pu-file-cta svg { width: 24px; height: 24px; flex: none; }
.pu-file input[type=file] { font-size: var(--fs-small); color: var(--muted); }
.pu-flags { margin: 0 0 var(--sp-3); display: flex; gap: var(--sp-3); flex-wrap: wrap; }
.pu-files { display: flex; gap: var(--sp-7); flex-wrap: wrap; align-items: flex-start; }
.pu-file-card { margin: 0; display: flex; flex-direction: column; gap: var(--sp-2); max-width: 100%; }
.pu-thumb { display: block; width: var(--thumb-w); max-width: 100%; height: auto; border: 1px solid var(--line); border-radius: var(--radius); }
.pu-video { display: block; width: var(--video-w); max-width: 100%; border-radius: var(--radius); background: var(--ink); }
.grid tr.row-flag td { background: var(--warn-soft); }
.list-edit input.input-num { min-width: var(--num-w); width: var(--num-w); }
.list-edit input[type=checkbox] { min-width: 0; }

/* ======================================================================
   MENU SECTIONS: collapsible headings in the rail and the phone drawer.
   Each module section and Platform is a <details class="nav-section">
   whose <summary> is the heading, so it opens and closes with no script
   and a screen reader hears it as expanded or collapsed. The section of
   the page in view is rendered open; the rest load closed every time.
   A closed section's heading carries the sum of its modules' badges
   (.nav-sum), hidden once it is open and each module shows its own.
   ====================================================================== */
.nav-summary {
    display: flex; align-items: center; gap: var(--sp-4);
    min-height: var(--nav-row-h);
    padding: var(--sp-2) var(--sp-8);
    color: var(--rail-strong); font-weight: var(--fw-medium);
    border-left: var(--marker-w) solid transparent;
    cursor: pointer;
    list-style: none;
    user-select: none;
}
.nav-summary::-webkit-details-marker { display: none; }
.nav-summary:hover { background: var(--rail-hover); color: var(--rail-strong); }
.sidebar .nav-summary:focus-visible { outline-offset: calc(-1 * var(--outline-w)); }
.nav-group-label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nav-sum { display: inline-flex; flex: 0 0 auto; }
.nav-section[open] > .nav-summary .nav-sum { display: none; }
.nav-chevron { color: var(--rail-ink-dim); transform: rotate(var(--chevron-shut)); }
.nav-summary:hover .nav-chevron { color: var(--rail-strong); }
.nav-section[open] > .nav-summary .nav-chevron { transform: none; }
.nav-section[open] { padding-bottom: var(--sp-2); }
.nav-section > .nav-link {
    min-height: var(--nav-sub-h);
    padding-left: var(--nav-indent);
    color: var(--rail-ink); font-weight: var(--fw-regular);
}
.nav-section > .nav-link:hover { color: var(--rail-strong); }
.nav-section > .nav-link.active, .nav-section > .nav-link[aria-current] { color: var(--rail-strong); }
/* The heading of the section holding the page in view: its icon in the
   marker colour; folded shut, it takes the active page's marker too, so
   the page in view is never lost. */
.nav-section[data-nav-current] > .nav-summary .nav-ico { color: var(--rail-marker); }
.nav-section[data-nav-current]:not([open]) > .nav-summary {
    background: var(--rail-active);
    border-left-color: var(--rail-marker);
}

/* Site settings and Platform: at the foot of the menu when there is room
   (pushed down by margin-top: auto; the menu scrolls when there is not),
   each below a divider. Site settings is one link (Chris, 14 Oct 2026),
   current on the Settings page and every setup page. Platform covers every
   site, so its heading is in the marker colour with a globe and an "All
   sites" tag (blueprint 5.4). */
.nav-admin { margin-top: auto; padding-top: var(--sp-7); }
.nav-admin > .nav-section,
.nav-admin > .nav-admin-link {
    border-top: 1px solid var(--rail-line);
    padding-top: var(--sp-2);
}
.nav-admin > * + * { margin-top: var(--sp-2); }
.nav-section-platform > .nav-summary,
.nav-section-platform > .nav-summary:hover { color: var(--rail-marker); }
.nav-scope {
    flex: 0 0 auto;
    padding: 0 var(--sp-3);
    border: 1px solid currentColor; border-radius: var(--radius-pill);
    font-size: var(--fs-micro); font-weight: var(--fw-bold);
    text-transform: uppercase; letter-spacing: var(--ls-label);
    line-height: var(--nav-count-h);
}

@media (max-width: 720px) {
    /* The drawer: every row at least 44px for gloves (--tap-floor, 5.8). */
    .nav-summary { min-height: var(--tap-floor); padding-top: var(--sp-3); padding-bottom: var(--sp-3); font-size: var(--fs-panel); }
    .nav-section > .nav-link { min-height: var(--tap-floor); }
}

/* ======================================================================
   WEIGHPOINT (app/templates/weighpoint/*, app/static/js/weighpoint.js).
   The comparison bar is two <progress> elements, behind (drawn from the
   middle leftwards) and ahead (from the middle rightwards), so the server
   sets them with value="" and no inline style is needed (the CSP).
   Ahead and behind use the status tokens, which await Convertus sign-off
   (11(d)). New for Convertus MES.
   ====================================================================== */
.wp-datebar { max-width: none; align-items: center; justify-content: space-between; }
/* One row: Previous, the date, Next. The date grows on a phone and the two
   buttons never stretch, so it stays one row at 390px. Show and Today sit
   apart; Show hides once the script submits the form on change. */
.wp-dayrow { display: flex; align-items: center; gap: var(--sp-3); flex: 1 1 auto; min-width: 0; max-width: var(--search-max); }
.wp-dayrow .btn { flex: 0 0 auto; }
.wp-dayrow input[type=date] { flex: 1 1 var(--date-w); min-width: 0; }
.wp-dayextra { display: flex; align-items: center; gap: var(--sp-3); }
.wp-day { margin: 0 0 var(--sp-7); }
.wp-compare-grid { display: flex; gap: var(--sp-7); flex-wrap: wrap; margin-bottom: var(--sp-9); }
.wp-compare { flex: 1 1 var(--wp-compare-min); min-width: 0; margin-bottom: 0; }
.wp-bar { display: flex; margin: var(--sp-3) 0 var(--sp-7); }
.wp-bar progress {
    flex: 1 1 0; min-width: 0; height: var(--wp-bar-h); margin: 0;
    appearance: none; -webkit-appearance: none;
    border: 1px solid var(--line); background: var(--hover); color: var(--ok);
}
.wp-bar .wp-bar-behind { transform: scaleX(-1); border-right: 0; color: var(--danger); }
.wp-bar .wp-bar-ahead { border-left: var(--outline-w) solid var(--muted); }
.wp-bar progress::-webkit-progress-bar { background: var(--hover); }
.wp-bar .wp-bar-ahead::-webkit-progress-value { background: var(--ok); }
.wp-bar .wp-bar-behind::-webkit-progress-value { background: var(--danger); }
.wp-bar .wp-bar-ahead::-moz-progress-bar { background: var(--ok); }
.wp-bar .wp-bar-behind::-moz-progress-bar { background: var(--danger); }
.wp-status { font-weight: var(--fw-bold); margin: 0; }
.panel .wp-status-ahead { color: var(--ok); }
.panel .wp-status-behind { color: var(--danger); }
.panel .wp-status-equal { color: var(--muted); }
/* The trucks table on a phone: each truck is a stacked row (the ticket and
   the truck, then in, out and turnaround, then weight, zone and material)
   instead of six crushed columns. Above 720px it is the plain table.
   weighpoint.js builds the same cells with the same classes. */
@media (max-width: 720px) {
    .wp-trucks thead { display: none; }
    .wp-trucks tbody tr {
        display: grid; grid-template-columns: auto auto 1fr;
        grid-template-areas: "ticket truck ." "time time time" "net zone material";
        column-gap: var(--sp-6); row-gap: var(--sp-1);
        padding: var(--sp-4) var(--sp-6); border-bottom: 1px solid var(--line-soft);
    }
    .wp-trucks tbody tr:last-child { border-bottom: 0; }
    .wp-trucks tbody td { display: block; padding: 0; border: 0; text-align: left; }
    .wp-trucks .wp-ticket { grid-area: ticket; }
    .wp-trucks .wp-truck { grid-area: truck; }
    .wp-trucks .wp-time { grid-area: time; }
    .wp-trucks .wp-net { grid-area: net; }
    .wp-trucks .wp-zone { grid-area: zone; }
    .wp-trucks .wp-material { grid-area: material; }
    .wp-trucks .wp-time .sub { display: inline; margin: 0; }
    .wp-trucks .wp-time .sub::before { content: " · "; }
    .wp-trucks td[data-label]::before { content: attr(data-label) " "; color: var(--muted); font-size: var(--fs-small); }
    .wp-trucks tr.row-flag { background: var(--warn-soft); }
    .wp-trucks tr.row-flag td { background: transparent; }
}

/* ======================================================================
   OBSERVATIONS (app/templates/observations/*). Safe and Unsafe are the
   Pre-Use answer boxes (.choice), each with a line saying what it means.
   The list is a plain table above 720px; on a phone each observation is
   one stacked row (the reference and its pills, then when, then what was
   seen, then the area and who), never a table that scrolls sideways
   (13(j) rule 7). Actions and the trail on a record are a plain list.
   New for Convertus MES.
   ====================================================================== */
.obs-type { align-items: flex-start; }
/* Observations' answers have longer labels than Pre-Use's Yes or No, so
   their boxes start wider, take the form's control size and wrap inside
   the box: a word too long for its box breaks rather than spill out. */
.obs-form .choice {
    flex-basis: var(--obs-choice-min); padding: var(--sp-3) var(--sp-5);
    font-size: var(--fs-control);
}
.obs-form .choice > span { min-width: 0; overflow-wrap: anywhere; }
.obs-form .choice input[type=radio] { flex: none; }
.obs-blurb { display: block; font-weight: var(--fw-regular); }
.obs-actions { list-style: none; margin: 0 0 var(--sp-7); padding: 0; }
.obs-actions > li { padding: var(--sp-4) 0; border-bottom: 1px solid var(--line-soft); }
.obs-actions > li:last-child { border-bottom: 0; }
.obs-ref .pill { margin-left: var(--sp-2); }
@media (max-width: 720px) {
    .obs-list thead { display: none; }
    .obs-list tbody tr {
        display: grid; grid-template-columns: 1fr auto;
        grid-template-areas: "ref ref" "when when" "text text" "area by";
        column-gap: var(--sp-6); row-gap: var(--sp-1);
        padding: var(--sp-4) var(--sp-6); border-bottom: 1px solid var(--line-soft);
    }
    .obs-list tbody tr:last-child { border-bottom: 0; }
    .obs-list tbody td { display: block; padding: 0; border: 0; }
    .obs-list .obs-ref { grid-area: ref; }
    .obs-list .obs-ref .ref { display: inline-flex; align-items: center; min-height: var(--tap-floor); }
    .obs-list .obs-when { grid-area: when; color: var(--muted); font-size: var(--fs-small); }
    .obs-list .obs-text { grid-area: text; }
    .obs-list .obs-area { grid-area: area; color: var(--muted); font-size: var(--fs-small); }
    .obs-list .obs-by { grid-area: by; color: var(--muted); font-size: var(--fs-small); text-align: right; }
    .obs-list tr.row-off { background: var(--row-off-bg); }
    .obs-list tr.row-off td { background: transparent; }
    /* A record's answers: the question above its answer, full width, so a
       long question never squeezes the answer into a narrow column. */
    .obs-answers { grid-template-columns: 1fr; row-gap: var(--sp-1); }
    .obs-answers dd { margin-bottom: var(--sp-4); }
}

/* People, grouped by site (Chris, 12 Oct 2026): one panel per site. */
.people-section + .people-section { margin-top: var(--sp-6); }
.people-section h2 { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; }
.people-section h2 .count { color: var(--muted); font-weight: var(--fw-regular); font-size: var(--fs-small); }
.people-section .empty-line { color: var(--muted); margin: 0; }

/* ======================================================================
   INCIDENTS: working on one and closing it (docs/plans/incident-flow.md).
   The work page is one form with one Save in a sticky bar that stays on
   screen, above the phone's home indicator (safe area). New for Convertus MES.
   ====================================================================== */
.work-cta .form-actions .btn-primary { min-height: var(--tap-floor); }
.why-list { margin: 0 0 var(--sp-6); padding-left: var(--sp-9); }
.why-list li + li { margin-top: var(--sp-1); }
.cause-removed { list-style: none; margin: 0 0 var(--sp-3); padding: 0; display: flex; flex-direction: column; gap: var(--sp-2); }
.cause-removed .pill { margin-left: var(--sp-2); }
.cause-remove { min-height: var(--tap-floor); margin-top: var(--sp-5); padding-top: var(--sp-4); border-top: 1px solid var(--line-soft); }
.cause-remove-hint { display: block; margin: 0; }
.cause-new { background: transparent; border-style: dashed; }
.cause-new .field + .field { margin-top: var(--sp-4); }
.cause-new-rows { gap: var(--sp-5); }
.ca-list { list-style: none; margin: 0 0 var(--sp-6); padding: 0; display: flex; flex-direction: column; gap: var(--sp-4); }
.ca-item { border: 1px solid var(--line-soft); border-radius: var(--radius-md); padding: var(--sp-4) var(--sp-6); overflow-wrap: anywhere; }
.ca-item.row-off { background: var(--row-off-bg); color: var(--muted); }
.ca-line { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; }
.ca-line .ref { display: inline-flex; align-items: center; min-height: var(--tap-floor); }
/* One line per item with a hairline between, the mark in a fixed column so
   the eye runs down Done / Missing without reading the words. */
.checklist { list-style: none; margin: 0 0 var(--sp-6); padding: 0; display: flex; flex-direction: column; }
.checklist-item { display: flex; align-items: center; gap: var(--sp-6); padding: var(--sp-4) 0; }
.checklist-item + .checklist-item { border-top: 1px solid var(--line-soft); }
.checklist-item.is-blocking { font-weight: var(--fw-medium); }
.checklist-item .pill { flex: 0 0 var(--checklist-pill-w); justify-content: center; text-align: center; }
.unsaved { color: var(--warn); font-weight: var(--fw-medium); margin-left: var(--sp-2); }
.save-bar {
    /* .content is the scroller on a laptop and its padding would leave a
       strip of page under a bottom:0 bar, so the bar sits into it. */
    position: sticky; bottom: calc(-1 * var(--sp-10)); z-index: var(--z-appbar);
    display: flex; flex-direction: column; gap: var(--sp-3);
    background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius-lg);
    padding: var(--sp-5) var(--sp-7);
    padding-bottom: calc(var(--sp-5) + env(safe-area-inset-bottom, 0px));
    box-shadow: var(--shadow-bar);
}
.save-bar .btn { min-height: var(--tap-floor); }
@media (max-width: 720px) {
    /* Edge to edge on a phone, the buttons full width in one row where
       they fit, Save widest. */
    .save-bar {
        bottom: 0; margin: 0 calc(-1 * var(--sp-7)); border-radius: 0; border-left: 0; border-right: 0;
        padding-left: var(--sp-7); padding-right: var(--sp-7);
    }
    .save-bar .form-actions .btn { flex: 1 1 auto; }
}

/* A section's own Edit link on an incident's read view (Chris, 12 Oct 2026):
   jumps to that part of the one work page, which keeps one Save. */
.section-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-4); }
.section-head h2 { margin-bottom: 0; }
.section-head + * { margin-top: var(--sp-4); }
.section-edit { display: inline-flex; align-items: center; min-height: var(--tap-floor); padding: 0 var(--sp-3);
    font-weight: var(--fw-medium); }

/* ======================================================================
   SECTION LEADER: the read-only notice on record pages
   (docs/plans/section-leader.md; app/templates/base.html)
   ====================================================================== */
.read-only-note {
    display: flex; align-items: center; gap: var(--sp-3);
    background: var(--info-soft); border: 1px solid var(--line); color: var(--info-ink);
    padding: var(--sp-4) var(--sp-6); border-radius: var(--radius);
    margin: 0 0 var(--sp-8);
}
/* A setup page managed by its section's leaders (schema 020; Chris, 14 Oct
   2026), for a site Admin who does not lead it: the same notice (above),
   and a question or card shown in a disabled fieldset instead of a form.
   On the Settings page, the section's card says who manages it. */
.read-only-fields { border: 0; margin: 0; padding: 0; min-width: 0; }
.settings-card-note {
    display: flex; align-items: center; gap: var(--sp-3);
    margin: 0; padding: var(--sp-4) var(--sp-9);
    font-size: var(--fs-small); color: var(--info-ink); background: var(--info-soft);
    border-bottom: 1px solid var(--line-soft);
}
.settings-card-note .icon-inline { color: var(--info-ink); }

/* ======================================================================
   SCADAVIEW (docs/plans/scadaview.md section 8; templates/plant/)
   The live board's tiles, the water tanks, the status line, the tabs and
   the history charts. A tile's state colours its top edge and its main
   reading: low info blue, ok the status green, high warn amber, alarm
   danger red, idle and none neutral. Charts are inline SVG drawn by
   scada_chart.js; their colours are these classes, never attributes.
   ====================================================================== */
.badge-warn {
    display: inline-block; font-size: var(--fs-label); font-weight: var(--fw-bold);
    padding: var(--sp-1) var(--sp-4); border-radius: var(--radius-pill);
    background: var(--warn-soft); color: var(--warn); border: 1px solid var(--warn-line);
    text-transform: uppercase; letter-spacing: var(--ls-label); white-space: nowrap;
}
.sc-statusbar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-3) var(--sp-7); margin-bottom: var(--sp-7); }
.sc-status {
    display: inline-flex; align-items: center; gap: var(--sp-3);
    font-weight: var(--fw-bold); letter-spacing: var(--ls-label); font-size: var(--fs-field);
    padding: var(--sp-2) var(--sp-5); border-radius: var(--radius-pill); background: var(--neutral-soft); color: var(--neutral-ink);
}
.sc-dot { width: var(--sc-dot); height: var(--sc-dot); border-radius: 50%; background: currentColor; }
.sc-status-live { background: var(--ok-soft); color: var(--ok); }
.sc-status-stale { background: var(--danger-soft); color: var(--danger); }
.sc-updated { color: var(--muted); font-size: var(--fs-field); font-variant-numeric: tabular-nums; }
.sc-note { color: var(--danger); font-size: var(--fs-small); }
.sc-stale-note { margin-bottom: var(--sp-7); }
.sc-board.is-stale .sc-panel { opacity: var(--sc-stale-opacity); }

.sc-tabs { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-bottom: var(--sp-8); border-bottom: 1px solid var(--line); }
.sc-tab {
    display: inline-flex; align-items: center; min-height: var(--tap);
    padding: var(--sp-3) var(--sp-7); color: var(--neutral-ink); font-weight: var(--fw-medium);
    border-bottom: var(--marker-w) solid transparent; margin-bottom: -1px; white-space: nowrap;
}
.sc-tab:hover { color: var(--accent-dark); text-decoration: none; border-bottom-color: var(--line); }
.sc-tab[aria-current] { color: var(--heading); border-bottom-color: var(--accent); font-weight: var(--fw-bold); }

.sc-grid {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(var(--sc-tile-min), 1fr));
    gap: var(--sp-7); margin-bottom: var(--sp-9);
}
.sc-tile {
    min-width: 0;
    background: var(--panel); border: 1px solid var(--line); border-top: var(--sc-state-w) solid var(--line);
    border-radius: var(--radius-lg); padding: var(--sp-7) var(--sp-8);
}
.sc-grid-tanks { grid-template-columns: repeat(auto-fill, minmax(var(--sc-tank-min), 1fr)); }
.sc-tile-head { display: flex; flex-direction: column; align-items: flex-start; gap: var(--sp-3); margin-bottom: var(--sp-3); }
.sc-tile-title { margin: 0; font-size: var(--fs-panel); white-space: nowrap; }
.sc-tile-title a { color: var(--heading); }
.sc-pills { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); min-height: var(--sp-10); }
.sc-main { font-size: var(--fs-sc-main); font-weight: var(--fw-bold); line-height: var(--lh-tight); color: var(--heading); font-variant-numeric: tabular-nums; white-space: nowrap; }
.sc-main-unit { font-size: var(--fs-stat-unit); font-weight: var(--fw-medium); color: var(--muted); margin-left: var(--sp-2); }
.sc-main-label { font-size: var(--fs-label); text-transform: uppercase; letter-spacing: var(--ls-label); color: var(--muted); margin: var(--sp-1) 0 var(--sp-5); }
.sc-stats { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-3) var(--sp-7); margin: 0; }
.sc-stats-one { grid-template-columns: minmax(0, 1fr); }
.sc-stat { min-width: 0; }
.sc-stat dt { font-size: var(--fs-label); color: var(--muted); }
.sc-stat dd { margin: 0; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
.sc-stat-alarm dd { color: var(--danger); font-weight: var(--fw-bold); }
.sc-stat-warn dd { color: var(--warn); font-weight: var(--fw-bold); }

.sc-state-ok { border-top-color: var(--ok); }
.sc-state-ok .sc-main-value { color: var(--ok); }
.sc-state-low { border-top-color: var(--info); }
.sc-state-low .sc-main-value { color: var(--info-ink); }
.sc-state-high { border-top-color: var(--warn); }
.sc-state-high .sc-main-value { color: var(--warn); }
.sc-state-alarm { border-top-color: var(--danger); }
.sc-state-alarm .sc-main-value { color: var(--danger); }
.sc-state-idle { border-top-color: var(--neutral-ink); }
.sc-state-idle .sc-main-value, .sc-state-none .sc-main-value { color: var(--neutral-ink); }
.sc-state-neutral { border-top-color: var(--accent-dark); }

.sc-tank-body { display: flex; gap: var(--sp-8); align-items: flex-start; }
.sc-tank-svg { flex: 0 0 var(--sc-tank-w); width: var(--sc-tank-w); height: var(--sc-tank-h); }
.sc-tank-shell { fill: var(--hover); stroke: var(--line); stroke-width: 1; }
.sc-tank-fill { fill: var(--sc-tank-fill); }
.sc-state-high .sc-tank-fill { fill: var(--warn); }
.sc-tank-mark { stroke: var(--warn); stroke-width: 1.5; stroke-dasharray: 3 2; }
.sc-tank-figs { flex: 1 1 auto; min-width: 0; }
.sc-subhead { font-size: var(--fs-control); color: var(--heading); margin: 0 0 var(--sp-4); }
.sc-light { display: inline-block; width: var(--sc-dot); height: var(--sc-dot); border-radius: 50%; background: var(--line); }
.sc-state-ok .sc-light { background: var(--ok); }
.sc-state-alarm .sc-light { background: var(--danger); }
.sc-state-idle .sc-light { background: var(--neutral-ink); }
.sc-light-text { font-size: var(--fs-small); font-weight: var(--fw-medium); color: var(--neutral-ink); }
.sc-state-ok .sc-light-text { color: var(--ok); }
.sc-state-alarm .sc-light-text { color: var(--danger); }
.sc-valves { list-style: none; margin: 0 0 var(--sp-9); padding: 0; display: flex; flex-wrap: wrap; gap: var(--sp-3); }
.sc-valve {
    display: inline-flex; align-items: center; gap: var(--sp-3);
    background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius-pill);
    padding: var(--sp-2) var(--sp-6);
}
.sc-valve-name { font-weight: var(--fw-bold); font-variant-numeric: tabular-nums; }
.sc-more { margin: 0 0 var(--sp-9); }
.sc-other { margin-bottom: var(--sp-9); }
.sc-other-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(var(--field-min), 1fr)); gap: var(--sp-3) var(--sp-9); margin: var(--sp-5) 0 0; }
.sc-other-list dt { font-size: var(--fs-small); color: var(--muted); overflow-wrap: anywhere; }
.sc-other-list dd { margin: 0; font-variant-numeric: tabular-nums; }

.sc-detail { display: flex; flex-wrap: wrap; gap: var(--sp-9); align-items: flex-start; }
.sc-detail-tile { flex: 0 1 var(--sc-detail-tile); min-width: 0; display: flex; }
.sc-detail-charts { flex: 1 1 calc(2 * var(--sc-detail-tile)); min-width: 0; }
.sc-ranges { margin-bottom: var(--sp-3); }
.sc-chart { margin: 0 0 var(--sp-9); }
.sc-chart-title { font-weight: var(--fw-bold); color: var(--heading); margin-bottom: var(--sp-4); }
.sc-chart-canvas { height: var(--sc-chart-h); }
.sc-svg { display: block; width: 100%; height: 100%; touch-action: pan-y; }
.sc-grid-line { stroke: var(--line-soft); stroke-width: 1; }
.sc-axis { fill: var(--muted); font-size: var(--fs-micro); font-family: var(--font-body); }
.sc-band { fill: var(--sc-band-fill); }
.sc-ref { stroke: var(--warn); stroke-width: 1.5; stroke-dasharray: var(--sc-dash); }
.sc-line { fill: none; stroke-width: var(--sc-line-w); stroke-linejoin: round; stroke-linecap: round; }
.sc-line-s1 { stroke: var(--sc-s1); }
.sc-line-s2 { stroke: var(--sc-s2); }
.sc-line-s3 { stroke: var(--sc-s3); }
.sc-line-s4 { stroke: var(--sc-s4); }
.sc-line-s5 { stroke: var(--sc-s5); stroke-dasharray: var(--sc-dash); }
.sc-range { fill: var(--sc-range-fill); stroke: none; }
.sc-cursor { stroke: var(--muted); stroke-width: 1; }
.sc-legend { display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-8); margin-top: var(--sp-4); font-size: var(--fs-small); color: var(--ink); }
.sc-key { display: inline-flex; align-items: center; gap: var(--sp-3); }
.sc-key::before { content: ""; display: inline-block; width: var(--sp-10); height: var(--sc-line-w); background: var(--muted); }
.sc-key-s1::before { background: var(--sc-s1); }
.sc-key-s2::before { background: var(--sc-s2); }
.sc-key-s3::before { background: var(--sc-s3); }
.sc-key-s4::before { background: var(--sc-s4); }
.sc-key-s5::before { background: var(--sc-s5); }
.sc-key-band::before { height: var(--sp-5); background: var(--sc-band-fill); border: 1px solid var(--ok-line); }
.sc-key-line::before { background: var(--warn); }
.sc-chart-note { min-height: calc(var(--fs-small) * var(--lh-body)); margin: var(--sp-3) 0 0; font-variant-numeric: tabular-nums; }
.sc-band-set { border: 0; padding: 0; margin: 0; }
.sc-band-set legend { font-weight: var(--fw-bold); color: var(--heading); margin-bottom: var(--sp-3); padding: 0; }

/* The boardroom screen (display link): a navy band like the rail, then
   the board at full width. */
.sc-display-page { background: var(--bg); min-height: 100%; display: flex; flex-direction: column; }
.sc-display { flex: 1 0 auto; width: 100%; max-width: var(--sc-display-max); margin: 0 auto; padding: 0 var(--sp-10) var(--sp-10); }
.sc-display-head {
    display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-7) var(--sp-12);
    background: var(--rail); color: var(--rail-text);
    margin: 0 calc(-1 * var(--sp-10)) var(--sp-9); padding: var(--sp-8) var(--sp-10);
}
.sc-display-brand .brand-logo { width: var(--logo-rail-w); height: var(--logo-rail-h); display: block; }
.sc-display-brand .site-suffix { color: var(--rail-text); }
.sc-display-title { color: var(--rail-text); margin: 0; font-size: var(--fs-heading); }
.sc-display-title .opt { color: var(--rail-ink-dim); font-weight: var(--fw-regular); font-size: var(--fs-base); }
.sc-display .sc-main { font-size: var(--fs-sc-display-main); }
.sc-display .sc-grid { grid-template-columns: repeat(auto-fill, minmax(var(--sc-tile-min-display), 1fr)); }
.sc-display-foot { flex: 0 0 auto; padding: 0 var(--sp-10) var(--sp-8); }

/* Platform: a token or address shown once. */
.sc-issued { border-left: var(--marker-w) solid var(--warn); }
.sc-secret code, .sc-cmd code {
    display: block; padding: var(--sp-5) var(--sp-7); background: var(--hover);
    border: 1px solid var(--line); border-radius: var(--radius); overflow-wrap: anywhere;
    user-select: all;
}
.sc-cmd { margin: 0 0 var(--sp-5); white-space: pre-wrap; }

@media (max-width: 720px) {
    .sc-tabs { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: thin; }
    .sc-tab { min-height: var(--tap-floor); }
    .sc-grid, .sc-grid-tanks { grid-template-columns: minmax(0, 1fr); }
    .sc-main { font-size: var(--fs-sc-main-touch); }
    .sc-ranges { display: flex; }
    .sc-ranges .range-pill { min-height: var(--tap-floor); display: inline-flex; align-items: center; }
    .sc-chart-canvas { height: var(--sc-chart-h-touch); }
    .sc-detail-tile { flex-basis: 100%; }
    .sc-display { padding: 0 var(--sp-7) var(--sp-8); }
    .sc-display-head { margin: 0 calc(-1 * var(--sp-7)) var(--sp-8); padding: var(--sp-7); }
    .sc-display .sc-main { font-size: var(--fs-sc-main-touch); }
}

/* ======================================================================
   BUCKET CHECK (app/templates/bucket_check/*; docs/plans/bucket-check.md).
   The landing page is in the compact list style (COMPACT LIST, below):
   "Latest by fraction" (key figures and a dense table) and "History"
   (its filter row and a dense table); dense lists on a phone. The rules here are the
   check's own pages and form. New for Convertus MES.
   ====================================================================== */
.bc-keep { white-space: nowrap; }
.bc-unit-set { border: 0; margin: 0; padding: 0; min-width: 0; }
.bc-unit-set > legend { padding: 0; margin-bottom: var(--sp-3); font-weight: var(--fw-medium); color: var(--heading); }
.bc-unit-set .choice { min-height: var(--tap-floor); }
.list-edit.bc-areas select { min-width: var(--field-min); }
/* The new-check page's Today strip: a line, a slim bar in the status
   green on a quiet track, then a chip per fraction. Chips, not cards, so
   it never looks like the landing page: done is a green chip with its
   average; not yet is an outlined link that picks the fraction. */
.bc-today { display: flex; flex-direction: column; gap: var(--sp-4); }
.bc-today-line { margin: 0; color: var(--ink); }
.bc-today-line strong { color: var(--ok); }
.bc-today-bar { -webkit-appearance: none; appearance: none; display: block; width: 100%; height: var(--progress-h); border: 0; border-radius: var(--radius-sm); background: var(--line-soft); overflow: hidden; }
.bc-today-bar::-webkit-progress-bar { background: var(--line-soft); border-radius: var(--radius-sm); }
.bc-today-bar::-webkit-progress-value { background: var(--ok); border-radius: var(--radius-sm); }
.bc-today-bar::-moz-progress-bar { background: var(--ok); border-radius: var(--radius-sm); }
.bc-chips { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--sp-3); }
/* Same corners as the mt | % switch: slightly rounded, not pills (Chris, 15 Oct 2026). */
.bc-chip { display: inline-flex; align-items: center; gap: var(--sp-2); min-height: var(--tap-floor); padding: 0 var(--sp-5); border-radius: var(--radius); border: 1px solid var(--line); font-weight: var(--fw-medium); }
.bc-chip-done { background: var(--ok-soft); border-color: var(--ok-line); color: var(--ok); }
.bc-chip-avg { font-weight: var(--fw-bold); font-variant-numeric: tabular-nums; }
.bc-chip-todo { background: var(--panel); border-color: var(--check-line); color: var(--ink); text-decoration: none; }
.bc-chip-todo:hover { border-color: var(--accent); color: var(--accent); background: var(--hover); text-decoration: none; }
/* The new-check form: the three weights in numbered boxes, side by side on
   a laptop, stacked on a phone (below); the live average under them. */
.bc-weights { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--sp-6); margin: var(--sp-6) 0; counter-reset: bc-weight; }
.bc-weights .pu-number { padding: var(--sp-6) var(--sp-7); border: 1px solid var(--line); border-top: var(--marker-w) solid var(--accent); border-radius: var(--radius); background: var(--panel); min-width: 0; }
.bc-weights .pu-number > label:first-child { display: flex; align-items: center; gap: var(--sp-4); color: var(--heading); }
.bc-weights .pu-number > label:first-child::before {
    counter-increment: bc-weight; content: counter(bc-weight); content: counter(bc-weight) / "";
    flex: none; display: inline-flex; align-items: center; justify-content: center;
    width: var(--step-badge); height: var(--step-badge); border-radius: var(--radius-pill);
    background: var(--accent); color: var(--on-accent); font-weight: var(--fw-bold); font-variant-numeric: tabular-nums;
}
.bc-weights .pu-number input { font-size: var(--fs-base); font-variant-numeric: tabular-nums; }
.bc-weights .pu-number > :last-child { margin-bottom: 0; }
.bc-live { margin: 0 0 var(--sp-6); padding: var(--sp-4) var(--sp-6); background: var(--info-soft); color: var(--info-ink); font-weight: var(--fw-medium); border-radius: var(--radius); font-variant-numeric: tabular-nums; }
.bc-live[hidden] { display: none; }
@media (max-width: 720px) {
    .bc-weights { grid-template-columns: minmax(0, 1fr); }
    /* The areas table on a phone: one stacked row per area, each cell
       labelled, the order and action buttons on their own line. */
    .bc-areas thead { display: none; }
    .bc-areas tbody tr { display: flex; flex-direction: column; gap: var(--sp-3); padding: var(--sp-5) 0; border-bottom: 1px solid var(--line-soft); }
    .bc-areas td { border: 0; padding: 0; }
    .bc-areas td[data-label]::before { content: attr(data-label); display: block; color: var(--muted); font-size: var(--fs-small); margin-bottom: var(--sp-1); }
    .bc-areas td.actions { text-align: left; white-space: normal; }
    .list-edit.bc-areas input, .list-edit.bc-areas select { width: 100%; min-width: 0; }
}

/* ======================================================================
   INVENTORY (app/templates/inventory/*; docs/plans/inventory.md).
   The landing page is in the compact list style (COMPACT LIST, below):
   "On site now" as key figures, the whole strip opening the count
   (.rec-tap), the latest count's areas as a dense table folded under it
   (.dt-fold, closed), then History as a dense table. A count's own page is in
   the compact record style (COMPACT RECORD, below), its areas the same
   dense table (inventory/_rows.html). The count form is one row per area: its name, its facts small, and a small
   mt | % switch beside the value box. The switch is a radio pair (no
   script needed): the radios are hidden but stay focusable, and their
   labels are the two halves, the checked one filled with the accent.
   44 px tall for gloves. New for Convertus MES.
   ====================================================================== */
/* Each area is its own box; its left edge is blue (.edge-info), green once a value is in (inventory.js). */
.inv-row { margin: 0 0 var(--sp-4); padding: var(--sp-5) var(--sp-7); min-width: 0; border: 1px solid var(--line); border-radius: var(--radius); background: var(--panel); }
.inv-row.is-filled { border-left-color: var(--ok); }
.inv-row.inv-row-error { border-left-color: var(--danger); }
.inv-row > legend { float: left; width: 100%; padding: 0; font-weight: var(--fw-medium); color: var(--heading); overflow-wrap: anywhere; }
.inv-row > legend + * { clear: both; }
.inv-row-facts { margin-bottom: var(--sp-3); }
.inv-entry { display: flex; align-items: center; gap: var(--sp-5); flex-wrap: wrap; }
.inv-seg { position: relative; display: inline-flex; flex: none; border: 1px solid var(--accent); border-radius: var(--radius); overflow: hidden; }
.inv-seg .inv-seg-input { position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0; }
.inv-seg label {
    min-width: var(--inv-seg-w); min-height: var(--tap-floor);
    display: inline-flex; align-items: center; justify-content: center;
    padding: 0 var(--sp-5); margin: 0; cursor: pointer;
    font-size: var(--fs-control); font-weight: var(--fw-medium);
    color: var(--accent); background: var(--panel);
}
.inv-seg label + .inv-seg-input + label { border-left: 1px solid var(--accent); }
.inv-seg .inv-seg-input:checked + label { background: var(--accent); color: var(--on-accent); font-weight: var(--fw-bold); }
.inv-seg .inv-seg-input:focus-visible + label { outline: var(--outline-w) solid var(--accent); outline-offset: calc(-2 * var(--outline-w)); box-shadow: inset 0 0 0 var(--focus-w) var(--accent-soft); }
.inv-seg .inv-seg-input:disabled + label { color: var(--muted); background: var(--hover); cursor: not-allowed; opacity: var(--opacity-disabled); }
.inv-entry input.inv-value { width: var(--inv-value-w); height: var(--tap-floor); font-variant-numeric: tabular-nums; }
.inv-preview { display: block; margin-top: var(--sp-2); font-weight: var(--fw-medium); color: var(--heading); font-variant-numeric: tabular-nums; }
.inv-preview[hidden] { display: none; }
.inv-note { margin: var(--sp-2) 0 0; }
.inv-row-error .inv-value { border-color: var(--danger); }
@media (max-width: 720px) {
    .inv-entry input.inv-value { flex: 1 1 var(--inv-value-w); width: auto; min-width: 0; }
}

/* ======================================================================
   RECORD ROWS. A list's card or row opens the record as a whole
   (.rec-tap with a stretched .rec-open link), so a past record is one tap from the module's page (Chris, 15 Oct 2026). Edit and Delete
   live only on the record's own pages: in the record page's toolbar, and
   Delete at the far end of the edit page's toolbar, for Admins; a deleted
   record's page carries a red message bar (.msgbar-bad, COMPACT RECORD)
   with Restore. Used by Bucket Check and Inventory. New for Convertus MES.
   ====================================================================== */
/* The whole card or row opens the record (Chris, 15 Oct 2026). Its title
   link (.rec-open) is stretched over the surface with ::after; Edit lives
   only on the record's own page. Focus draws round the whole surface. */
.rec-tap { position: relative; }
.rec-tap:hover { background: var(--hover); }
.rec-open::after { content: ""; position: absolute; inset: 0; border-radius: inherit; }
.rec-open:focus-visible { outline: none; }
.rec-open:focus-visible::after { outline: var(--outline-w) solid var(--accent); outline-offset: calc(-1 * var(--outline-w)); box-shadow: inset 0 0 0 var(--focus-w) var(--accent-soft); }

/* ======================================================================
   COMPACT LIST (the standard page style, Chris 16 Oct 2026; blueprint 11(e)).
   A denser, snappier look for list pages on a laptop, two densities in
   one markup: compact above 720px, comfortable touch sizes on a phone.
   Reference: Bucket Check's and Inventory's landing pages; every list page
   can adopt it with these classes and the macros in _macros.html
   (page_head, st, when_short):
     .ph          the page header, IN THE APPBAR (appbar_title, through
                  page_head): the title and a muted count line (.ph-count,
                  hidden on a phone). The title is never repeated in
                  content.
     .btn-dense   32 px buttons with 4 px corners for the appbar toolbar
                  and the filter row; the variant still says what it is
                  (one .btn-primary, quiet or secondary for the rest).
                  Back to touch size on a phone.
     .kpis        a strip of key figures (<dl>; .kpi > dt.kpi-l +
                  dd.kpi-v, .kpi-ok / .kpi-warn colour a figure). With
                  .rec-tap and a .rec-open link inside, the whole strip
                  opens a record.
     .searchbar-dense  the filter row, a .searchbar-wide GET form at the
                  dense height.
     .dt-caption  a section heading over key figures or a dense table
                  (section_head), .dt-caption-sub a muted note beside it.
     .dt-fold     a breakdown folded, closed, under its key figures (fold):
                  the summary in view, the detail one tap away.
     .dt          the dense table, in .table-scroll.dt-scroll: 13 px,
                  8 px cells, a sticky header, .n right-aligned figures in
                  the mono face, .dt-chev a chevron column, .dt-void a
                  deleted or missing row, .dt-note a small note,
                  .dt-line one line of several in a cell (an entry's
                  notes, its comment).
                  Rows that open a record: <tr class="dt-tap"
                  data-row-link> with the first cell's link .rec-open;
                  app.js follows that link on a click anywhere in the row
                  (a stretched link is not used on a laptop: a table row
                  is not a dependable containing block in every browser,
                  and the overlay would escape to the page). The link
                  is the keyboard stop.
                  On a phone each row is a two-line item: .dt-title and
                  .dt-fig on the first line, .dt-phone (a cell only phones
                  show, the details in one line) and .dt-stc (the status)
                  on the second; .dt-lap cells are laptop only. The row is
                  a grid there, so the stretched .rec-open link covers it
                  and the whole row is one tap of at least --tap-floor.
     .st          a status: a coloured dot and a word, .ok .warn .bad
                  .none (a hollow dot). The word always carries the state.
   .dt-scroll lets the table show past the wrapper rather than scroll
   sideways, so the header can stick under the appbar while the page
   scrolls; text cells wrap instead, so the table fits from 721px up and
   is never wider than its box. Below that it is a list.
   ====================================================================== */
.ph { display: inline-flex; align-items: baseline; gap: var(--sp-6); min-width: 0; max-width: 100%; }
.ph-title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ph-count {
    font-family: var(--font-body); font-size: var(--fs-dense); font-weight: var(--fw-regular);
    color: var(--muted); white-space: nowrap;
}
.btn-dense {
    min-height: var(--btn-h-dense); height: var(--btn-h-dense);
    padding: 0 var(--dt-pad-x); font-size: var(--fs-dense); font-weight: var(--fw-medium);
    border-radius: var(--r-dense);
}

.kpis {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(var(--kpi-min), 1fr));
    gap: var(--kpi-gap); margin: 0 0 var(--sp-8);
    background: var(--line-soft); border: 1px solid var(--line); border-radius: var(--r-dense);
    border-top: var(--accent-top-w) solid var(--accent); box-shadow: var(--shadow-1);
    overflow: hidden;
}
.kpi { display: flex; flex-direction: column; gap: var(--sp-1); min-width: 0; padding: var(--sp-4) var(--sp-8); background: var(--surface-tint); }
.kpi-l { margin: 0; font-size: var(--fs-label); text-transform: uppercase; letter-spacing: var(--ls-label); color: var(--muted); }
.kpi-v {
    margin: 0; font-family: var(--font-mono); font-size: var(--fs-kpi); font-weight: var(--fw-medium);
    line-height: var(--lh-tight); color: var(--ink); font-variant-numeric: tabular-nums;
    overflow-wrap: anywhere;
}
.kpi-v.kpi-ok { color: var(--ok); }
.kpi-v.kpi-warn { color: var(--warn); }
.kpi-sub { display: block; margin-top: var(--sp-1); font-family: var(--font-body); font-size: var(--fs-dense); font-weight: var(--fw-regular); color: var(--muted); }
.kpi-link { color: var(--accent-dark); }
.kpis.rec-tap:hover { border-color: var(--accent); }
.kpis.rec-tap:hover .kpi, .kpis.rec-tap:focus-within .kpi { background: var(--dt-hover); }
html:not(.no-js) .kpis.rec-tap { cursor: pointer; }

.searchbar-dense { margin-bottom: var(--sp-6); gap: var(--sp-3) var(--sp-6); }
.searchbar-dense label:not(.check) { font-size: var(--fs-dense); }
.searchbar-dense input:not([type=checkbox]):not([type=radio]):not([type=file]):not([type=submit]):not([type=button]):not([type=hidden]):not([type=range]):not([type=color]),
.searchbar-dense select {
    height: var(--btn-h-dense); padding-top: 0; padding-bottom: 0;
    font-size: var(--fs-dense); border-radius: var(--r-dense);
}

.dt-caption {
    display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--sp-3);
    margin: var(--sp-9) 0 var(--sp-3); font-size: var(--fs-panel); font-weight: var(--fw-medium);
    color: var(--heading); scroll-margin-top: var(--sp-8);
}
.content-body > .dt-caption:first-child,
.content-body > [id]:first-child > .dt-caption:first-child { margin-top: 0; }
/* A view switch (app.js, data-swap; blueprint 11(e)) moves focus to the
   swapped region's heading for keyboard and screen-reader users; the
   heading is not a control, so it shows no ring. */
[data-swap-focus]:focus { outline: none; }
.dt-fold { margin: 0; }
.dt-fold > summary { cursor: pointer; display: list-item; min-height: var(--tap-floor); padding: var(--sp-3) 0; font-weight: var(--fw-medium); color: var(--accent); }
.dt-fold[open] > summary { margin-bottom: var(--sp-3); }
.dt-caption-sub { font-size: var(--fs-dense); font-weight: var(--fw-regular); color: var(--muted); }
.dt-hint { margin: 0 0 var(--sp-3); }

.table-scroll.dt-scroll {
    overflow: visible; background: var(--panel);
    border: 1px solid var(--line); border-radius: var(--r-dense);
    box-shadow: var(--shadow-1);
}
.dt { width: 100%; border-collapse: separate; border-spacing: 0; font-size: var(--fs-dense); }
.dt th, .dt td {
    padding: var(--dt-pad-y) var(--dt-pad-x); text-align: left; vertical-align: middle;
    border-bottom: 1px solid var(--line-soft); overflow-wrap: anywhere;
}
.dt thead th {
    position: sticky; top: 0; z-index: 1;
    background: var(--dt-thead); border-bottom-color: var(--line);
    font-size: var(--fs-dense-head); font-weight: var(--fw-medium); color: var(--muted);
    white-space: nowrap; overflow-wrap: normal;
}
.dt thead th:first-child { border-top-left-radius: var(--r-dense); }
.dt thead th:last-child { border-top-right-radius: var(--r-dense); }
.dt tbody tr:last-child td { border-bottom: 0; }
.dt .n { text-align: right; white-space: nowrap; overflow-wrap: normal; }
.dt td.n { font-family: var(--font-mono); font-size: var(--fs-mono); font-variant-numeric: tabular-nums; }
.dt .dt-nowrap { white-space: nowrap; overflow-wrap: normal; }
.dt-title { font-weight: var(--fw-medium); color: var(--heading); }
.dt-title a { color: var(--accent-dark); }
.dt .dt-chev { width: var(--dt-chev-w); padding-left: 0; text-align: right; color: var(--muted); }
.dt .dt-phone { display: none; }
html:not(.no-js) .dt-tap { cursor: pointer; }
.dt-tap:hover td, .dt-tap:focus-within td { background: var(--dt-hover); }
/* No stretched overlay on a laptop (see above): the link keeps its own ring. */
.dt .rec-open::after { content: none; }
.dt .rec-open:focus-visible { outline: var(--outline-w) solid var(--accent); outline-offset: var(--outline-w); }
.dt-void td { background: var(--row-off-bg); color: var(--muted); }
.dt-void .dt-title, .dt-void .dt-title a { color: var(--muted); }
.dt-note { margin-left: var(--sp-2); font-size: var(--fs-dense-head); color: var(--muted); }
.dt-line { display: block; white-space: pre-line; }
.dt-line + .dt-line { margin-top: var(--sp-1); }
.dt-empty td { padding: var(--sp-8); text-align: center; color: var(--muted); }

.st { display: inline-flex; align-items: center; gap: var(--sp-2); font-weight: var(--fw-medium); white-space: nowrap; }
.st::before { content: ""; flex: none; width: var(--dt-dot); height: var(--dt-dot); border-radius: var(--radius-pill); background: currentColor; }
.st.ok { color: var(--ok); }
.st.warn { color: var(--warn); }
.st.bad { color: var(--danger); }
.st.none { color: var(--neutral-ink); }
/* A soft ring in the status's own light shade: depth, not a new colour. */
.st.ok::before { box-shadow: 0 0 0 var(--dt-dot-halo) var(--ok-soft); }
.st.warn::before { box-shadow: 0 0 0 var(--dt-dot-halo) var(--warn-soft); }
.st.bad::before { box-shadow: 0 0 0 var(--dt-dot-halo) var(--danger-soft); }
.st.none::before { background: transparent; border: var(--dt-dot-ring) solid currentColor; }

@media (max-width: 720px) {
    .ph-count { display: none; }
    .ph { display: inline; }
    .ph-title { white-space: normal; overflow: visible; }
    .btn-dense {
        height: auto; min-height: var(--tap-floor); padding: var(--sp-3) var(--sp-7);
        font-size: var(--fs-btn-touch); border-radius: var(--radius);
    }
    .searchbar-dense label:not(.check) { display: flex; flex-direction: column; align-items: stretch; gap: var(--sp-1); min-width: 0; font-size: var(--fs-field); }
    .searchbar-dense input:not([type=checkbox]):not([type=radio]):not([type=file]):not([type=submit]):not([type=button]):not([type=hidden]):not([type=range]):not([type=color]),
    .searchbar-dense select { height: var(--control-h-touch); font-size: var(--fs-touch); border-radius: var(--radius); }
    /* History's filter row on a phone: Search across, From and To side by side, label over box. */
    .searchbar-dense { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: end; }
    .searchbar-dense label:first-child { grid-column: 1 / -1; }
    .searchbar-dense label:not(.check) input, .searchbar-dense label:not(.check) select { flex: none; width: 100%; min-width: 0; }
    .kpi { padding: var(--sp-4) var(--sp-6); }

    .dt, .dt tbody { display: block; }
    .dt thead { display: none; }
    .dt tr {
        position: relative; display: grid;
        grid-template-columns: minmax(0, 1fr) auto; grid-template-areas: "t f" "m s";
        align-items: baseline; gap: var(--sp-1) var(--sp-6);
        min-height: var(--tap-floor); padding: var(--sp-4) var(--sp-7);
        border-bottom: 1px solid var(--line-soft);
    }
    .dt tbody tr:last-child { border-bottom: 0; }
    .dt td { padding: 0; border: 0; min-width: 0; }
    .dt .dt-lap, .dt .dt-chev { display: none; }
    .dt .dt-title { grid-area: t; font-size: var(--fs-body); }
    .dt .dt-fig { grid-area: f; font-size: var(--fs-body); }
    .dt .dt-phone { display: block; grid-area: m; color: var(--muted); }
    .dt .dt-stc { grid-area: s; text-align: right; }
    .dt-tap:hover td, .dt-tap:focus-within td, .dt-void td { background: transparent; }
    .dt-tap:hover, .dt-tap:focus-within { background: var(--dt-hover); }
    .dt-void { background: var(--row-off-bg); }
    .dt tr.dt-empty { display: block; }
    /* The row is a grid now, so the stretched link covers it: one tap. */
    .dt .rec-open::after { content: ""; }
    .dt .rec-open:focus-visible { outline: none; }
}

/* ======================================================================
   COMPACT RECORD (the standard page style, Chris 16 Oct 2026; blueprint 11(e)).
   A record's own page, and its edit and delete pages, in the compact
   style, after the "Compact record" mock; the partner of COMPACT LIST.
   Reference: Bucket Check's and Inventory's records. The appbar is the
   top of the object header: the title (page_head) and one toolbar of
   .btn-dense buttons (Back quiet, Edit the one filled, Delete quiet
   danger), on a phone the .page-actions band.
     .msgbar        a compact message bar: .msgbar-text (a bold first
                    line, then plain) and .msgbar-actions; .msgbar-bad
                    (a deleted record, a delete to confirm), .msgbar-warn,
                    .msgbar-info (what an edit page is editing),
                    .msgbar-ok (a New page's "Saved").
     .oh            the object header: a thin brand-blue top accent,
                    .oh-sub (when, who, the status as .st with a muted
                    .oh-note, the last edit), then the key facts as a
                    .kpis strip on the faint tint (.kpi-text a figure in
                    words, .kpi-none none).
     .rec-body      .rec-main and .rec-side, side by side while each can be
                    --rec-col-min wide, stacked below that.
     .sec           a compact section: .sec-h a small header row
                    (.sec-h-sub muted), .sec-b its body, .sec-f a foot of
                    notes (.sec-sub-h a small subhead there; a form
                    under it); a .dt-scroll inside sits flush. .rec-answers
                    draws the shared answers panel (equipment/_inspection
                    answers_panel) the same way.
     .wboxes .wbox  small figure boxes (Bucket Check's three weights).
     .kv            a two-column key and value list.
     .rec-figs      a grid of figure fields on a new or edit page (a
                    tunnel entry's tonnes and buckets): as many across as
                    fit at --fig-field-min, two on a phone.
     .trail         an edit trail: .trail-what, .trail-fields (before in
                    <del>, struck and muted; after in <strong>), .trail-when
                    muted.
     .cdialog       the confirmation dialog, the standard (Chris, 16 Oct
                    2026; _macros.html confirm_dialog): a native <dialog>,
                    opened by app.js from a link marked data-dialog="<id>"
                    or a form marked data-dialog-form="<id>" (without script
                    the server asks the same as a page, confirm_page). .cdialog-h, the
                    heading; .cdialog-lead, one muted line; .cdialog-list
                    of .cdialog-item rows (.cdialog-what: .cdialog-title,
                    .cdialog-meta with a .cdialog-fig figure; then its
                    button); .cdialog-actions, the "anyway" (secondary)
                    and Cancel (quiet, data-dialog-close). A warn-amber
                    top accent; a bottom sheet on a phone with 44 px
                    buttons. .cdialog-list also sits in a .sec on the page.
     .rec-form      a new, edit or delete page: its panels drawn as sections,
                    34 px fields on a laptop, and .rec-toolbar, the one
                    toolbar at the bottom (Save or Delete, Cancel, and
                    .rec-toolbar-end at the far end). Phones keep touch
                    sizes: 40 px fields, 44 px buttons.
   ====================================================================== */
.msgbar {
    display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-3) var(--sp-7);
    margin: 0 0 var(--sp-6); padding: var(--dt-pad-y) var(--dt-pad-x);
    background: var(--info-soft); color: var(--ink);
    border: 1px solid var(--line); border-left: var(--marker-w) solid var(--accent); border-radius: var(--r-dense);
    font-size: var(--fs-dense); overflow-wrap: anywhere;
}
.msgbar-text { display: flex; flex-direction: column; gap: var(--sp-1); flex: 1 1 var(--search-min); min-width: 0; }
.msgbar strong { font-weight: var(--fw-medium); color: var(--info-ink); }
.msgbar-actions { flex: none; }
.msgbar-bad { background: var(--danger-soft); border-color: var(--danger-line); border-left-color: var(--danger); }
.msgbar-bad strong { color: var(--danger-dark); }
.msgbar-warn { background: var(--warn-soft); border-color: var(--warn-line); border-left-color: var(--warn); }
.msgbar-ok { background: var(--ok-soft); border-color: var(--ok-line); border-left-color: var(--ok); }
.msgbar-ok strong { color: var(--ok); }

.oh {
    margin: 0 0 var(--sp-8); overflow: hidden;
    background: var(--panel); border: 1px solid var(--line); border-top: var(--accent-top-w) solid var(--accent);
    border-radius: var(--r-dense); box-shadow: var(--shadow-1);
}
.oh-sub {
    display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-1) var(--sp-8);
    margin: 0; padding: var(--sp-4) var(--sp-8); font-size: var(--fs-dense); color: var(--ink);
}
.oh-note { color: var(--muted); }
.oh .kpis { margin: 0; border: 0; border-top: 1px solid var(--line-soft); border-radius: 0; box-shadow: none; }
.kpi-v.kpi-text { font-family: var(--font-body); }
.kpi-v.kpi-none { color: var(--muted); }

.rec-body { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 0 var(--sp-8); }
.rec-main { flex: 3 1 var(--rec-col-min); min-width: 0; }
.rec-side { flex: 2 1 var(--rec-col-min); min-width: 0; }

.sec, .rec-answers > .panel {
    margin: 0 0 var(--sp-8); background: var(--panel);
    border: 1px solid var(--line); border-radius: var(--r-dense); box-shadow: var(--shadow-1);
}
.rec-answers > .panel { padding: 0; }
.sec-h, .rec-answers > .panel > h2 {
    display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--sp-3);
    margin: 0; padding: var(--dt-pad-y) var(--dt-pad-x);
    font-size: var(--fs-dense); font-weight: var(--fw-medium); letter-spacing: normal; color: var(--heading);
    border-bottom: 1px solid var(--line-soft);
}
.sec-h-sub { font-weight: var(--fw-regular); color: var(--muted); }
.sec-b { padding: var(--dt-pad-x); }
.sec-b > :first-child { margin-top: 0; }
.sec-b > :last-child { margin-bottom: 0; }
.sec-b p { margin: 0; }
.sec-b > * + .hint, .sec-b > * + p { margin-top: var(--sp-4); }
.sec-f { padding: var(--dt-pad-y) var(--dt-pad-x); border-top: 1px solid var(--line-soft); }
.sec-f:empty { display: none; }
/* Fields straight in a section's body (a site's Details), a small subhead
   in its foot, and a form under that subhead (Chris, 16 Oct 2026). */
.sec-b > .field + .field { margin-top: var(--sp-6); }
.sec-sub-h { margin: 0 0 var(--sp-2); font-size: var(--fs-dense); font-weight: var(--fw-medium); color: var(--heading); }
.sec-f > form { margin-top: var(--sp-4); }
.sec-b > * + form { margin-top: var(--sp-6); }
/* A form of several sections with one toolbar at its foot, on a page with
   more sections under it (a site's Details and Time zone). */
.sec-form { margin: 0 0 var(--sp-8); }
.sec-form > .form-note { margin: 0 0 var(--sp-4); }
.sec-f .hint { margin: 0; }
.sec-f .hint + .hint { margin-top: var(--sp-2); }
.sec > .table-scroll.dt-scroll, .rec-answers .table-scroll { margin: 0; border: 0; border-radius: 0; box-shadow: none; }
.sec > .table-scroll.dt-scroll .dt thead th { border-radius: 0; }
.rec-answers .grid { border: 0; border-radius: 0; font-size: var(--fs-dense); }
.rec-answers .grid th, .rec-answers .grid td { padding: var(--dt-pad-y) var(--dt-pad-x); }
.rec-answers .grid thead th {
    background: var(--dt-thead); font-size: var(--fs-dense-head); font-weight: var(--fw-medium);
    text-transform: none; letter-spacing: normal;
}

.wboxes { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--sp-3); margin: 0 0 var(--dt-pad-x); }
.wbox { min-width: 0; padding: var(--dt-pad-y) var(--dt-pad-x); background: var(--surface-tint); border: 1px solid var(--line); border-radius: var(--r-dense); }
.wbox-l { font-size: var(--fs-dense-head); color: var(--muted); }
.wbox-v {
    margin: 0; font-family: var(--font-mono); font-size: var(--fs-base); font-weight: var(--fw-medium);
    color: var(--ink); font-variant-numeric: tabular-nums; overflow-wrap: anywhere;
}
.rec-figs { display: grid; grid-template-columns: repeat(auto-fill, minmax(var(--fig-field-min), 1fr)); gap: var(--sp-4) var(--sp-6); }
.rec-figs .pu-number { min-width: 0; margin: 0; padding: 0; border-bottom: 0; }
.kv { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: var(--sp-2) var(--sp-8); margin: 0; font-size: var(--fs-dense); }
.kv dt { color: var(--muted); }
.kv dd { margin: 0; overflow-wrap: anywhere; }

.trail { list-style: none; margin: 0; padding: 0; font-size: var(--fs-dense); }
.trail > li { display: flex; flex-direction: column; gap: var(--sp-1); padding: var(--dt-pad-y) 0; border-bottom: 1px solid var(--line-soft); overflow-wrap: anywhere; }
.trail > li:first-child { padding-top: 0; }
.trail > li:last-child { padding-bottom: 0; border-bottom: 0; }
.trail-what { font-weight: var(--fw-medium); color: var(--heading); }
.trail-fields { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--sp-1); }
.trail del { color: var(--muted); }
.trail strong { font-weight: var(--fw-bold); color: var(--ink); }
.trail-when { font-size: var(--fs-dense-head); color: var(--muted); }

.rec-toolbar { padding-top: var(--sp-6); border-top: 1px solid var(--line); }

/* No display here: a closed <dialog> must keep the browser's display: none. */
.cdialog {
    width: var(--cdialog-w); max-width: calc(100vw - 2 * var(--sp-8));
    max-height: calc(100dvh - 2 * var(--sp-8)); overflow: auto;
    padding: 0; color: var(--ink); background: var(--panel);
    border: 1px solid var(--line); border-top: var(--accent-top-w) solid var(--warn);
    border-radius: var(--r-dense); box-shadow: var(--shadow-dialog);
}
.cdialog::backdrop { background: var(--overlay); }
.cdialog-h {
    margin: 0; padding: var(--sp-6) var(--sp-8) var(--sp-2);
    font-size: var(--fs-panel); font-weight: var(--fw-medium); letter-spacing: normal; color: var(--heading);
}
.cdialog-lead { margin: 0; padding: 0 var(--sp-8) var(--sp-6); font-size: var(--fs-dense); color: var(--muted); }
.cdialog-list { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--line-soft); }
.cdialog-item {
    display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-3) var(--sp-6);
    padding: var(--dt-pad-y) var(--sp-8); border-bottom: 1px solid var(--line-soft);
}
.cdialog-what { display: flex; flex-direction: column; gap: var(--sp-1); flex: 1 1 var(--cdialog-what-min); min-width: 0; }
.cdialog-title { font-weight: var(--fw-medium); color: var(--ink); overflow-wrap: anywhere; }
.cdialog-meta { font-size: var(--fs-dense); color: var(--muted); }
.cdialog-fig { font-family: var(--font-mono); font-variant-numeric: tabular-nums; color: var(--ink); }
/* A field the dialog asks for (a typed confirmation), under its facts. */
.cdialog-field { margin: 0; padding: var(--sp-6) var(--sp-8) 0; }
.cdialog-actions {
    display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--sp-3);
    padding: var(--sp-6) var(--sp-8); background: var(--surface-tint);
}
.sec > .cdialog-list { border-top: 0; }
.sec > .cdialog-list > .cdialog-item { padding-left: var(--dt-pad-x); padding-right: var(--dt-pad-x); }
.sec > .cdialog-list > .cdialog-item:last-child { border-bottom: 0; }
.rec-toolbar-end { margin-left: auto; }
/* .dt-acts: a dense table's last cell of row actions (a person's Sites and
   roles), compact buttons side by side on a laptop, a full-width row of
   touch buttons under the row's two lines on a phone. */
.dt .dt-acts { text-align: right; white-space: nowrap; overflow-wrap: normal; }
.dt .dt-acts .inline-form + .inline-form { margin-left: var(--sp-2); }

@media (min-width: 721px) {
    .rec-form .panel {
        margin-bottom: 0; padding: var(--dt-pad-x) var(--sp-8);
        border-radius: var(--r-dense); box-shadow: var(--shadow-1);
    }
    .rec-form .panel > h2 {
        margin: calc(-1 * var(--dt-pad-x)) calc(-1 * var(--sp-8)) var(--dt-pad-x);
        padding: var(--dt-pad-y) var(--sp-8);
        font-size: var(--fs-dense); font-weight: var(--fw-medium); letter-spacing: normal; color: var(--heading);
        border-bottom: 1px solid var(--line-soft);
    }
    .rec-form .panel > :not(h2) + .field, .rec-form .panel > :not(h2) + .row { margin-top: var(--sp-6); }
    .rec-form .field label, .rec-form .field .field-label { font-size: var(--fs-dense); }
    .rec-form input:not([type=checkbox]):not([type=radio]):not([type=file]):not([type=submit]):not([type=button]):not([type=hidden]):not([type=range]):not([type=color]),
    .rec-form select {
        height: var(--field-h-dense); padding-top: 0; padding-bottom: 0;
        font-size: var(--fs-dense); border-radius: var(--r-dense);
    }
    .rec-form textarea { font-size: var(--fs-dense); border-radius: var(--r-dense); }
    .rec-form .bc-weights { gap: var(--sp-4); margin: var(--sp-4) 0; }
    .rec-form .bc-weights .pu-number {
        padding: var(--dt-pad-y) var(--dt-pad-x); background: var(--surface-tint);
        border-top-width: var(--accent-top-w); border-radius: var(--r-dense);
    }
    .rec-form .bc-weights .pu-number input { font-size: var(--fs-body); }
    .rec-form .bc-live { padding: var(--dt-pad-y) var(--dt-pad-x); border-radius: var(--r-dense); font-size: var(--fs-dense); }
    .rec-form .inv-row { margin-bottom: var(--sp-3); padding: var(--dt-pad-y) var(--dt-pad-x); border-radius: var(--r-dense); }
    .rec-form .inv-seg { border-radius: var(--r-dense); }
    .rec-form .inv-seg label { min-height: var(--field-h-dense); font-size: var(--fs-dense); }
    .rec-form .inv-entry input.inv-value { height: var(--field-h-dense); }
    .rec-form .form-note { font-size: var(--fs-dense-head); }
    .rec-form .bc-today { gap: var(--sp-3); margin-bottom: var(--sp-6); }
    .rec-form .bc-today-line { font-size: var(--fs-dense); }
    .rec-form .bc-chips { gap: var(--sp-2); }
    .rec-form .bc-chip { min-height: var(--field-h-dense); padding: 0 var(--dt-pad-x); border-radius: var(--r-dense); font-size: var(--fs-dense); }
    .rec-toolbar .btn {
        height: var(--btn-h-dense); padding: 0 var(--dt-pad-x);
        font-size: var(--fs-dense); font-weight: var(--fw-medium); border-radius: var(--r-dense);
    }
}
@media (max-width: 720px) {
    .oh-sub { gap: var(--sp-1) var(--sp-6); padding: var(--sp-4) var(--sp-6); }
    .rec-toolbar .btn, .msgbar-actions .btn, .rec-form .check-block { min-height: var(--tap-floor); }
    .msgbar-actions { flex: 1 1 auto; }
    .msgbar-actions .btn { width: 100%; }
    /* A bottom sheet: full width, at the foot of the screen, 44 px buttons. */
    .cdialog {
        width: 100%; max-width: 100%; max-height: var(--cdialog-sheet-h); margin: auto 0 0;
        border-bottom: 0; border-radius: var(--r-dense) var(--r-dense) 0 0;
    }
    .cdialog-item .btn, .cdialog-actions .btn, .sec > .cdialog-list .btn { width: 100%; min-height: var(--tap-floor); }
    .cdialog-actions { flex-direction: column; padding-bottom: calc(var(--sp-6) + env(safe-area-inset-bottom, 0px)); }
    .dt .dt-acts { grid-column: 1 / -1; display: flex; flex-wrap: wrap; gap: var(--sp-3); margin-top: var(--sp-3); text-align: left; white-space: normal; }
    .dt .dt-acts .inline-form { display: block; flex: 1 1 auto; }
    .dt .dt-acts .inline-form + .inline-form { margin-left: 0; }
    .dt .dt-acts .btn { width: 100%; }
}

/* ======================================================================
   ACCESS (docs/plans/access-roles.md, slice B; Super Admin pages).
   A role page's module ticks and the person's Access card
   (platform/_access_card.html):
     .acc-group     one menu section's ticks, its name as .acc-legend
     .acc-ticks     the ticks as a grid, as many across as fit at
                    --acc-col-min, one on a phone (each a 44 px row there)
     .acc-row       one module: its box, label, tag (.st) and why line
     .acc-off       "Off at London": muted, italic
     .acc-scope     the scope links (All sites, each of their sites), one
                    row that wraps; the current one filled
     .acc-people    the people who hold a role, one line each
   New for Convertus MES.
   ====================================================================== */
.acc-group + .acc-group { margin-top: var(--sp-6); }
.acc-legend { padding: 0; margin-bottom: var(--sp-3); font-size: var(--fs-dense); font-weight: var(--fw-medium); color: var(--heading); text-transform: uppercase; letter-spacing: var(--ls-label); }
.acc-ticks { display: grid; grid-template-columns: repeat(auto-fill, minmax(var(--acc-col-min), 1fr)); gap: var(--sp-3) var(--sp-8); }
.acc-row .st { margin-left: var(--sp-2); font-size: var(--fs-dense); }
.acc-row .sub { font-size: var(--fs-dense-head); }  /* the notes under each module ("In the Leader role", "Off at Surrey"): smaller (Chris, 16 Oct 2026) */
.acc-off { font-style: italic; }
.acc-row-off { color: var(--muted); cursor: default; opacity: .5; }  /* switched off where the ticks apply: shown, clearly greyed, not tickable */
.acc-row-off input { cursor: default; }
.acc-scope { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin: var(--sp-4) 0 var(--sp-2); }
.acc-scope-link {
    display: inline-flex; align-items: center; min-height: var(--btn-h-dense); padding: 0 var(--dt-pad-x);
    border: 1px solid var(--line); border-radius: var(--r-dense); background: var(--panel);
    font-size: var(--fs-dense); font-weight: var(--fw-medium); color: var(--accent-dark); text-decoration: none;
}
.acc-scope-link:hover { border-color: var(--accent); }
.acc-scope-link[aria-current="true"] { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.acc-people { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: var(--sp-2); }
@media (max-width: 720px) {
    .acc-ticks { grid-template-columns: 1fr; }
    .acc-row, .acc-scope-link { min-height: var(--tap-floor); }
    .acc-people a { display: inline-flex; align-items: center; min-height: var(--tap-floor); }
}
/* .role-row: a site Admin's compact "Role [select] Change" row on a
   person's page (access roles, slice C). The select grows, the button
   sits at its foot; on a phone they stack, each a full touch target. */
.role-row { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--sp-3); margin-top: var(--sp-6); }
.role-row-field { flex: 1 1 var(--search-min); min-width: 0; }
.role-row-field select { width: 100%; }
@media (max-width: 720px) {
    .role-row { flex-direction: column; align-items: stretch; }
    /* In a column the flex-basis would become a 240 px height. */
    .role-row-field { flex: none; }
}

/* ======================================================================
   ROLE BUILDER (Chris, 16 Oct 2026; blueprint 13(k); platform/roles.html,
   platform/role_form.html, app/static/js/roles.js).
     .rmx           the roles matrix: roles across, modules down under
                    their section rows, a tick per cell and nothing else
     .rmx-tick      a cell's button, aria-pressed says ticked; the mark is
                    drawn with borders (no icon, no colour literal)
     .rmx-pick      the role picker, phones only: the matrix shows one
                    role (.is-picked) as a list of 44 px rows
     .rb-grid       a role page: the form, and on a laptop a side column
                    (.rb-side) of folds (.rb-fold): the menu preview
                    (.rb-menu) and the people (.rb-people)
     .rb-label      "Same as the name" hides the switcher label's box, by
                    a :has() rule, so it works without script
   New for Convertus MES.
   ====================================================================== */
.rmx-bar { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: var(--sp-3); margin: 0 0 var(--sp-3); }
.rmx-pick { display: none; }
.rmx-retired { font-size: var(--fs-dense); }
.rmx-scroll { overflow-x: auto; }
.rmx { width: 100%; border-collapse: separate; border-spacing: 0; font-size: var(--fs-dense); }
.rmx th, .rmx td { padding: var(--sp-2) var(--dt-pad-x); border-bottom: 1px solid var(--line-soft); text-align: left; vertical-align: middle; }
.rmx thead th {
    position: sticky; top: 0; z-index: 1; vertical-align: bottom;
    background: var(--dt-thead); border-bottom-color: var(--line);
}
.rmx thead th:first-child { border-top-left-radius: var(--r-dense); }
.rmx thead th:last-child { border-top-right-radius: var(--r-dense); }
.rmx tbody:last-child tr:last-child > * { border-bottom: 0; }
.rmx .rmx-role { min-width: var(--rmx-role-w); text-align: center; }
.rmx-role a { display: block; font-weight: var(--fw-medium); color: var(--accent-dark); }
.rmx-role.is-retired a { color: var(--muted); }
.rmx-sub { display: block; font-size: var(--fs-dense-head); font-weight: var(--fw-regular); color: var(--muted); white-space: nowrap; }
.rmx .rmx-mod { width: var(--rmx-mod-w); font-weight: var(--fw-regular); color: var(--ink); }
.rmx-sec th {
    padding-top: var(--sp-6); font-size: var(--fs-dense-head); font-weight: var(--fw-medium);
    color: var(--heading); text-transform: uppercase; letter-spacing: var(--ls-label);
}
.rmx tbody tr:not(.rmx-sec):hover > * { background: var(--dt-hover); }
.rmx-cell { text-align: center; }
.rmx-cell form { display: flex; justify-content: center; }
.rmx-tick {
    position: relative; display: inline-flex; align-items: center; justify-content: center;
    width: var(--rmx-tick); height: var(--rmx-tick); padding: 0;
    border: 1px solid var(--line); border-radius: var(--r-dense); background: var(--panel); cursor: pointer;
}
.rmx-tick:hover { border-color: var(--accent); }
.rmx-tick:focus-visible { outline: var(--outline-w) solid var(--accent); outline-offset: var(--outline-w); }
.rmx-tick[aria-pressed="true"] { background: var(--accent); border-color: var(--accent); }
.rmx-tick[aria-pressed="true"]::after {
    content: ""; width: var(--rmx-mark-w); height: var(--rmx-mark-h); margin-top: calc(-1 * var(--sp-1));
    border: solid var(--on-accent); border-width: 0 var(--outline-w) var(--outline-w) 0; transform: rotate(45deg);
}
form[aria-busy="true"] .rmx-tick { opacity: .5; }

.rb-grid { display: grid; grid-template-columns: minmax(0, 1fr) var(--rb-side-w); gap: var(--sp-10); align-items: start; }
.rb-side { display: flex; flex-direction: column; gap: var(--sp-6); position: sticky; top: var(--sp-8); }
.rb-fold { background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-dense); box-shadow: var(--shadow-1); }
.rb-fold > summary { cursor: pointer; padding: var(--sp-4) var(--sp-7); font-weight: var(--fw-medium); color: var(--heading); }
.rb-fold-body { padding: 0 var(--sp-7) var(--sp-6); }
.rb-fold-body > .hint:first-child { margin-top: 0; }
.rb-menu, .rb-menu ul { margin: 0; padding: 0; list-style: none; }
.rb-menu-sec + .rb-menu-sec { margin-top: var(--sp-4); }
.rb-menu-h { display: block; font-size: var(--fs-dense-head); font-weight: var(--fw-medium); color: var(--muted); text-transform: uppercase; letter-spacing: var(--ls-label); }
.rb-menu ul li { padding: var(--sp-1) 0 var(--sp-1) var(--sp-6); font-size: var(--fs-dense); }
.rb-people { width: 100%; border-collapse: collapse; font-size: var(--fs-dense); }
.rb-people th, .rb-people td { padding: var(--sp-2) var(--sp-3) var(--sp-2) 0; border-bottom: 1px solid var(--line-soft); text-align: left; vertical-align: top; overflow-wrap: anywhere; }
.rb-people th { font-size: var(--fs-dense-head); font-weight: var(--fw-medium); color: var(--muted); }
.rb-people .n { text-align: right; padding-right: 0; }
.field .rb-same-label { display: flex; margin-top: var(--sp-1); font-weight: var(--fw-regular); }
.rb-label-field { margin-top: var(--sp-3); }
.rb-label:has(.rb-same:checked) .rb-label-field { display: none; }
.rb-access { border: 0; margin: 0; padding: 0; min-width: 0; }
.rb-access > legend { padding: 0; margin-bottom: var(--sp-3); font-weight: var(--fw-medium); color: var(--heading); }
.rb-access .choice { flex: 0 1 var(--rmx-mod-w); min-height: var(--btn-h-dense); display: flex; margin-bottom: 0; font-size: var(--fs-base); }  /* .field label would stack it */
.rb-menu { margin-top: var(--sp-4); }
@media (max-width: 720px) {
    .rmx-bar { justify-content: space-between; }
    .rmx-pick { display: flex; flex-wrap: wrap; gap: var(--sp-2); width: 100%; }
    .rmx-pick .acc-scope-link, .rmx-retired { display: inline-flex; align-items: center; min-height: var(--tap-floor); }
    .rmx .rmx-role:not(.is-picked), .rmx .rmx-cell:not(.is-picked) { display: none; }
    .rmx .rmx-mod { width: auto; font-size: var(--fs-body); }
    .rmx-cell { width: var(--tap-floor); }
    /* A normal-sized box with a finger-sized target: the button's border
       is the box, an invisible ring around it takes the tap. */
    .rmx-cell .rmx-tick { width: var(--rmx-tick); height: var(--rmx-tick); }
    .rmx-cell .rmx-tick::before { content: ""; position: absolute; inset: calc((var(--rmx-tick) - var(--tap-floor)) / 2); }
    .rb-grid { grid-template-columns: minmax(0, 1fr); }
    .rb-side { position: static; }
    .rb-fold > summary { min-height: var(--tap-floor); display: flex; align-items: center; gap: var(--sp-3); }
    .field .rb-same-label { min-height: var(--tap-floor); align-items: center; }
    .rb-access .choice { flex: 1 1 0; min-height: var(--tap-floor); }
    .rb-people a { display: inline-flex; align-items: center; min-height: var(--tap-floor); }
}

/* ======================================================================
   COMING SOON. A placeholder module's one page (the Business Plan
   section, docs/plans/bpd.md): a calm empty state, the module's icon in
   a soft accent circle, "Coming soon", and one plain line. Centred on the
   grey page, no panel or dashed edge, so it never reads as an error or a
   missing setup. New for Convertus MES.
   ====================================================================== */
.soon {
    display: flex; flex-direction: column; align-items: center; text-align: center;
    padding: var(--sp-13) var(--sp-8);
}
.soon-mark {
    display: inline-flex; align-items: center; justify-content: center;
    width: var(--soon-mark); height: var(--soon-mark); border-radius: var(--radius-pill);
    background: var(--accent-soft); color: var(--accent); margin-bottom: var(--sp-8);
}
.soon-mark .icon { width: var(--soon-icon); height: var(--soon-icon); }
.soon-title {
    font-family: var(--font-head); font-size: var(--fs-heading); font-weight: var(--fw-bold);
    color: var(--heading); margin: 0 0 var(--sp-4);
}
.soon-line { color: var(--muted); max-width: var(--soon-max); margin: 0; line-height: var(--lh-body); }

/* ======================================================================
   APPEARANCE: the scheme cards on /account/appearance (blueprint 11(c)).
   A radio card per scheme with a small painted preview: rail with menu
   lines, page, panel and a button, from the --sw-* tokens so each card
   shows its own scheme whatever the page wears. Selected: the accent
   border and soft band, as .choice.
   Adapted from Pulse EHS app/static/css/style.css .scheme-card @ aa321d3.
   ====================================================================== */
.scheme-set { border: 0; padding: 0; margin: 0; min-width: 0; }
.scheme-set > legend { padding: 0; }
.scheme-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(var(--scheme-card-min), 1fr)); gap: var(--sp-5); }
.scheme-card {
    display: flex; flex-direction: column; gap: var(--sp-4); min-width: 0;
    padding: var(--sp-5); cursor: pointer;
    border: var(--choice-line) solid var(--line); border-radius: var(--r-dense);
    background: var(--panel); color: var(--ink);
}
.scheme-card:hover { background: var(--hover); }
.scheme-card:has(input:checked) { border-color: var(--accent); background: var(--accent-soft); }
.scheme-card:has(input:focus-visible) { outline: var(--outline-w) solid var(--accent); outline-offset: var(--outline-w); }
.scheme-text { display: flex; flex-direction: column; gap: var(--sp-1); }
.scheme-name { display: flex; align-items: center; gap: var(--sp-3); font-weight: var(--fw-medium); color: var(--heading); }
.scheme-name input[type=radio] { margin: 0; }
.scheme-blurb { font-size: var(--fs-small); color: var(--muted); }
.scheme-preview {
    display: flex; height: var(--sw-h); overflow: hidden;
    border: 1px solid var(--line); border-radius: var(--r-dense);
}
.sp-rail { flex: 0 0 var(--sw-rail-w); display: flex; flex-direction: column; gap: var(--sp-2); padding: var(--sp-3) var(--sp-2); }
.sp-rail i { display: block; height: var(--sp-2); border-radius: var(--radius-pill); }
.sp-rail i + i { opacity: var(--sw-line-opacity); }
.sp-page { flex: 1 1 auto; display: flex; padding: var(--sp-3); }
.sp-panel { flex: 1 1 auto; display: flex; align-items: flex-end; padding: var(--sp-3); border-radius: var(--radius-sm); }
.sp-btn { display: block; width: var(--sw-btn-w); height: var(--sp-4); border-radius: var(--radius-sm); }
.sp-convertus .sp-rail { background: var(--sw-convertus-rail); }
.sp-convertus .sp-rail i { background: var(--sw-convertus-mark); }
.sp-convertus .sp-page { background: var(--sw-convertus-page); }
.sp-convertus .sp-panel { background: var(--sw-convertus-panel); }
.sp-convertus .sp-btn { background: var(--sw-convertus-accent); }
.sp-navy .sp-rail { background: var(--sw-navy-rail); }
.sp-navy .sp-rail i { background: var(--sw-navy-mark); }
.sp-navy .sp-page { background: var(--sw-navy-page); }
.sp-navy .sp-panel { background: var(--sw-navy-panel); }
.sp-navy .sp-btn { background: var(--sw-navy-accent); }
.sp-green .sp-rail { background: var(--sw-green-rail); }
.sp-green .sp-rail i { background: var(--sw-green-mark); }
.sp-green .sp-page { background: var(--sw-green-page); }
.sp-green .sp-panel { background: var(--sw-green-panel); }
.sp-green .sp-btn { background: var(--sw-green-accent); }
.sp-light .sp-rail { background: var(--sw-light-rail); border-right: 1px solid var(--line); }
.sp-light .sp-rail i { background: var(--sw-light-mark); }
.sp-light .sp-page { background: var(--sw-light-page); }
.sp-light .sp-panel { background: var(--sw-light-panel); }
.sp-light .sp-btn { background: var(--sw-light-accent); }
@media (max-width: 720px) {
    .scheme-card { padding: var(--sp-6); }
    .scheme-name { min-height: var(--tap-floor); }
    .scheme-name input[type=radio] { width: var(--choice-radio); height: var(--choice-radio); }
}

/* ======================================================================
   STATE EDGE. A record's state as a coloured left edge on a card, a
   stacked row or a record header, in the status colours (11(c)): ok for
   current and in use, warn for stale, danger for voided, info for a plain
   record, quiet for one that is superseded, none (dashed) for nothing
   recorded yet (the compact list's .st none says it in words instead).
   Colour is never the only sign: a pill or a word beside it
   says the same. Used by Bucket Check and Inventory.
   Kept at the end of the file, after every component it edges, so it
   wins over their own borders.
   New for Convertus MES.
   ====================================================================== */
.edge-ok { border-left: var(--marker-w) solid var(--ok); }
.edge-warn { border-left: var(--marker-w) solid var(--warn); }
.edge-danger { border-left: var(--marker-w) solid var(--danger); }
.edge-info { border-left: var(--marker-w) solid var(--accent); }
.edge-quiet { border-left: var(--marker-w) solid var(--line); }
