/* ==========================================================================
   OYO-HSAP. Oyo State Facility Health & Safety Audit Portal
   ==========================================================================

   Design intent: an official record, not a dashboard product.

   The reference points are government gazettes, audit ledgers and controlled
   documents, an editorial serif for headings, a monospace face for reference
   IDs (which are serial numbers and deserve to look like it), ruled tables that
   read as a ledger, and a deep ink palette on paper.

   --brand and --accent are injected by the server from the settings registry,
   so an administrator's branding change repaints the whole portal.
   ========================================================================== */

/* --- Tokens -------------------------------------------------------------- */

:root {
    /* The state's own colours, sampled from the Oyo State Government logo file
       rather than chosen. #9C1E23 is the maroon of the wordmark and the shield's
       star field, #F9C117 the gold of its border and strapline. An administrator
       can still change both under Branding; these are what the portal ships as.
       See public/assets/img/SOURCES.md. */
    --brand: #9c1e23;   /* 7.1 : 1 on paper, so it can carry text */
    /* 11.4 : 1 on ink and 1.5 : 1 on paper. It is a rule, a fill and a mark on
       the dark plane. It is never a word on paper, and there is a check in the
       stylesheet's comments to that effect because it is an easy mistake. */
    --accent: #f9c117;

    /* Pressed and deep surfaces, derived from the maroon so a masthead and a
       hovered button stay unmistakably the same colour family. */
    --brand-deep: #7a161b;
    --brand-ink:  #4a0d11;

    /* The Ministry is of Environment and Natural Resources, and the scheme is a
       health-and-safety one, so green earns its place as a third colour rather
       than being decoration. Used for compliance, growth and confirmation. */
    --forest:      #1b6b45;
    --forest-soft: #e8f0ea;

    /* Ink and paper. Warmed towards the maroon so nothing reads blue, and the
       ink pulled down near black: the page now alternates between a dark plane
       and a paper one, and a soft charcoal cannot hold its own against paper
       that bright. Depth is what the old single flat sheet was missing. */
    /* Every one of these was measured against the paper below rather than
       picked by eye, and the old scale failed: its secondary grey sat at about
       4.4 to 1, which is under the line for body text and well under what an
       older reader needs. These clear AAA at 7 to 1 down to --ink-3. Only
       --ink-4 sits lower, and nothing that must be read is set in it. */
    --ink:          #171310;   /* 16.4 : 1 */
    --ink-2:        #3b3630;   /* 10.6 : 1 */
    --ink-3:        #575049;   /*  7.0 : 1 */
    --ink-4:        #726a60;   /*  4.7 : 1, furniture only */
    --rule:         #ddd6c8;
    --rule-strong:  #c3baa9;
    --paper:        #f4f1e8;
    --paper-raised: #ffffff;
    --paper-sunk:   #eae5d8;

    /* The dark plane. Near black with a faint green cast taken from the crest,
       so the page's two grounds belong to the same object. */
    --ground:       #0e1210;

    /* The dark plane's own scale. Ink is the ground; these are what sits on it.
       Kept as tokens rather than written inline, because the homepage, the rail
       and the sign-in plate all stand on the same ground and must agree. */
    --on-ink:       #f4f1e8;   /* 16.7 : 1 */
    --on-ink-2:     #b8b2a8;   /*  9.0 : 1 */
    --on-ink-3:     #8e877d;   /*  5.3 : 1 */
    --ink-rule:     rgba(244, 241, 232, .16);
    --ink-raised:   #161b18;

    --danger:  #a41d1d;
    --warning: #8a5a00;
    --success: #1a6b3c;

    /* Type, chosen for who actually reads this.

       A statutory notice is read by people who did not choose to be here and
       who are frequently not young. That ruled out the high-contrast display
       serif this design first used: it is handsome at 7rem and its thin strokes
       vanish at 15px, on a poor screen, or for anyone with reduced vision.

       Atkinson Hyperlegible Next carries the interface. It was drawn by the
       Braille Institute to raise character recognition for low-vision readers,
       and it earns its place here for a second, concrete reason: this portal's
       central artifact is a reference like OYO/HSAP/2026/HSP/0001, and this is
       a family that refuses to let O and 0, or l and 1, look alike. The mono
       cut is the same typeface, so a reference number reads in the same voice
       as the sentence around it while still being unmistakably a serial.

       Literata carries the headings. It was drawn for long-form reading on
       screen: large x-height, sturdy serifs, moderate stroke contrast. It gives
       the institutional weight the display serif was there for, without the
       fragility. Its optical size axis is set high for display and low for the
       text sizes, which is what the axis is for. */
    --display: "Literata", "Iowan Old Style", Georgia, "Times New Roman", serif;
    --sans:    "Atkinson Hyperlegible Next", "Atkinson Hyperlegible",
               "Segoe UI", Helvetica, Arial, sans-serif;
    --mono:    "Atkinson Hyperlegible Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

    /* Kept so anything still asking for --serif gets the display face rather
       than the browser's default Times. */
    --serif: var(--display);

    /* A display scale with real range. The old page set its largest heading at
       roughly twice its body text, which is why it read as flat however well it
       was spaced: there was no contrast for the eye to travel across. */
    --t-display: clamp(2.6rem, 6.4vw, 5.4rem);
    --t-title:   clamp(2rem, 4vw, 3.3rem);
    --t-head:    clamp(1.5rem, 2.4vw, 2.1rem);

    /* A 4px rhythm throughout. */
    --s1: 4px;  --s2: 8px;  --s3: 12px; --s4: 16px;
    --s5: 24px; --s6: 32px; --s7: 48px; --s8: 64px;

    /* Squarer than before. Rounded corners read as consumer software, and an
       instrument of state should sit flat on the page. */
    --radius:    2px;
    --radius-lg: 4px;

    /* Motion. The built-in CSS easings are too weak to read as intentional, so
       these are the stronger custom curves: ease-out for anything entering,
       because it moves immediately and therefore feels responsive at the exact
       moment the eye is on it. Nothing in the interface runs past 260ms, a
       slower transition does not read as considered, it reads as a slow portal. */
    --ease-out:    cubic-bezier(.23, 1, .32, 1);
    --ease-in-out: cubic-bezier(.77, 0, .175, 1);
    --t-press:  110ms;
    --t-quick:  160ms;
    --t-enter:  240ms;

    /* Shadows are tight and low, printed matter, not floating glass. */
    --shadow-1: 0 1px 2px rgba(20, 24, 26, .06);
    --shadow-2: 0 2px 4px rgba(20, 24, 26, .07), 0 1px 2px rgba(20, 24, 26, .05);
    --shadow-3: 0 8px 24px rgba(20, 24, 26, .10), 0 2px 6px rgba(20, 24, 26, .06);

    --rail: 248px;
}

/* --- Reset --------------------------------------------------------------- */

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
    margin: 0;
    font-family: var(--sans);
    /* Up from 15px. Small interface text is a habit borrowed from software made
       for people who use it all day; this is a public statutory service and a
       good deal of its readership is older. 17px with a longer line costs a
       little density and buys back rather more than that. */
    font-size: 17px;
    line-height: 1.62;
    color: var(--ink);
    background: var(--paper);
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;

    /* A very fine grain, so the background reads as paper rather than a flat
       fill. Two hairline rules at a low angle, almost subliminal. */
    background-image:
        repeating-linear-gradient(
            120deg,
            rgba(20, 24, 26, .014) 0 1px,
            transparent 1px 3px
        );
}

/* The display face on the page title, where it has room to be one. Panel and
   card headings are interface furniture, not prose, and are set in the sans. */
h1 {
    font-family: var(--display);
    font-weight: 400;
    /* Literata's optical size axis is pinned rather than left to the browser,
       which drives it towards 72 at display sizes and thins the strokes into a
       Didone. The sturdier mid-range cut is the one that stays legible for a
       reader who is squinting, and it is the reason this face was chosen. */
    font-optical-sizing: none;
    font-variation-settings: "opsz" 32;
    font-size: var(--t-head);
    line-height: 1.06;
    letter-spacing: -.02em;
    margin: 0;
    color: var(--ink);
}

h2, h3, h4 {
    font-family: var(--sans);
    font-weight: 600;
    line-height: 1.25;
    letter-spacing: -.012em;
    margin: 0;
    color: var(--ink);
}

h2 { font-size: 18px; }
h3 { font-size: 16px; }

p { margin: 0 0 var(--s3); }
a { color: var(--brand); text-decoration-thickness: 1px; text-underline-offset: 2px; }
@media (hover: hover) and (pointer: fine) {
    a:hover { color: var(--ink); }
}

button, input, select, textarea { font: inherit; color: inherit; }

/* Focus is always visible, and always the same shape. */
:focus-visible {
    outline: 2px solid var(--brand);
    outline-offset: 2px;
    border-radius: 2px;
}

/* --- Boot / no-script ---------------------------------------------------- */

.boot, .noscript {
    min-height: 100vh;
    display: grid;
    place-content: center;
    justify-items: center;
    gap: var(--s4);
    color: var(--ink-3);
    text-align: center;
    padding: var(--s5);
}

.boot__spinner {
    width: 22px; height: 22px;
    border: 2px solid var(--rule-strong);
    border-top-color: var(--brand);
    border-radius: 50%;
    animation: spin .7s linear infinite;
}

@keyframes spin { to { transform: rotate(360deg); } }

/* --- Reference IDs ------------------------------------------------------- *
   The serial number at the heart of the system. Treated as a stamped mark:
   monospace, tracked out, with a hairline rule beneath it.                   */

.ref {
    font-family: var(--mono);
    font-size: .88em;
    letter-spacing: .04em;
    font-variant-ligatures: none;
    white-space: nowrap;
}

.ref--hero {
    font-size: 15px;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--ink);
    padding-bottom: 3px;
    border-bottom: 1.5px solid var(--accent);
    display: inline-block;
}

/* --- Layout -------------------------------------------------------------- */

.shell { display: grid; grid-template-columns: var(--rail) 1fr; min-height: 100vh; }

.rail {
    background: var(--ink);
    color: var(--on-ink-2);
    display: flex;
    flex-direction: column;
    position: sticky;
    top: 0;
    height: 100vh;
    overflow-y: auto;
}

.rail__crest {
    display: flex;
    align-items: center;
    gap: var(--s3);
    padding: var(--s5) var(--s4) var(--s4);
    border-bottom: 1px solid rgba(255, 255, 255, .08);
}

/* The crest carries its own colour and shape, so it needs no plate behind it. */
.rail__mark {
    width: 38px; height: 38px;
    flex: none;
    display: grid; place-items: center;
}

.rail__mark img { width: 100%; height: 100%; object-fit: contain; }

.rail__name {
    font-family: var(--sans);
    font-weight: 600;
    letter-spacing: -.01em;
    font-size: 15px;
    line-height: 1.15;
    color: #fff;
}

.rail__sub {
    font-size: 12px;
    letter-spacing: .09em;
    text-transform: uppercase;
    color: var(--on-ink-3);
    margin-top: 2px;
    /* The administrator's own tagline goes here and can be any length. The rail
       is narrow, so it is bounded at two lines: one line cut "Health & Safety
       Audit Portal" down to "Health & Safety Audit ...", and a tagline the
       administrator cannot read is the same as no tagline at all. Two lines
       still bound the height, so the crest is never pushed around. */
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    overflow: hidden;
    overflow-wrap: anywhere;
}

.rail__nav { padding: var(--s3) var(--s2); flex: 1; }

.rail__group {
    font-size: 12px;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: #7d8a83;
    padding: var(--s4) var(--s3) var(--s2);
}

.rail__link {
    display: flex;
    align-items: center;
    gap: var(--s3);
    padding: 9px var(--s3);
    margin-bottom: 1px;
    color: #cdd4d0;
    text-decoration: none;
    border-radius: var(--radius);
    font-size: 15px;
    position: relative;
    transition: background .12s ease, color .12s ease;
    min-height: 44px;
}

@media (hover: hover) and (pointer: fine) {
    .rail__link:hover { background: rgba(255, 255, 255, .06); color: #fff; }
}

.rail__link[aria-current="page"] {
    background: rgba(255, 255, 255, .09);
    color: #fff;
}

/* A stamped tick of accent against the active item. */
.rail__link[aria-current="page"]::before {
    content: "";
    position: absolute;
    left: -var(--s2);
    left: -8px;
    top: 8px; bottom: 8px;
    width: 2px;
    background: var(--accent);
    border-radius: 0 2px 2px 0;
}

.rail__icon { width: 16px; height: 16px; flex: none; opacity: .85; }

.rail__foot {
    padding: var(--s4);
    border-top: 1px solid rgba(255, 255, 255, .08);
    font-size: 13px;
    color: var(--on-ink-3);
}

.rail__user { display: flex; align-items: center; gap: var(--s3); margin-bottom: var(--s3); }

.rail__avatar {
    width: 30px; height: 30px; flex: none;
    border-radius: 50%;
    background: var(--accent);
    /* Ink on gold, not white. White on this gold measures 1.7 to 1, which is a
       pair of initials nobody can read; ink on it measures 11 to 1. */
    color: var(--ink);
    display: grid; place-items: center;
    font-size: 13px; font-weight: 600;
}

.rail__username { color: #fff; font-size: 14px; line-height: 1.2; }
.rail__role { font-size: 12px; letter-spacing: .07em; text-transform: uppercase; color: var(--on-ink-3); }

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

.topbar {
    display: flex;
    align-items: center;
    gap: var(--s4);
    padding: var(--s4) var(--s6);
    border-bottom: 1px solid var(--rule);
    background: rgba(247, 246, 242, .88);
    backdrop-filter: saturate(1.4) blur(8px);
    position: sticky; top: 0; z-index: 20;
}

.topbar__title { flex: 1; min-width: 0; }
.topbar__eyebrow {
    font-size: 12px; letter-spacing: .13em; text-transform: uppercase;
    color: var(--ink-3); margin-bottom: 1px;
}

.content { padding: var(--s6); max-width: 1180px; width: 100%; }
.content--wide { max-width: none; }

/* --- Page load: a single staggered reveal, not scattered fidgeting -------- */

/* Cut from 420ms to 200ms, and the delays roughly halved.

   This is not a landing page flourish: it runs on every navigation inside the
   portal, so a reviewer moving between reports sees it some dozens of times a
   day. At the old timing the last block did not settle until about 680ms after
   the click, which is the difference between an interface that responds and one
   that is thinking about it. The reveal still does its job, which is to say
   "this is a new page", and it now gets out of the way. */
.stagger > * {
    opacity: 0;
    animation: rise 200ms var(--ease-out) forwards;
}
.stagger > *:nth-child(1) { animation-delay: 0ms; }
.stagger > *:nth-child(2) { animation-delay: 35ms; }
.stagger > *:nth-child(3) { animation-delay: 65ms; }
.stagger > *:nth-child(4) { animation-delay: 90ms; }
.stagger > *:nth-child(5) { animation-delay: 110ms; }
.stagger > *:nth-child(n+6) { animation-delay: 120ms; }

@keyframes rise {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: none; }
}

/* Reduced motion means less movement, not less feedback: colour and opacity
   transitions still tell somebody their click registered, so those are kept and
   only the travel is removed. */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .01ms !important;
        animation-iteration-count: 1 !important;
        transition-property: background-color, border-color, color, opacity, box-shadow !important;
        transition-duration: 120ms !important;
    }
    .stagger > * { opacity: 1; }
    .btn:active:not([disabled]) { transform: none; }
}

/* --- Press feedback ------------------------------------------------------ *
   Everything that can be pressed says so. A button had this and nothing else
   did, which meant that tapping a table row or a rail link, both of which
   navigate, gave no acknowledgement at all until the next screen arrived. On a
   slow connection that is a second of wondering whether the tap registered,
   and the usual response to that is to tap again.

   The scale is smaller than the button's .97 because these are large surfaces:
   a table row shrinking by three per cent is a lurch, where a button doing it
   is a click.                                                                 */

.rail__link,
.ledger tbody tr.row-link,
.evidence,
.viewer__btn,
.ac__option,
.seg__option {
    transition: transform var(--t-press) var(--ease-out),
                background var(--t-quick) ease,
                border-color var(--t-quick) ease,
                color var(--t-quick) ease;
}

.rail__link:active,
.evidence:active,
.viewer__btn:active,
.ac__option:active,
.seg__option:active { transform: scale(.985); }

/* A row is wide and flat, so it is pressed rather than shrunk: scaling
   something that spans the screen reads as the page moving, not the row. */
.ledger tbody tr.row-link:active { transform: translateY(1px); }

@media (prefers-reduced-motion: reduce) {
    .rail__link:active,
    .evidence:active,
    .viewer__btn:active,
    .ac__option:active,
    .seg__option:active,
    .ledger tbody tr.row-link:active { transform: none; }
}

/* --- Cards / panels ------------------------------------------------------ */

/* A sheet of paper laid on the page rather than a card floating above it. The
   border and the shadow together were what made every screen read as boxes
   inside boxes; a single hairline and a flat ground say the same thing about
   where the panel begins and stops competing with its own contents. */
.panel {
    background: var(--paper-raised);
    border: 0;
    border-top: 2px solid var(--rule-strong);
    border-radius: 0;
    box-shadow: none;
}

/* The one place a panel still lifts: when it is the subject of the screen. */
.panel--raised {
    border-top-color: var(--brand);
    box-shadow: var(--shadow-1);
}

.panel__head {
    display: flex; align-items: center; gap: var(--s3);
    padding: var(--s4) 0 var(--s3);
    margin-inline: var(--s5);
    border-bottom: 1px solid var(--rule);
}

.panel__head h2, .panel__head h3 { flex: 1; min-width: 0; }
.panel__body { padding: var(--s5); }
.panel__body--flush { padding: 0; }

/* The dashboard's split. Both tracks are minmax(0, ...) rather than a bare
   fraction: a bare 1fr floors at the content's min-content width, which for a
   table of reference IDs is wider than a phone, and the whole page scrolls
   sideways. Collapsing it is a media query rather than a measurement taken once
   at render, which never fires again when the window is resized. */
.dash-grid {
    display: grid;
    grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
    gap: var(--s5);
    align-items: start;
}

@media (max-width: 900px) {
    .dash-grid { grid-template-columns: minmax(0, 1fr); }
}

/* --- Statistics ---------------------------------------------------------- */

/* Four figures reported across the top of a page, held apart by rules rather
   than boxed individually. Four bordered tiles in a row is the most boxed thing
   an interface can do, and it buys nothing: the numbers are already distinct. */
.stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
    gap: 0;
    background: var(--paper-raised);
    border-top: 2px solid var(--rule-strong);
}

.stat {
    padding: var(--s4) var(--s5) var(--s5);
    position: relative;
}

.stat + .stat { border-left: 1px solid var(--rule); }

/* The colour sits on the rule above the figure it belongs to. */
.stat::after {
    content: "";
    position: absolute; inset: -2px 0 auto 0; height: 2px;
    background: var(--tint, var(--brand));
}

.stat__label {
    font-size: 12px; letter-spacing: .12em; text-transform: uppercase;
    color: var(--ink-3); margin-bottom: var(--s2);
}

.stat__value {
    font-family: var(--serif);
    font-size: 34px;
    line-height: 1;
    font-variant-numeric: tabular-nums lining-nums;
}

.stat__note { font-size: 14px; color: var(--ink-3); margin-top: var(--s2); }

/* --- Ledger tables ------------------------------------------------------- */

.ledger { width: 100%; border-collapse: collapse; font-size: 15px; }

.ledger th {
    text-align: left;
    font-weight: 500;
    font-size: 12px;
    letter-spacing: .11em;
    text-transform: uppercase;
    color: var(--ink-3);
    padding: var(--s3) var(--s4);
    border-bottom: 1px solid var(--rule-strong);
    white-space: nowrap;
    background: var(--paper-sunk);
}

.ledger td {
    padding: var(--s3) var(--s4);
    border-bottom: 1px solid var(--rule);
    vertical-align: middle;
}

.ledger tbody tr { transition: background .1s ease; }
@media (hover: hover) and (pointer: fine) {
    .ledger tbody tr:hover { background: var(--paper-sunk); }
}
.ledger tbody tr:last-child td { border-bottom: 0; }
.ledger .num { font-variant-numeric: tabular-nums; text-align: right; }

.ledger__wrap { overflow-x: auto; min-width: 0; }

.row-link { cursor: pointer; }

/* --- Status marks -------------------------------------------------------- *
   Statuses read as an inked stamp: uppercase, tracked, ruled underneath in
   their own colour rather than filled with it.                              */

.status {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    letter-spacing: .09em;
    text-transform: uppercase;
    font-weight: 500;
    color: var(--tone, var(--ink-2));
    white-space: nowrap;
}

.status::before {
    content: "";
    width: 6px; height: 6px; border-radius: 50%;
    background: currentColor;
    flex: none;
}

.status--block {
    padding: 5px 10px 4px;
    border: 1px solid currentColor;
    border-radius: var(--radius);
    background: color-mix(in srgb, var(--tone, var(--ink-2)) 7%, transparent);
}

/* --- Buttons ------------------------------------------------------------- */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--s2);
    padding: 9px var(--s4);
    /* 44px is the smallest control a finger hits reliably, and this portal is
       used by people whose aim is not what it was. It was 37px. */
    min-height: 44px;
    border: 1px solid var(--rule-strong);
    border-radius: var(--radius);
    background: var(--paper-raised);
    color: var(--ink);
    font-size: 15px;
    font-weight: 500;
    cursor: pointer;
    text-decoration: none;
    white-space: nowrap;
    transition:
        background var(--t-quick) ease,
        border-color var(--t-quick) ease,
        color var(--t-quick) ease,
        transform var(--t-press) var(--ease-out);
}

/* Touch devices fire :hover on tap and then keep it, so a tapped button would
   stay looking hovered. Gated to pointers that can genuinely hover. */
@media (hover: hover) and (pointer: fine) {
    .btn:hover { background: var(--paper-sunk); border-color: var(--ink-4); color: var(--ink); }
}

/* Scale rather than a colour change: it is the press itself the hand is looking
   for confirmation of, and scale reads as pressure in a way colour never does. */
.btn:active:not([disabled]) { transform: scale(.97); }
.btn[disabled] { opacity: .5; cursor: not-allowed; transform: none; }

.btn--primary {
    background: var(--brand);
    border-color: var(--brand);
    color: #fff;
}
@media (hover: hover) and (pointer: fine) {
    .btn--primary:hover {
        background: color-mix(in srgb, var(--brand) 86%, #000);
        border-color: color-mix(in srgb, var(--brand) 86%, #000);
        color: #fff;
    }
}

.btn--danger { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 35%, var(--rule)); }
@media (hover: hover) and (pointer: fine) {
    .btn--danger:hover { background: color-mix(in srgb, var(--danger) 8%, transparent); color: var(--danger); }
}

.btn--ghost { background: transparent; border-color: transparent; }
@media (hover: hover) and (pointer: fine) {
    .btn--ghost:hover { background: var(--paper-sunk); border-color: transparent; }
}

/* The compact variant stays compact where there is a mouse, and grows to the
   full target on anything touched. A dense toolbar is worth having on a desk
   and is worth nothing on a phone. */
.btn--sm { padding: 6px var(--s3); font-size: 14px; min-height: 34px; }

@media (pointer: coarse) {
    .btn--sm { min-height: 44px; padding-inline: var(--s4); }
}
.btn--lg { padding: 14px var(--s5); font-size: 17px; }
.btn--block { width: 100%; }

/* --- Forms --------------------------------------------------------------- */

.field { margin-bottom: var(--s4); }

.field__label {
    display: block;
    font-size: 13px;
    letter-spacing: .05em;
    text-transform: uppercase;
    color: var(--ink-2);
    margin-bottom: 6px;
    font-weight: 500;
}

.field__hint { font-size: 14px; color: var(--ink-3); margin-top: 5px; }
.field__error { font-size: 14px; color: var(--danger); margin-top: 5px; }
/* A hint that is telling the reader something is currently wrong, rather than
   explaining the field. Weightier than a hint and lighter than an error,
   because nothing has been rejected: the value is simply not going to work. */
.field__hint--warn { color: var(--warning); font-weight: 500; }

.input, .select, .textarea {
    width: 100%;
    padding: 9px var(--s3);
    border: 1px solid var(--rule-strong);
    border-radius: var(--radius);
    background: var(--paper-raised);
    transition: border-color .12s ease, box-shadow .12s ease;
}

.input:focus, .select:focus, .textarea:focus {
    outline: none;
    border-color: var(--brand);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 14%, transparent);
}

.input[aria-invalid="true"], .textarea[aria-invalid="true"] { border-color: var(--danger); }

.textarea { min-height: 96px; resize: vertical; font-family: inherit; }

.select {
    appearance: none;
    background-image: linear-gradient(45deg, transparent 50%, var(--ink-3) 50%),
                      linear-gradient(135deg, var(--ink-3) 50%, transparent 50%);
    background-position: calc(100% - 16px) 50%, calc(100% - 11px) 50%;
    background-size: 5px 5px, 5px 5px;
    background-repeat: no-repeat;
    padding-right: var(--s6);
}

.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s4); }

/* Drop zone for the report PDF. */
.dropzone {
    border: 1.5px dashed var(--rule-strong);
    border-radius: var(--radius-lg);
    padding: var(--s7) var(--s5);
    text-align: center;
    background: var(--paper-sunk);
    cursor: pointer;
    transition: border-color .15s ease, background .15s ease;
}
@media (hover: hover) and (pointer: fine) {
    .dropzone:hover, .dropzone.is-over { border-color: var(--brand); background: color-mix(in srgb, var(--brand) 5%, var(--paper-sunk)); }
}
.dropzone__title { font-family: var(--sans); font-weight: 600; font-size: 16px; margin-bottom: 4px; }
.dropzone__hint { font-size: 14px; color: var(--ink-3); }

.progress {
    height: 5px;
    background: var(--rule);
    border-radius: 99px;
    overflow: hidden;
    margin-top: var(--s3);
}
/* Width, not a scaleX transform, and deliberately so. Animating width costs a
   layout where a transform would not, but this bar has fully rounded ends and
   scaling it horizontally turns those caps into ellipses. It is driven by a
   batch loop that ticks a handful of times, not per frame, so the layout is
   cheaper than the distortion. If this ever becomes per-frame, drop the radius
   and switch to transform. */
.progress__bar {
    height: 100%;
    background: var(--brand);
    border-radius: 99px;
    transition: width .25s ease;
}

/* --- Notices ------------------------------------------------------------- */

.notice {
    border: 1px solid var(--rule-strong);
    border-left: 3px solid var(--tone, var(--ink-3));
    border-radius: var(--radius);
    padding: var(--s3) var(--s4);
    background: var(--paper-raised);
    font-size: 15px;
    margin-bottom: var(--s4);
}
.notice--error   { --tone: var(--danger);  background: color-mix(in srgb, var(--danger) 4%, var(--paper-raised)); }
.notice--warning { --tone: var(--warning); background: color-mix(in srgb, var(--warning) 5%, var(--paper-raised)); }
.notice--success { --tone: var(--success); background: color-mix(in srgb, var(--success) 5%, var(--paper-raised)); }
.notice--info    { --tone: var(--brand); }
.notice__title { font-weight: 600; margin-bottom: 2px; }

/* --- Toasts -------------------------------------------------------------- */

.toasts {
    position: fixed;
    right: var(--s5); bottom: var(--s5);
    display: flex; flex-direction: column; gap: var(--s2);
    z-index: 200;
    max-width: 380px;
}

.toast {
    background: var(--ink);
    color: #f2f4f3;
    padding: var(--s3) var(--s4);
    border-radius: var(--radius);
    box-shadow: var(--shadow-3);
    font-size: 15px;
    display: flex; gap: var(--s3); align-items: flex-start;
    animation: toast-in .28s cubic-bezier(.2, .8, .3, 1);
    border-left: 3px solid var(--tone, var(--brand));
}
.toast--error   { --tone: #e06666; }
.toast--success { --tone: #57b37e; }

@keyframes toast-in {
    from { opacity: 0; transform: translateX(12px) scale(.98); }
    to   { opacity: 1; transform: none; }
}

/* --- Timeline ------------------------------------------------------------ */

.timeline { position: relative; padding-left: var(--s5); }
.timeline::before {
    content: "";
    position: absolute; left: 5px; top: 6px; bottom: 6px;
    width: 1px; background: var(--rule-strong);
}

.event { position: relative; padding-bottom: var(--s4); }
.event::before {
    content: "";
    position: absolute; left: calc(-1 * var(--s5) + 2px); top: 6px;
    width: 7px; height: 7px; border-radius: 50%;
    background: var(--paper); border: 1.5px solid var(--ink-4);
}
.event--mark::before { background: var(--accent); border-color: var(--accent); }
.event__meta { font-size: 13px; color: var(--ink-3); }
.event__text { font-size: 15px; }

/* --- Comments ------------------------------------------------------------ */

.comment { padding: var(--s4) 0; border-bottom: 1px solid var(--rule); }
.comment:last-child { border-bottom: 0; }
.comment__head { display: flex; align-items: baseline; gap: var(--s2); margin-bottom: 5px; }
.comment__author { font-weight: 600; font-size: 15px; }
.comment__meta { font-size: 13px; color: var(--ink-3); }
.comment__body { font-size: 15px; white-space: pre-wrap; }

/* An internal note is visually unmistakable, it must never be mistaken for
   something the consultant can see. */
.comment--internal {
    background: repeating-linear-gradient(
        135deg,
        color-mix(in srgb, var(--warning) 5%, transparent) 0 8px,
        transparent 8px 16px
    );
    border-left: 3px solid var(--warning);
    padding-left: var(--s4);
    margin-left: calc(-1 * var(--s4));
}

.tag-internal {
    font-size: 12px; letter-spacing: .1em; text-transform: uppercase;
    color: var(--warning); border: 1px solid currentColor;
    padding: 1px 5px; border-radius: 2px;
}

/* --- Evidence gallery ---------------------------------------------------- */

.gallery { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: var(--s3); }

.evidence {
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    overflow: hidden;
    background: var(--paper-raised);
    text-decoration: none;
    color: inherit;
    display: block;
    transition: border-color .12s ease, box-shadow .12s ease;
}
@media (hover: hover) and (pointer: fine) {
    .evidence:hover { border-color: var(--ink-4); box-shadow: var(--shadow-2); }
}
.evidence__thumb {
    aspect-ratio: 4 / 3;
    background: var(--paper-sunk);
    display: grid; place-items: center;
    color: var(--ink-4);
    font-size: 12px; letter-spacing: .1em; text-transform: uppercase;
}
.evidence__thumb img { width: 100%; height: 100%; object-fit: cover; }
.evidence__meta { padding: var(--s2) var(--s3); font-size: 13px; }
.evidence__name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.evidence__size { color: var(--ink-3); font-size: 12px; }

/* --- Sign-in ------------------------------------------------------------- *
   A single centred plate on ink, so entering the portal feels like presenting
   credentials rather than opening an app.                                    */

.gate {
    min-height: 100vh;
    display: grid;
    grid-template-columns: 1fr;
    place-items: center;
    padding: var(--s5);
    background:
        radial-gradient(1100px 600px at 15% -10%, color-mix(in srgb, var(--brand) 26%, transparent), transparent 60%),
        radial-gradient(800px 500px at 110% 110%, color-mix(in srgb, var(--accent) 18%, transparent), transparent 60%),
        var(--ink);
}

.gate__plate {
    width: 100%;
    max-width: 412px;
    background: var(--paper-raised);
    border-radius: var(--radius-lg);
    box-shadow: 0 24px 60px rgba(0, 0, 0, .4);
    padding: var(--s7) var(--s6) var(--s6);
    animation: rise .5s cubic-bezier(.2, .7, .3, 1);
}

/* The crest carries its own colour and shape, so it needs no plate behind it. */
.gate__crest {
    width: 54px; height: 54px;
    display: grid; place-items: center;
    margin-bottom: var(--s5);
}
.gate__crest img { width: 100%; height: 100%; object-fit: contain; }

.gate__title { font-size: 24px; margin-bottom: 5px; }
.gate__sub { color: var(--ink-3); font-size: 15px; margin-bottom: var(--s6); }
.gate__foot {
    margin-top: var(--s5); padding-top: var(--s4);
    border-top: 1px solid var(--rule);
    font-size: 13px; color: var(--ink-3); text-align: center;
}

/* --- Empty states -------------------------------------------------------- */

.empty { text-align: center; padding: var(--s8) var(--s5); color: var(--ink-3); }
.empty__title { font-family: var(--sans); font-weight: 600; font-size: 17px; color: var(--ink-2); margin-bottom: 6px; }
.empty__text { font-size: 15px; max-width: 44ch; margin: 0 auto var(--s4); }

/* --- Utilities ----------------------------------------------------------- */

.stack > * + * { margin-top: var(--gap, var(--s4)); }
.row { display: flex; align-items: center; gap: var(--s3); }
.row--between { justify-content: space-between; }
.row--wrap { flex-wrap: wrap; }
.spacer { flex: 1; }
.muted { color: var(--ink-3); }
.small { font-size: 14px; }
.tabular { font-variant-numeric: tabular-nums; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hidden { display: none !important; }
.sr-only {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.skeleton {
    background: linear-gradient(90deg, var(--paper-sunk) 25%, var(--rule) 37%, var(--paper-sunk) 63%);
    background-size: 400% 100%;
    animation: shimmer 1.3s ease-in-out infinite;
    border-radius: var(--radius);
    height: 14px;
}
@keyframes shimmer { from { background-position: 100% 0; } to { background-position: 0 0; } }

/* --- Responsive ---------------------------------------------------------- */

@media (max-width: 900px) {
    .shell { grid-template-columns: 1fr; }

    .rail {
        position: fixed; inset: 0 auto 0 0;
        width: var(--rail); height: 100vh; z-index: 100;
        transform: translateX(-100%);
        transition: transform .22s cubic-bezier(.2, .8, .3, 1);
        box-shadow: var(--shadow-3);
    }
    .rail.is-open { transform: none; }

    .scrim {
        position: fixed; inset: 0; background: rgba(20, 24, 26, .45);
        z-index: 90; backdrop-filter: blur(2px);
    }

    .content { padding: var(--s4); }

    /* Page actions wrap under the title rather than running off the right edge.
       A page with two or three of them (facilities has "Import" and "Add") is
       wider than a phone once the title has taken its share. */
    .topbar { padding: var(--s3) var(--s4); flex-wrap: wrap; row-gap: var(--s3); }
    .topbar__title { flex-basis: 100%; }

    .field-row { grid-template-columns: 1fr; }
}

@media (min-width: 901px) { .rail__toggle { display: none; } }

/* --- Viewer -------------------------------------------------------------- *
   Reports and evidence open in place rather than in a new tab, so a reviewer
   keeps the report's context, its status, comments and history, beside them. */

.viewer {
    position: fixed;
    inset: 0;
    z-index: 300;
    display: grid;
    grid-template-rows: auto 1fr;
    background: rgba(20, 24, 26, .72);
    backdrop-filter: blur(3px);
    animation: viewer-in .18s ease;
}

@keyframes viewer-in { from { opacity: 0; } to { opacity: 1; } }

.viewer__bar {
    display: flex;
    align-items: center;
    gap: var(--s3);
    padding: var(--s3) var(--s4);
    background: var(--ink);
    color: var(--on-ink);
}

.viewer__title { flex: 1; min-width: 0; font-family: var(--sans); font-weight: 600; font-size: 15px; }
.viewer__sub { font-size: 13px; color: #98a39c; }

.viewer__frame {
    margin: 0 var(--s4) var(--s4);
    border: 0;
    border-radius: var(--radius-lg);
    background: var(--paper-raised);
    box-shadow: var(--shadow-3);
    width: calc(100% - var(--s4) * 2);
    height: 100%;
}

.viewer__media {
    margin: 0 auto var(--s4);
    display: block;
    max-width: calc(100% - var(--s4) * 2);
    max-height: calc(100% - var(--s4));
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-3);
    background: #000;
    align-self: center;
}

.viewer__btn {
    background: rgba(255, 255, 255, .1);
    border: 1px solid rgba(255, 255, 255, .16);
    color: var(--on-ink);
}
@media (hover: hover) and (pointer: fine) {
    .viewer__btn:hover { background: rgba(255, 255, 255, .18); color: #fff; border-color: rgba(255,255,255,.28); }
}

@media (max-width: 900px) {
    .viewer__frame { margin: 0 var(--s2) var(--s2); width: calc(100% - var(--s2) * 2); }
}

/* --- Permission matrix --------------------------------------------------- *
   The role editor. Two columns on a wide screen so a whole area of the portal
   can be taken in at once, and one on a narrow one where a cramped checkbox is
   worse than a longer scroll.                                                */

.perm-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 2px var(--s5);
}

.perm {
    display: flex;
    align-items: flex-start;
    gap: var(--s3);
    padding: 9px var(--s3);
    margin: 0 calc(var(--s3) * -1);
    border-radius: var(--radius);
    cursor: pointer;
    transition: background .12s ease;
}
@media (hover: hover) and (pointer: fine) {
    .perm:hover { background: var(--paper-sunk); }
}

.perm input[type="checkbox"] {
    margin-top: 2px;
    width: 15px;
    height: 15px;
    accent-color: var(--brand);
    flex: none;
    cursor: pointer;
}
.perm input[type="checkbox"]:disabled { cursor: default; }

.perm__name { display: block; font-size: 15px; }
.perm__slug {
    display: block;
    font-family: var(--mono);
    font-size: 13px;
    color: var(--ink-3);
    margin-top: 1px;
}

/* The wildcard, set apart because it is a different kind of decision. */
.perm--lead {
    border: 1px solid var(--rule-strong);
    background: var(--paper-sunk);
    margin: var(--s4) 0 0;
    padding: var(--s4);
}
.perm--lead .perm__name { font-family: var(--sans); font-weight: 600; font-size: 15px; }
.perm--lead .perm__slug { font-family: inherit; font-size: 14px; line-height: 1.5; margin-top: 4px; }

/* Full access makes the individual permissions moot; they stay legible so the
   reader can see what is being superseded. */
.is-muted { opacity: .45; pointer-events: none; }

/* --- Attached files ------------------------------------------------------ *
   The evidence queue on the submission form, before anything is sent.        */

.attach {
    display: flex;
    align-items: center;
    gap: var(--s3);
    padding: var(--s3);
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    background: var(--paper-sunk);
}
.attach__name { font-size: 15px; }
.attach__meta { font-size: 13px; color: var(--ink-3); }
.attach .input { max-width: 220px; }

.input--sm { padding: 6px var(--s3); font-size: 14px; }

.dropzone--slim { padding: var(--s5); }

@media (max-width: 700px) {
    .attach { flex-wrap: wrap; }
    .attach .input { max-width: none; width: 100%; }
}

/* --- Type-ahead picker --------------------------------------------------- *
   Used for the facility register, where a dropdown of thousands would be
   neither loadable nor searchable.                                          */

.ac { position: relative; }

.ac__list {
    position: absolute;
    z-index: 40;
    left: 0;
    right: 0;
    top: calc(100% + 4px);
    margin: 0;
    padding: 4px;
    list-style: none;
    max-height: 280px;
    overflow-y: auto;
    background: var(--paper);
    border: 1px solid var(--rule-strong);
    border-radius: var(--radius);
    box-shadow: var(--shadow-3);
}

.ac__option {
    display: block;
    padding: 8px 10px;
    border-radius: var(--radius);
    cursor: pointer;
}
/* Keyboard selection and pointer hover want the same treatment, but only the
   pointer half may be gated: a touch device still moves this highlight with the
   arrow keys on an attached keyboard, and gating both would lose it. */
.ac__option.is-active { background: var(--paper-sunk); }

@media (hover: hover) and (pointer: fine) {
    .ac__option:hover { background: var(--paper-sunk); }
}

.ac__label { display: block; font-size: 15px; }
.ac__detail { display: block; font-size: 13px; color: var(--ink-3); margin-top: 1px; }

.ac__empty {
    padding: 10px;
    font-size: 14px;
    color: var(--ink-3);
}

/* --- Per-account access -------------------------------------------------- *
   One row per permission, with a three-way choice: follow the role, allow, or
   refuse.                                                                    */

.access__grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
    gap: 2px var(--s5);
}

.access__row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s4);
    padding: 8px 0;
    border-bottom: 1px solid var(--rule);
}
.access__row:last-child { border-bottom: 0; }

.access__name { font-size: 15px; }
.access__meta { font-size: 13px; color: var(--ink-3); margin-top: 1px; }
.access__meta .ref { font-size: 12px; }

/* A segmented control rather than a dropdown: all three states stay readable,
   and the current one is visible without opening anything. */
.seg {
    display: inline-flex;
    flex: none;
    border: 1px solid var(--rule-strong);
    border-radius: var(--radius);
    overflow: hidden;
    background: var(--paper);
}

.seg__option {
    position: relative;
    display: block;
    padding: 5px 11px;
    font-size: 13px;
    color: var(--ink-2);
    cursor: pointer;
    border-right: 1px solid var(--rule);
    user-select: none;
}
.seg__option:last-child { border-right: 0; }
@media (hover: hover) and (pointer: fine) {
    .seg__option:hover { background: var(--paper-sunk); }
}

.seg__option input {
    position: absolute;
    opacity: 0;
    width: 100%;
    height: 100%;
    inset: 0;
    margin: 0;
    cursor: pointer;
}

.seg__option:has(input:checked) { color: #fff; background: var(--ink-2); }
.seg__option--grant:has(input:checked) { background: var(--success); }
.seg__option--revoke:has(input:checked) { background: var(--danger); }

/* Keyboard focus has to be visible, since the real input is hidden. */
.seg__option:has(input:focus-visible) { outline: 2px solid var(--brand); outline-offset: -2px; }

@media (max-width: 620px) {
    .access__row { flex-direction: column; align-items: flex-start; }
}

/* --- Compliance review ---------------------------------------------------- *
   The advisory score is deliberately drawn in a different visual language from
   the report's actual status. It is a dashed, sunk card with its own heading, and
   it never borrows the status palette, a reviewer's eye must not learn to read
   the machine's figure as the Ministry's verdict.                             */

.advisory {
    display: flex;
    flex-direction: column;
    gap: var(--s1);
    padding: var(--s3) var(--s4);
    border: 1px dashed var(--rule-strong);
    border-radius: var(--radius-lg);
    background: var(--paper-sunk);
    min-width: 260px;
}

.advisory__label {
    font-family: var(--sans);
    font-size: .68rem;
    font-weight: 600;
    letter-spacing: .07em;
    text-transform: uppercase;
    color: var(--ink-3);
}

/* "advisory" sits inside the same text node as the number, so it cannot be
   cropped out of a screenshot of the figure. */
.advisory__value { font-size: 1.5rem; font-weight: 600; color: var(--ink-2); }

.advisory__foot { color: var(--ink-3); max-width: 46ch; }

/* --- Verdict chips ------------------------------------------------------- */

.verdict {
    display: inline-block;
    padding: 1px var(--s2);
    border-radius: 999px;
    border: 1px solid var(--rule-strong);
    font-family: var(--sans);
    font-size: .68rem;
    font-weight: 600;
    letter-spacing: .03em;
    text-transform: uppercase;
    white-space: nowrap;
    color: var(--ink-2);
    background: var(--paper-raised);
}

.verdict--covered   { color: var(--success); border-color: color-mix(in srgb, var(--success) 40%, var(--rule)); }
.verdict--partial   { color: var(--warning); border-color: color-mix(in srgb, var(--warning) 40%, var(--rule)); }
.verdict--absent    { color: var(--danger);  border-color: color-mix(in srgb, var(--danger) 40%, var(--rule)); }
.verdict--critical  { color: var(--danger);  border-color: color-mix(in srgb, var(--danger) 40%, var(--rule)); }
.verdict--major     { color: var(--warning); border-color: color-mix(in srgb, var(--warning) 40%, var(--rule)); }
.verdict--minor,
.verdict--advisory,
.verdict--not_applicable,
.verdict--unable_to_verify,
.verdict--pending   { color: var(--ink-3); }

/* --- Findings ------------------------------------------------------------ */

.finding {
    padding: var(--s3);
    border-left: 2px solid var(--rule);
    background: var(--paper-raised);
}

.finding + .finding { border-top: 1px solid var(--rule); }
.finding--critical { border-left-color: var(--danger); }
.finding--major    { border-left-color: var(--warning); }
.finding--minor    { border-left-color: var(--rule-strong); }

.finding p { margin: var(--s1) 0 0; }

.quote {
    margin: var(--s2) 0 0;
    padding: var(--s2) var(--s3);
    border-left: 2px solid var(--rule-strong);
    background: var(--paper-sunk);
    font-family: var(--serif);
    font-size: .9rem;
    color: var(--ink-2);
}

.quote cite { font-family: var(--sans); font-size: .75rem; font-style: normal; color: var(--ink-3); }

.finding-group > summary {
    cursor: pointer;
    padding: var(--s2) 0;
    font-weight: 600;
    border-bottom: 1px solid var(--rule);
}

.finding-group > summary::marker { color: var(--ink-4); }

/* A button that reads as a link: used for page citations and row drill-downs. */
.linkish {
    background: none;
    border: 0;
    padding: 0;
    font: inherit;
    color: var(--brand);
    text-decoration: underline;
    text-underline-offset: 2px;
    cursor: pointer;
}

@media (hover: hover) and (pointer: fine) {
    /* Not the gold: these sit on paper, where it measures 1.5 to 1. A link
       that becomes unreadable when you point at it is worse than one that does
       not respond, so this darkens towards the ink instead. */
    .linkish:hover { color: var(--ink); text-decoration-thickness: 2px; }
}

.tag--internal {
    display: inline-block;
    padding: 1px var(--s2);
    border-radius: 999px;
    border: 1px solid var(--rule-strong);
    background: var(--paper-sunk);
    font-family: var(--sans);
    font-size: .68rem;
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--ink-3);
}

@media (prefers-color-scheme: dark) {
    .advisory { background: rgba(255, 255, 255, .03); }
    .quote { background: rgba(255, 255, 255, .03); }
}

/* --- Who built it -------------------------------------------------------- */

.gate__builder {
    margin-top: var(--s4);
    padding-top: var(--s3);
    border-top: 1px solid var(--rule);
    display: flex;
    justify-content: center;
}

.gate__builder img { display: block; width: 168px; height: auto; }

/* The technical half of a capability notice. Set apart and set down, because it
   is not addressed to the person reading the screen: it is the line they hand
   to whoever runs the server. It appears only when something needs fixing. */
.notice__remedy {
    display: block;
    margin-top: var(--s3);
    padding-top: var(--s3);
    border-top: 1px solid var(--rule);
    font-size: 14px;
    color: var(--ink-3);
}

.notice__remedy-label {
    display: block;
    font-size: 12px;
    font-weight: 600;
    letter-spacing: .09em;
    text-transform: uppercase;
    color: var(--ink-4);
    margin-bottom: 2px;
}

/* --- A guide, and a checklist of what worked ------------------------------ */

.guide { max-width: 68ch; }
.guide__para { margin: 0 0 var(--s3); line-height: 1.65; }
.guide__para:last-child { margin-bottom: 0; }

.guide--steps { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s5); }

.guide__step { display: grid; grid-template-columns: 2.4rem minmax(0, 1fr); gap: var(--s3); }

.guide__num {
    font-family: var(--mono);
    font-size: 15px;
    font-weight: 600;
    color: var(--brand);
    padding-top: 2px;
}

.guide__title { margin: 0 0 var(--s2); font-size: 16px; }
.guide__body > .notice { margin-top: var(--s3); }

.guide__url {
    margin: 0 0 var(--s3);
    padding: var(--s3);
    background: var(--paper-sunk);
    border-left: 2px solid var(--rule-strong);
    font-family: var(--mono);
    font-size: 13px;
    overflow-x: auto;
    white-space: pre-wrap;
    word-break: break-all;
}

.guide__faults { margin: 0; display: grid; gap: var(--s2); }
.guide__faults dt { font-weight: 600; }
.guide__faults dd { margin: 0 0 var(--s3); color: var(--ink-2); }
.guide__faults dd:last-child { margin-bottom: 0; }

/* Each step of a connection test, so a failure says which one rather than
   leaving an administrator to guess between a bad key and an unshared folder. */
.checklist { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s2); }

.checklist__item {
    display: grid;
    grid-template-columns: 1.4rem minmax(0, 1fr);
    gap: var(--s2);
    align-items: start;
}

.checklist__mark { font-weight: 700; line-height: 1.5; }
.checklist__item--ok  .checklist__mark { color: var(--success); }
.checklist__item--bad .checklist__mark { color: var(--danger); }

/* --- Storage pattern tokens ----------------------------------------------- */
/* A legend rather than a paragraph. Eleven tokens and their meanings inside a
   help sentence read as a wall of text; in two columns they are scanned. Folded
   away by default because somebody who already knows the pattern they want does
   not need to walk past the list every time they visit this screen. */
.tokens__wrap { margin-top: 8px; }
.tokens__wrap > summary {
    cursor: pointer; font-size: 14px; color: var(--ink-2);
    padding: 4px 0; width: fit-content;
}
.tokens__wrap > summary:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }

.tokens {
    display: grid; grid-template-columns: max-content 1fr;
    gap: 6px var(--s3); margin: 8px 0 0; align-items: baseline;
}
.tokens dd { margin: 0; font-size: 14px; color: var(--ink-3); }

.token {
    font-family: var(--mono); font-size: 13px;
    background: var(--paper-sunk); border: 1px solid var(--rule);
    border-radius: var(--radius); padding: 2px 6px;
    color: var(--ink); cursor: pointer;
}
.token:hover { border-color: var(--ink-3); }
.token:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
