/*
 * MockStop design system v1.0 — the public site, the organization portal and the founder
 * dashboard, from one set of tokens.
 *
 * Every value here comes from "MockStop Design System.dc.html" rather than from taste. Where a
 * number looks arbitrary it is not: it is the one the design names, and changing it here changes
 * it everywhere, which is the point of having the file at all.
 *
 * **White on the web, navy for product chrome.** The marketing site is a light document somebody
 * reads on a laptop in an office; the portal and the app are tools used for a long time in a dim
 * cab. They share a palette and a typeface and they are not the same surface.
 *
 * No framework and no build step. A few hundred lines of CSS outlives a toolchain, and these
 * pages have to still open in two years.
 */

/* ---------- type ----------
 *
 * Barlow and Barlow Condensed, self-hosted from the same files the Flutter app bundles. Served
 * from this origin rather than a font CDN: the CSP for these pages is default-src 'none' and
 * should stay that way, a truck-stop connection does not need a second DNS lookup, and a landing
 * page that renders in a different face when Google is slow looks broken. */

@font-face {
    font-family: "Barlow";
    src: url("/fonts/barlow-400.woff2") format("woff2");
    font-weight: 400; font-style: normal; font-display: swap;
}

@font-face {
    font-family: "Barlow";
    src: url("/fonts/barlow-600.woff2") format("woff2");
    font-weight: 600; font-style: normal; font-display: swap;
}

@font-face {
    font-family: "Barlow";
    src: url("/fonts/barlow-700.woff2") format("woff2");
    font-weight: 700; font-style: normal; font-display: swap;
}

@font-face {
    font-family: "Barlow Condensed";
    src: url("/fonts/barlow-condensed-700.woff2") format("woff2");
    font-weight: 700; font-style: normal; font-display: swap;
}

:root {
    /* Colour. Named for what it is, not where it is used, so a rename never lies. */
    --ink: #0B1424;          /* text, portal chrome */
    --ink-2: #1B2A41;        /* hover, secondary dark */
    --red: #C8102E;          /* primary action — and nothing else */
    --red-pressed: #A30D25;
    --slate-700: #3D4757;    /* body on white */
    --slate-600: #4A5565;    /* secondary text */
    --slate-500: #6B7482;    /* meta text */
    --border: #E3E6EB;       /* the system's divider, for tables and rules */
    --hairline: #ECEDF0;     /* the same idea one step quieter, for card edges */
    --surface: #F5F5F7;      /* section fill */
    --white: #ffffff;

    /* Depth instead of outlines.
     *
     * The system names one border colour and it is right for a table rule, where two rows have to
     * be told apart. Drawn around every card it turns a page into a grid of boxes, which is what
     * "basic" looks like. A card is lifted off the page instead: a hairline for the edge and two
     * very soft shadows for the lift — one tight for the contact, one wide and faint for the
     * ambient. Both are near-black at low alpha rather than grey, so they read as shadow rather
     * than as a second border. */
    --lift: 0 1px 2px rgba(11, 20, 36, .04), 0 4px 16px rgba(11, 20, 36, .05);
    --lift-raised: 0 2px 4px rgba(11, 20, 36, .05), 0 16px 48px rgba(11, 20, 36, .09);

    /* Status. Always paired with an icon and a word — see .badge. */
    --ready: #14804A;
    --ready-bg: #E7F5EC;
    --ready-fg: #0E6B3C;
    --practicing: #D98A1C;
    --practicing-bg: #FDF1E1;
    --practicing-fg: #8A4A06;
    --attention: #A11B12;
    --attention-bg: #FDECEA;
    --pending-bg: #EAF0FC;
    --pending-fg: #1F4FB8;
    --focus: #2F6FEB;

    /* On navy: the portal's own scale, where slate on white would vanish. */
    --on-navy: #ffffff;
    --on-navy-dim: #C5CCD6;
    --on-navy-muted: #AEB7C4;
    --on-navy-line: rgba(255, 255, 255, .12);
    --on-navy-fill: rgba(255, 255, 255, .07);

    --sans: "Barlow", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
    --display: "Barlow Condensed", "Barlow", system-ui, sans-serif;

    /* Spacing. The eight the system names, and no others. */
    --s1: 4px; --s2: 8px; --s3: 12px; --s4: 16px;
    --s5: 24px; --s6: 32px; --s7: 48px; --s8: 64px;

    --r-badge: 4px;
    --r-control: 8px;
    --r-card: 10px;
    --r-media: 14px;

    --site: 1240px;   /* content max on the marketing site */
    --portal: 1400px; /* and in the portal, which has more to show */
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
    margin: 0;
    background: var(--white);
    color: var(--slate-700);
    font: 400 17px/1.6 var(--sans);
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
}

a { color: var(--red); text-decoration: none; }
a:hover { color: var(--red-pressed); }

/* One focus style, visible on every surface. Never removed — the people using this product are
   often on a phone in one hand, and a keyboard user with no focus ring is lost. */
:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }

h1, h2, h3, h4 { margin: 0; color: var(--ink); }
p { margin: 0; }

/* Display and H2 are condensed: it is how highway signage sets the words that matter, and it lets
   a headline be genuinely large without wrapping to four lines on a phone. */
.display {
    font-family: var(--display);
    font-weight: 700;
    font-size: clamp(42px, 6vw, 88px);
    line-height: .98;
    letter-spacing: -.005em;
}

h2.section-title {
    font-family: var(--display);
    font-weight: 700;
    font-size: clamp(38px, 5vw, 60px);
    line-height: 1;
}

h3 { font-size: 22px; font-weight: 700; line-height: 1.25; }

.eyebrow {
    display: block;
    font-size: 13px;
    font-weight: 600;
    letter-spacing: .14em;
    text-transform: uppercase;
    margin-bottom: var(--s4);
    color: var(--red);
}

.lede { font-size: 19px; line-height: 1.6; color: var(--slate-600); }
.meta { font-size: 15px; color: var(--slate-500); }

.wrap { max-width: var(--site); margin: 0 auto; padding: 0 clamp(20px, 4vw, 40px); }

/* ---------- buttons ----------
 *
 * Minimum 44px hit target, one primary action per view, and red reserved for primary action and
 * critical status. The last rule is why there is a .btn-dark: a second prominent button on a
 * light section needs weight without borrowing the colour that means "this is the action". */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--s2);
    min-height: 44px;
    padding: 0 var(--s5);
    border: 1px solid transparent;
    border-radius: var(--r-control);
    font: 600 16px/1 var(--sans);
    cursor: pointer;
    transition: background .15s ease, border-color .15s ease, color .15s ease;
    white-space: nowrap;
}

.btn-lg { min-height: 52px; padding: 0 var(--s6); font-size: 17px; }

.btn-primary { background: var(--red); color: var(--white); }
.btn-primary:hover { background: var(--red-pressed); color: var(--white); }

.btn-secondary {
    background: var(--white);
    color: var(--ink);
    border-color: #DCDEE3;
}
.btn-secondary:hover { background: var(--surface); color: var(--ink); border-color: #cfd4dc; }

.btn-tertiary { background: transparent; color: var(--ink); padding: 0 var(--s3); }
.btn-tertiary:hover { background: var(--surface); color: var(--ink); }

.btn-dark { background: var(--ink); color: var(--white); }
.btn-dark:hover { background: var(--ink-2); color: var(--white); }

.btn-destructive { background: var(--attention); color: var(--white); }
.btn-destructive:hover { background: #8c150e; color: var(--white); }

.btn[disabled], .btn.is-disabled {
    background: var(--surface);
    color: #9aa2ae;
    border-color: var(--border);
    cursor: not-allowed;
}

.btn-block { width: 100%; }

/* On navy, a secondary button borrows the surface rather than the page. */
.on-navy .btn-secondary {
    background: var(--on-navy-fill);
    color: var(--on-navy);
    border-color: var(--on-navy-line);
}
.on-navy .btn-secondary:hover { background: rgba(255, 255, 255, .13); color: var(--on-navy); }

/* ---------- status ----------
 *
 * **Status never relies on colour alone.** Every badge carries an icon and a word, because a
 * colour-blind instructor reading a roster of forty drivers should not have to guess, and because
 * a red dot means nothing to somebody whose English is the thing being trained. */

.badge {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 3px var(--s2);
    border-radius: var(--r-badge);
    font-size: 13px;
    font-weight: 600;
    white-space: nowrap;
    border: 0;
}

.badge i { font-style: normal; }

.badge-ready { background: var(--ready-bg); color: var(--ready-fg); }
.badge-practicing { background: var(--practicing-bg); color: var(--practicing-fg); }
.badge-attention { background: var(--attention-bg); color: var(--attention); }
.badge-pending { background: var(--pending-bg); color: var(--pending-fg); }
.badge-improving { background: var(--ready-bg); color: var(--ready-fg); }

/* **Not assessed is not a score.** Dashed, uncoloured, and never a zero — a driver who has not
   practised has not failed, and an instructor reading 0% would coach the wrong thing. */
.badge-none {
    background: var(--white);
    color: var(--slate-600);
    border: 1px dashed #B8BFC9;
}

/* ---------- score bars ----------
 *
 * Green at or above 80, amber 60–79, red below, and always with the number beside them. The
 * number is not decoration: a bar alone cannot be read aloud down a phone to a driver. */

.score { display: flex; align-items: center; gap: var(--s3); }
.score-track { flex: 1; height: 8px; border-radius: 999px; background: var(--border); overflow: hidden; }
.score-fill { display: block; height: 100%; border-radius: 999px; background: var(--ready); }
.score-fill.is-practicing { background: var(--practicing); }
.score-fill.is-attention { background: var(--attention); }
.score-value { font-weight: 700; font-variant-numeric: tabular-nums; min-width: 3.2em; text-align: right; }

/* ---------- surfaces ---------- */

.card {
    background: var(--white);
    border: 1px solid var(--hairline);
    border-radius: var(--r-card);
    padding: var(--s6);
    box-shadow: var(--lift);
}

.card-raised { box-shadow: var(--lift-raised); }

/* On a tinted band the card needs no edge at all — the fill already separates it. */
.band .card { border-color: transparent; }

.band { background: var(--surface); }

.band-navy {
    background: var(--ink);
    color: var(--on-navy);
    position: relative;
    overflow: hidden;
}

.band-navy h2, .band-navy h3 { color: var(--on-navy); }
.band-navy .lede { color: var(--on-navy-dim); }
.band-navy .eyebrow { color: #FF8A9B; }

/* The grid and the glow the design puts behind its navy sections. Decoration, so it never takes
   a pointer and never sits above content. */
.band-navy .grid-overlay {
    position: absolute;
    inset: 0;
    background-image:
        linear-gradient(rgba(255, 255, 255, .035) 1px, transparent 1px),
        linear-gradient(90deg, rgba(255, 255, 255, .035) 1px, transparent 1px);
    background-size: 64px 64px;
    pointer-events: none;
}

.band-navy .glow {
    position: absolute;
    left: 50%;
    top: -240px;
    width: 900px;
    height: 480px;
    transform: translateX(-50%);
    background: radial-gradient(closest-side, rgba(200, 16, 46, .22), rgba(200, 16, 46, 0));
    pointer-events: none;
}

.band-navy .wrap { position: relative; }

section { padding: clamp(80px, 10vw, 128px) 0; }
section.tight { padding: clamp(56px, 7vw, 88px) 0; }

.section-head { max-width: 760px; margin: 0 auto var(--s7); text-align: center; }
.section-head.left { margin-left: 0; text-align: left; }
.section-head h2 { margin-bottom: var(--s4); }

.grid { display: grid; gap: var(--s5); }
.grid-airy { gap: var(--s6); }
.grid-2 { grid-template-columns: repeat(2, 1fr); }
.grid-3 { grid-template-columns: repeat(3, 1fr); }
.grid-4 { grid-template-columns: repeat(4, 1fr); }

.rule { height: 1px; background: var(--border); border: 0; margin: 0; }

/* ---------- forms ---------- */

.field { margin-bottom: var(--s5); }
.field label { display: block; font-size: 15px; font-weight: 600; color: var(--ink); margin-bottom: 6px; }

.field input, .field select {
    width: 100%;
    min-height: 44px;
    padding: 0 var(--s3);
    font: 400 16px/1 var(--sans);
    color: var(--ink);
    background: var(--white);
    border: 1px solid #DCDEE3;
    border-radius: var(--r-control);
}

.field input:focus, .field select:focus { border-color: var(--focus); }
.field .hint { font-size: 14px; color: var(--slate-500); margin-top: 6px; }

.field.has-error input { border-color: var(--attention); }
.field .error-text { font-size: 14px; color: var(--attention); margin-top: 6px; }

.notice {
    padding: var(--s3) var(--s4);
    border-radius: var(--r-control);
    background: var(--pending-bg);
    color: var(--pending-fg);
    font-size: 15px;
    margin-bottom: var(--s5);
}

/* Test mode, where an order grants a licence and nobody is charged (MS-176). A striped edge rather
   than another colour of box: the billing page already uses the pending amber for ordinary notices,
   and this one has to be impossible to skim past. */
.notice-test {
    border-left: 4px solid var(--attention-fg, #b45309);
    background:
        repeating-linear-gradient(
            135deg,
            var(--pending-bg) 0 10px,
            color-mix(in srgb, var(--pending-bg) 88%, #000) 10px 20px);
}

.error {
    padding: var(--s3) var(--s4);
    border-radius: var(--r-control);
    background: var(--attention-bg);
    color: var(--attention);
    font-size: 15px;
    margin-bottom: var(--s5);
}

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

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

thead th {
    text-align: left;
    padding: 0 var(--s4) var(--s3);
    font-size: 13px;
    font-weight: 600;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--slate-500);
    border-bottom: 1px solid var(--border);
}

tbody td { padding: var(--s4); border-bottom: 1px solid var(--border); vertical-align: middle; }
tbody tr:hover { background: var(--surface); }
td a { color: var(--ink); font-weight: 600; }
td a:hover { color: var(--red); text-decoration: underline; }

/* Tables scroll inside their panel rather than reflowing: a roster has columns that only mean
   something beside each other, and stacking them into cards loses the comparison an instructor is
   making.

   At every width, not only on a phone. Scoped to the phone breakpoint, a laptop window at 800px —
   or a portal opened in a split pane — got a horizontally scrolling *page* instead, which slides
   the navigation off screen and breaks every panel on it, not just the wide one. The container
   clips; the table keeps its real width and scrolls within it. */
.table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; max-width: 100%; }
.table-scroll table { min-width: 620px; }

.empty { padding: var(--s8) var(--s5); text-align: center; color: var(--slate-500); }
.empty h3 { margin-bottom: var(--s2); }
.empty p { margin-bottom: var(--s5); }

.footnote { font-size: 14px; line-height: 1.6; color: var(--slate-500); max-width: 72ch; }

/* ---------- site header ---------- */

.site-head {
    position: sticky;
    top: 0;
    z-index: 40;
    background: rgba(255, 255, 255, .92);
    backdrop-filter: saturate(140%) blur(12px);
    border-bottom: 1px solid var(--border);
}

.site-head .wrap { display: flex; align-items: center; gap: var(--s6); min-height: 72px; }

.brand { display: inline-flex; align-items: center; gap: var(--s3); color: var(--ink); }
.brand img { width: 36px; height: 36px; border-radius: var(--r-control); display: block; }
.brand b { font: 700 20px/1 var(--sans); letter-spacing: -.01em; }
.brand span { display: block; font-size: 12px; color: var(--slate-500); margin-top: 2px; }

.site-nav { display: flex; align-items: center; gap: var(--s5); margin-left: auto; }
/* Links only. A button in the nav is still a button.
 *
 * `.site-nav a` is one class plus one element, so it outranked `.btn-primary`, which is one class —
 * and the header's red "Get MockStop" rendered with slate text on red. Contrast about 2.4:1, on the
 * single most important button on the site. Excluding buttons here fixes it at the cause rather
 * than by piling another override on top. */
.site-nav a:not(.btn) { color: var(--slate-600); font-weight: 500; font-size: 15px; }
.site-nav a:not(.btn):hover { color: var(--ink); }
.site-nav .btn { margin-left: var(--s2); }

.menu-toggle { display: none; }

/* ---------- site footer ---------- */

.site-foot { background: var(--ink); color: var(--on-navy-muted); padding: var(--s8) 0 var(--s7); }
.site-foot a { color: var(--on-navy-dim); }
.site-foot a:hover { color: var(--white); }
.site-foot .brand { color: var(--white); }
.site-foot .brand span { color: var(--on-navy-muted); }
.site-foot .cols { display: grid; grid-template-columns: 2fr 1fr 1fr 1fr; gap: var(--s6); }
.site-foot h4 { color: var(--white); font-size: 14px; margin-bottom: var(--s3); }
.site-foot ul { list-style: none; margin: 0; padding: 0; }
.site-foot li { margin-bottom: var(--s2); font-size: 15px; }
.site-foot .legal {
    margin-top: var(--s7);
    padding-top: var(--s5);
    border-top: 1px solid var(--on-navy-line);
    font-size: 13.5px;
    line-height: 1.6;
    color: var(--on-navy-muted);
}

/* ---------- portal ----------
 *
 * Navy chrome, because this is a tool somebody has open all day rather than a page they read
 * once. Persistent left navigation at 1024 and up; a slide-over below that, per the breakpoints
 * the design system names. */

.portal { display: grid; grid-template-columns: 260px 1fr; min-height: 100vh; }

/* A grid item's default min-width is auto — "as wide as my content" — so below 1024, where the
   navigation becomes a horizontally scrolling strip, the strip's full content width (970px on an
   800px screen) grew its own column and took the entire page with it. The scroll container clipped
   nothing and the whole layout, main column included, hung off the right edge.

   Neither child may be wider than the column it is in. This one line is what makes every
   overflow-x:auto below it actually clip. */
.portal > * { min-width: 0; }

.portal-nav {
    background: var(--ink);
    color: var(--on-navy);
    padding: var(--s5) var(--s3);
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.portal-nav .brand { color: var(--white); padding: 0 var(--s3) var(--s5); }

/* The collapsed bar and its checkbox exist only below 1024. See the breakpoint block. */
.nav-check, .nav-bar { display: none; }

.portal-nav .nav-links { display: flex; flex-direction: column; gap: 2px; flex: 1; }

.portal-nav a.nav {
    display: flex;
    align-items: center;
    gap: var(--s2);
    min-height: 44px;
    padding: 0 var(--s3);
    border-radius: var(--r-control);
    color: var(--on-navy-muted);
    font-weight: 500;
    font-size: 15px;
}

.portal-nav a.nav:hover { background: var(--on-navy-fill); color: var(--white); }

.portal-nav a.nav.on {
    background: rgba(255, 255, 255, .1);
    color: var(--white);
    font-weight: 600;
    box-shadow: inset 3px 0 0 var(--red);
}

.portal-nav .count {
    margin-left: auto;
    background: var(--red);
    color: var(--white);
    border-radius: 999px;
    font-size: 12px;
    font-weight: 700;
    padding: 1px 7px;
}

.portal-nav .who {
    margin-top: auto;
    padding: var(--s4) var(--s3) 0;
    border-top: 1px solid var(--on-navy-line);
    font-size: 13.5px;
    color: var(--on-navy-muted);
}

.portal-nav .who strong { display: block; color: var(--white); font-size: 15px; }
.portal-nav .who a { color: var(--on-navy-muted); }
.portal-nav .who a:hover { color: var(--white); }

.portal-main { background: var(--surface); padding: var(--s6) clamp(20px, 3vw, 40px) var(--s8); }
.portal-main > * { max-width: var(--portal); margin-left: auto; margin-right: auto; }

.page-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--s5); margin-bottom: var(--s6); }
.page-head h1 { font-size: 28px; font-weight: 700; margin-bottom: 4px; }
.page-head .meta { font-size: 15px; }

.panel {
    background: var(--white);
    border: 1px solid var(--hairline);
    border-radius: var(--r-card);
    padding: var(--s5);
    margin-bottom: var(--s5);
    box-shadow: var(--lift);
}

.panel-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s4); margin-bottom: var(--s4); }
.panel-head h2 { font-size: 18px; font-weight: 700; }

.stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--s4); margin-bottom: var(--s5); }

.stat {
    position: relative;
    overflow: hidden;
    background: var(--white);
    border: 1px solid var(--hairline);
    border-radius: var(--r-card);
    padding: var(--s5);
    box-shadow: var(--lift);
}

/* A tone rule along the top edge, so the four numbers read as a status at arm's length rather than
   as four identical white boxes. Colour is never the only signal: the word is in the label and the
   number is in the figure — this is the third telling, for somebody glancing. */
.stat::before {
    content: "";
    position: absolute;
    top: 0; left: 0; right: 0;
    height: 3px;
    background: var(--slate-500);
}

.stat.is-ready::before { background: var(--ready); }
.stat.is-practicing::before { background: var(--practicing); }
.stat.is-attention::before { background: var(--attention); }

.stat.is-ready .stat-value { color: var(--ready-fg); }
.stat.is-attention .stat-value { color: var(--attention); }

/* The whole card is the target when there is somewhere to go. A number with "See why" under it is
   a link the size of two words; this makes it the size of the card. */
a.stat { display: block; }
a.stat:hover { box-shadow: var(--lift-raised); }
a.stat:hover .stat-label { color: var(--ink); }

.stat-label { display: block; font-size: 13px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--slate-500); margin-bottom: var(--s2); }
.stat-value { display: block; font: 700 30px/1 var(--sans); color: var(--ink); font-variant-numeric: tabular-nums; }
.stat-foot { display: block; font-size: 13.5px; color: var(--slate-500); margin-top: 6px; }

/* ---------- breakpoints ----------
 *
 * The four the design system names, and what each one is for. */

/* Portal sidebar becomes a slide-over; landing nav still fits. */
@media (max-width: 1023px) {
    .portal { grid-template-columns: 1fr; }

    /* Sticky, because the one thing a phone always needs within reach is the way out of the page
       it is on. 44px of tap target on the bar, per the same rule the nav links follow. */
    .portal-nav {
        position: sticky;
        top: 0;
        z-index: 40;
        padding: 0;
        gap: 0;
    }

    /* The always-on sidebar brand; the collapsed bar carries its own. */
    .portal-nav .nav-brand { display: none; }

    .nav-bar {
        display: flex;
        align-items: center;
        gap: var(--s4);
        min-height: 64px;
        padding: var(--s3) var(--s4);
        cursor: pointer;
        -webkit-tap-highlight-color: transparent;
    }

    .nav-bar .brand { padding: 0; color: var(--white); }
    .nav-bar .brand img { width: 32px; height: 32px; }
    .nav-bar .brand b { font-size: 17px; }

    .nav-here {
        display: flex;
        align-items: center;
        gap: var(--s2);
        margin-left: auto;
        padding: 6px 10px 6px 12px;
        border: 1px solid var(--on-navy-line);
        border-radius: 999px;
        font-size: 14px;
        font-weight: 600;
        color: var(--white);
    }

    /* The current page's name is the first thing to go when the screen is narrow: the chevron and
       the attention count are what make the bar a control, and the heading below repeats the name
       anyway. */
    .nav-here-page { max-width: 42vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

    .nav-chevron {
        width: 8px; height: 8px;
        margin-left: 2px;
        border-right: 2px solid currentColor;
        border-bottom: 2px solid currentColor;
        transform: translateY(-2px) rotate(45deg);
        transition: transform .15s ease;
    }

    /* The checkbox itself is off screen but still focusable, so the menu opens with Space. Not
       [hidden] and not display:none, both of which take it out of the tab order. */
    .nav-check {
        position: absolute;
        width: 1px; height: 1px;
        margin: 0;
        opacity: 0;
        pointer-events: none;
    }

    .nav-check:focus-visible + .nav-bar { outline: 2px solid var(--focus); outline-offset: -2px; }

    .portal-nav .nav-links {
        display: none;
        padding: 0 var(--s3) var(--s4);
        border-top: 1px solid var(--on-navy-line);
    }

    .nav-check:checked ~ .nav-links { display: flex; }
    .nav-check:checked + .nav-bar .nav-chevron { transform: translateY(2px) rotate(225deg); }

    .portal-nav .nav-links a.nav { min-height: 48px; margin-top: 2px; }
    .portal-nav .who { margin-top: var(--s4); }

    .stats { grid-template-columns: repeat(2, 1fr); }
    .grid-4 { grid-template-columns: repeat(2, 1fr); }
}

/* Two-column grids reflow; the hero stacks text over photo. */
@media (max-width: 939px) {
    .grid-3, .grid-2 { grid-template-columns: 1fr; }
    .site-foot .cols { grid-template-columns: 1fr 1fr; }
}

/* Single column. Landing nav collapses. Tables scroll horizontally. */
@media (max-width: 639px) {
    .site-nav { display: none; }
    .site-nav.is-open {
        display: flex;
        position: absolute;
        top: 72px; left: 0; right: 0;
        flex-direction: column;
        align-items: stretch;
        gap: var(--s2);
        padding: var(--s4);
        background: var(--white);
        border-bottom: 1px solid var(--border);
    }
    .site-nav.is-open .btn { margin-left: 0; }

    .menu-toggle {
        display: inline-flex;
        margin-left: auto;
        min-height: 44px;
        min-width: 44px;
        align-items: center;
        justify-content: center;
        background: none;
        border: 1px solid var(--border);
        border-radius: var(--r-control);
        cursor: pointer;
    }

    /* **Stat cards stay two-up on a phone.** One per row turned four numbers a school checks at a
       glance into four screenfuls of scrolling, with a 30px figure marooned in a card the width of
       the display. Two-up they read as a set, which is what they are. */
    .stats { grid-template-columns: repeat(2, 1fr); gap: var(--s3); }
    .stat { padding: var(--s4); }
    .stat-value { font-size: 26px; }

    .grid-4 { grid-template-columns: 1fr; }

    /* **A heading and its side note stop sharing a row.** These are `space-between` flex rows, which
       on a phone gives each side about 150px: "Readiness by scenario" and "Training detail" both
       broke over three lines and sat beside each other like two columns of a table nobody asked
       for. Stacked, the heading gets the full width and the note sits under it. */
    .section-head, .panel-head {
        flex-direction: column;
        align-items: flex-start;
        gap: 6px;
    }

    .page-head { flex-direction: column; }
    .page-head .btn { width: 100%; }

    /* **A big button stops being wider than the phone.** `.btn` is `white-space: nowrap`, which is
       right on a desktop and is how "Complete test order — $2,065, not charged" ended up 382px wide
       inside a 375px page: the whole billing page scrolled sideways, and a horizontal scrollbar on a
       phone reads as a broken layout rather than as a long button. Full width and wrapping, which is
       what a primary action wants on a phone anyway. */
    .btn-lg {
        width: 100%;
        white-space: normal;
        line-height: 1.3;
        padding: var(--s3) var(--s4);
        text-align: center;
    }

    .inline-form { display: block; width: 100%; }

    .site-foot .cols { grid-template-columns: 1fr; }

}

/* ---------- portal components ----------
 *
 * The pieces the instructor-facing pages are built from. Same tokens as the site; different
 * surface, because this is a tool rather than a document. */

.panel-card {
    background: var(--white);
    border: 1px solid var(--hairline);
    border-radius: var(--r-card);
    padding: var(--s5);
    margin-bottom: var(--s5);
    box-shadow: var(--lift);
}

.panel-card .section-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--s4);
    max-width: none;
    margin: 0 0 var(--s4);
    text-align: left;
}

.panel-card .section-head h2 { font-size: 18px; font-weight: 700; font-family: var(--sans); }
.panel-card table { margin: 0 calc(var(--s4) * -1); width: calc(100% + var(--s4) * 2); }

/* Grid and flex children default to min-width:auto, which is "as wide as my content" — so a panel
   holding a wide table refuses to shrink and pushes the page out sideways however carefully the
   table itself is clipped. These are what let the clip actually happen. */
.panel-card, .panel, .split > * { min-width: 0; }

.lede-sm { color: var(--slate-600); margin-bottom: var(--s4); max-width: 62ch; }
.muted { color: var(--slate-500); }

/* A driver's headline score, beside their name. */
.head-score { text-align: right; flex-shrink: 0; }
.big-score { display: block; font: 700 44px/1 var(--display); color: var(--ready); }
.big-score.is-practicing { color: var(--practicing); }
.big-score.is-attention { color: var(--attention); }
.big-score.is-none { color: var(--slate-500); }
.big-score-label { display: block; font-size: 13px; letter-spacing: .06em; text-transform: uppercase; color: var(--slate-500); margin-top: 6px; }

.card-score { font: 700 30px/1 var(--display); color: var(--ready); margin: var(--s2) 0; }
.card-score.is-practicing { color: var(--practicing); }
.card-score.is-attention { color: var(--attention); }
.card-score.is-none { font-size: 17px; font-family: var(--sans); color: var(--slate-500); }

/* The cohort bars on the overview. "Not assessed" is grey rather than red on purpose: not having
   started is not the same as doing badly. */
.band-row { display: flex; align-items: center; gap: var(--s4); margin-bottom: var(--s3); }
.band-name { width: 120px; flex-shrink: 0; color: var(--slate-600); font-size: 15px; }
.band-bar { flex: 1; height: 10px; border-radius: 999px; background: var(--border); overflow: hidden; }
.band-fill { display: block; height: 100%; border-radius: 999px; background: var(--slate-500); }
.band-fill.good { background: var(--ready); }
.band-fill.warn { background: var(--practicing); }
.band-fill.bad { background: var(--attention); }
.band-fill.none { background: #C9CFD8; }
.band-count { width: 32px; text-align: right; font-weight: 700; font-variant-numeric: tabular-nums; }

/* Why a driver is flagged. Headline then evidence, always both — a flag with no evidence beside
   it is exactly what this product must not produce. */
.reasons { list-style: none; margin: 0; padding: 0; }

.reasons li {
    display: grid;
    grid-template-columns: 220px 1fr;
    gap: var(--s4);
    align-items: baseline;
    padding: var(--s3) 0;
    border-bottom: 1px solid var(--border);
}

.reasons li:last-child { border-bottom: 0; padding-bottom: 0; }
.reasons li strong { color: var(--ink); }
.reasons li span { color: var(--slate-600); }

.attention-card { border-left: 3px solid var(--practicing); }

/* A form on one line: a field and the button that submits it. */
.row-form { display: flex; gap: var(--s3); align-items: flex-end; flex-wrap: wrap; }
.row-form .field { margin-bottom: 0; }
.row-form .field-grow { flex: 1; min-width: 220px; }

.inline-form { display: inline-block; margin: 0; }
.row-pending td { color: var(--slate-500); }
.row-current { background: var(--surface); }

.seat-picks { display: flex; gap: var(--s2); flex-wrap: wrap; margin-bottom: var(--s4); }

/* A quote. Deliberately plain: a buyer checks the arithmetic in their head, and anything
   decorative gets in the way of that. */
.quote {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-control);
    padding: var(--s4);
    margin-bottom: var(--s5);
}

.quote-row { display: flex; justify-content: space-between; gap: var(--s4); align-items: baseline; margin-bottom: var(--s2); }
.quote-row.muted { font-size: 15px; }
.quote-row strong { font-variant-numeric: tabular-nums; white-space: nowrap; color: var(--ink); }
.quote-note { font-size: 13.5px; color: var(--slate-500); border-top: 1px solid var(--border); padding-top: var(--s3); margin-top: var(--s3); line-height: 1.5; }

/* The two sign-in pages: one column, centred, nothing else on screen. */
.auth { max-width: 420px; margin: 9vh auto; padding: 0 var(--s5); }
.auth-wide { max-width: 480px; }
.auth .brand { display: flex; justify-content: center; margin-bottom: var(--s6); }
.auth h1 { font-size: 28px; text-align: center; margin-bottom: var(--s2); }
.auth .sub { text-align: center; color: var(--slate-600); margin-bottom: var(--s6); }
.auth-foot { text-align: center; margin-top: var(--s5); font-size: 15px; color: var(--slate-500); }

/* Ticked claims, on the marketing pages. */
.checks { list-style: none; margin: 0; padding: 0; }
.checks li { position: relative; padding-left: 28px; margin-bottom: var(--s3); color: var(--slate-700); }
.checks li::before {
    content: "";
    position: absolute;
    left: 0; top: 9px;
    width: 8px; height: 8px;
    border-radius: 2px;
    background: var(--ready);
}

@media (max-width: 639px) {
    .reasons li { grid-template-columns: 1fr; gap: 4px; }
    .band-name { width: 96px; font-size: 14px; }
    .row-form .btn { width: 100%; }
    .head-score { text-align: left; }
}

/* ---------- roster toolbar ----------
 *
 * Search, then the filters, then the sort. A GET form with no JavaScript: the resulting URL is the
 * view, so an instructor can bookmark "my students who are not ready at the scale house" and send
 * it to a colleague. Filtering in the browser would have been less code and would have made that
 * impossible. */

.toolbar { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--s3); margin-bottom: var(--s4); }
.toolbar .field { margin-bottom: 0; }
.toolbar .search { flex: 1 1 240px; min-width: 0; }
.toolbar .search input { width: 100%; }

.chips { display: flex; flex-wrap: wrap; gap: var(--s2); margin-bottom: var(--s4); }

.chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    min-height: 34px;
    padding: 0 var(--s3);
    border-radius: 999px;
    border: 1px solid var(--border);
    background: var(--white);
    color: var(--slate-600);
    font-size: 14px;
    font-weight: 600;
    white-space: nowrap;
}

.chip:hover { border-color: #cfd4dc; color: var(--ink); }
.chip.on { background: var(--ink); border-color: var(--ink); color: var(--white); }
.chip .n { font-variant-numeric: tabular-nums; opacity: .7; }

/* Sortable column headers. The arrow is a character rather than an icon so it is read out. */
thead th a { color: var(--slate-500); display: inline-flex; gap: 4px; align-items: center; }
thead th a:hover { color: var(--ink); }
thead th a .dir { font-size: 11px; }

/* ---------- distribution bar ----------
 *
 * One bar, three segments, each a link into the filtered roster. The count is written in the
 * segment and repeated in the legend, because a segment two pixels wide can hold no text and a
 * cohort where one driver is stuck is exactly the case that matters. */

.dist { display: flex; height: 28px; border-radius: var(--r-control); overflow: hidden; background: var(--border); margin-bottom: var(--s3); }

.dist a, .dist span {
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 12.5px;
    font-weight: 700;
    color: var(--white);
    font-variant-numeric: tabular-nums;
    min-width: 0;
    overflow: hidden;
}

.dist .seg-ready { background: var(--ready); }
.dist .seg-practicing { background: var(--practicing); }
.dist .seg-none { background: #C9CFD8; color: var(--slate-700); }
.dist a:hover { filter: brightness(1.08); color: var(--white); }
.dist .seg-none:hover { color: var(--ink); }

.legend { display: flex; flex-wrap: wrap; gap: var(--s4); font-size: 14px; color: var(--slate-600); }
.legend span { display: inline-flex; align-items: center; gap: 6px; }
.legend i { width: 10px; height: 10px; border-radius: 3px; display: inline-block; }
.legend .k-ready { background: var(--ready); }
.legend .k-practicing { background: var(--practicing); }
.legend .k-none { background: #C9CFD8; }

/* ---------- trend ----------
 *
 * Readiness over the last weeks, as columns. A week with no practice is a gap with a label rather
 * than a zero-height column at the bottom of the chart — a cohort on spring break did not get
 * worse. */

.trend { display: flex; align-items: flex-end; gap: var(--s2); height: 160px; margin-bottom: var(--s3); }
.trend .col { flex: 1; display: flex; flex-direction: column; justify-content: flex-end; align-items: center; height: 100%; min-width: 0; }
.trend .bar { width: 100%; max-width: 56px; border-radius: var(--r-control) var(--r-control) 0 0; background: var(--ready); position: relative; }
.trend .bar.is-practicing { background: var(--practicing); }
.trend .bar.is-attention { background: var(--attention); }
.trend .bar.is-gap { background: repeating-linear-gradient(45deg, #EDEFF2, #EDEFF2 4px, #E3E6EB 4px, #E3E6EB 8px); }
.trend .v { position: absolute; top: -22px; left: 0; right: 0; text-align: center; font-size: 13px; font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; }
.trend .x { font-size: 12.5px; color: var(--slate-500); margin-top: 6px; white-space: nowrap; }
.trend-axis { display: flex; justify-content: space-between; font-size: 13px; color: var(--slate-500); border-top: 1px solid var(--hairline); padding-top: var(--s2); }

/* ---------- horizontal bars ----------
 *
 * Scenario completion, weak skills, improvement after targeted practice. Label, bar, number —
 * and the number is always there, because a bar alone is a shape and this page is evidence. */

.hbars { display: flex; flex-direction: column; gap: var(--s3); }
.hbar { display: grid; grid-template-columns: minmax(120px, 190px) 1fr auto; gap: var(--s4); align-items: center; }
.hbar .l { font-size: 15px; color: var(--slate-700); }
.hbar .t { height: 12px; border-radius: 999px; background: var(--border); overflow: hidden; }
.hbar .f { display: block; height: 100%; border-radius: 999px; background: var(--ready); }
.hbar .f.is-practicing { background: var(--practicing); }
.hbar .f.is-attention { background: var(--attention); }
.hbar .f.is-neutral { background: var(--ink-2); }
.hbar .n { font-weight: 700; font-variant-numeric: tabular-nums; min-width: 4.5em; text-align: right; font-size: 15px; }
.hbar .n small { font-weight: 500; color: var(--slate-500); }

/* A before/after pair, so "improvement" is two numbers and an arrow rather than a delta nobody
   can check. */
.pair { display: flex; align-items: baseline; gap: 6px; font-variant-numeric: tabular-nums; }
.pair .was { color: var(--slate-500); }
.pair .arrow { color: var(--slate-500); }
.pair .now { font-weight: 700; color: var(--ink); }

/* ---------- activity ----------
 *
 * What happened, when, to whom. Never what was said. */

.feed { list-style: none; margin: 0; padding: 0; }

.feed li {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: var(--s4);
    align-items: baseline;
    padding: var(--s3) 0;
    border-bottom: 1px solid var(--hairline);
}

.feed li:last-child { border-bottom: 0; padding-bottom: 0; }
.feed .what { color: var(--slate-700); font-size: 15px; }
.feed .what a { font-weight: 600; }
.feed .when { color: var(--slate-500); font-size: 13.5px; white-space: nowrap; }

/* ---------- definition rows ----------
 *
 * Organization details, and anything else that is a list of labelled facts. */

.facts { display: grid; gap: 0; }

.facts > div {
    display: grid;
    grid-template-columns: 200px 1fr;
    gap: var(--s4);
    align-items: baseline;
    padding: var(--s3) 0;
    border-bottom: 1px solid var(--hairline);
}

.facts > div:last-child { border-bottom: 0; }
.facts dt { color: var(--slate-500); font-size: 14px; }
.facts dd { margin: 0; color: var(--ink); font-size: 15px; }

/* ---------- toggles ----------
 *
 * A real checkbox with a drawn track, so it keeps the keyboard and the screen reader it was born
 * with. The label describes what being on means, not what the setting is called. */

.toggles { display: flex; flex-direction: column; gap: 0; }

.toggle {
    display: grid;
    grid-template-columns: 1fr 52px;
    gap: var(--s4);
    align-items: center;
    padding: var(--s4) 0;
    border-bottom: 1px solid var(--hairline);
}

.toggle:last-child { border-bottom: 0; }
.toggle strong { display: block; color: var(--ink); font-size: 15px; margin-bottom: 3px; }
.toggle .hint { font-size: 14px; color: var(--slate-500); line-height: 1.5; }

.toggle input {
    appearance: none;
    -webkit-appearance: none;
    width: 46px; height: 28px;
    border-radius: 999px;
    background: #CDD3DB;
    position: relative;
    cursor: pointer;
    margin: 0;
    justify-self: end;
    transition: background .15s ease;
}

.toggle input::after {
    content: "";
    position: absolute;
    top: 3px; left: 3px;
    width: 22px; height: 22px;
    border-radius: 999px;
    background: var(--white);
    box-shadow: 0 1px 3px rgba(11, 20, 36, .3);
    transition: transform .15s ease;
}

.toggle input:checked { background: var(--ready); }
.toggle input:checked::after { transform: translateX(18px); }
.toggle input:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.toggle input[disabled] { background: #E3E6EB; cursor: not-allowed; }
.toggle input[disabled]::after { box-shadow: none; }

/* A setting that is deliberately not a setting. Shown, locked, and explained. */
.locked { background: var(--surface); border-radius: var(--r-control); padding: var(--s4); margin-top: var(--s4); }
.locked strong { color: var(--ink); display: block; margin-bottom: var(--s2); }
.locked p { font-size: 14px; color: var(--slate-600); line-height: 1.55; margin: 0; }

/* ---------- number field, for thresholds ---------- */
.field-narrow { max-width: 160px; }
.fields-row { display: flex; flex-wrap: wrap; gap: var(--s4); }

/* ---------- team ---------- */
.role { font-size: 13px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: var(--slate-500); }

/* ---------- two-column page split ----------
 *
 * Main column and a rail. The rail carries the things you read once — how seats work, what we will
 * not do — and it goes underneath on a phone rather than above the roster. */
.split { display: grid; grid-template-columns: minmax(0, 1.9fr) minmax(0, 1fr); gap: var(--s5); align-items: start; }

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

@media (max-width: 639px) {
    /* Label and number on one line, bar underneath across the full width.
     *
     * Every cell is placed explicitly. Leaving the label and the number to auto-placement around a
     * track that spans 1/-1 collapsed the auto column to zero on a phone, so the bar — the only
     * thing on the panel that is a picture — rendered at no width at all. */
    .hbar { grid-template-columns: 1fr auto; row-gap: 6px; column-gap: var(--s3); }
    .hbar .l { grid-row: 1; grid-column: 1; }
    .hbar .n { grid-row: 1; grid-column: 2; min-width: 0; }
    .hbar .t { grid-row: 2; grid-column: 1 / -1; }

    .facts > div { grid-template-columns: 1fr; gap: 2px; }
    .trend { height: 130px; }
    .trend .x { font-size: 11px; }
    .toggle { grid-template-columns: 1fr auto; }
}

/* ---------- the hero's audience tabs ----------
 *
 * **Driven by aria-selected, not by a class the markup hardcodes.** These were styled with inline
 * `style` attributes: the first tab carried the red underline and the dark text literally, so
 * clicking another tab swapped the panel underneath while the first one stayed lit. The selected
 * state and the thing screen readers are told were two separate facts, and only one of them moved.
 *
 * Now there is one source. The script toggles aria-selected; everything visual follows from it. */

.hero-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 28px;
    border-bottom: 1px solid var(--border);
    margin-bottom: 36px;
}

.hero-tab {
    height: 48px;
    padding: 0;
    margin-bottom: -1px;
    border: 0;
    border-bottom: 2px solid transparent;
    background: transparent;
    font: 600 16px var(--sans);
    color: var(--slate-500);
    cursor: pointer;
}

.hero-tab:hover { color: var(--ink); }

.hero-tab[aria-selected="true"] {
    border-bottom-color: var(--red);
    color: var(--ink);
}

/* Keyboard users move through these with arrow keys, so the focus ring has to be visible against
   the hairline underneath it. */
.hero-tab:focus-visible {
    outline: 2px solid var(--focus);
    outline-offset: 4px;
    border-radius: 2px;
}

/* **On a phone the tabs become pills.** Underlined tabs wrapped onto two rows at 375px, which left
   the shared bottom rule under the second row only and the third tab reading as a stray link that
   had fallen out of the navigation above it. Pills wrap honestly: each carries its own edge, so a
   second row reads as more of the same control.

   Here rather than in the breakpoint block at the top of the file: a media query adds no
   specificity, so those rules sat above the .hero-tab defaults and lost to every one of them —
   including the selected tab's colour, which left white-on-navy text rendering navy on navy. */
@media (max-width: 639px) {
    .hero-tabs {
        gap: 8px;
        border-bottom: 0;
        margin-bottom: 28px;
    }

    .hero-tab {
        height: 40px;
        padding: 0 14px;
        margin-bottom: 0;
        border: 1px solid var(--border);
        border-radius: 999px;
        background: var(--white);
        font-size: 15px;
    }

    .hero-tab[aria-selected="true"] {
        background: var(--ink);
        border-color: var(--ink);
        color: var(--white);
    }
}

/* ---------- photography ----------
 *
 * Four supplied photographs, used where a picture says something the copy would take a paragraph
 * to: what a roadside check actually looks like, and where somebody practises.
 *
 * **Every one carries its own aspect-ratio and object-fit.** A photograph in a responsive layout
 * without them reflows the page as it loads and shifts everything under the reader's thumb; with
 * them the space is reserved before a byte of image arrives. */

.photo-band {
    position: relative;
    isolation: isolate;
    overflow: hidden;
    background: var(--ink);
}

.photo-band img {
    display: block;
    width: 100%;
    height: 100%;
    position: absolute;
    inset: 0;
    object-fit: cover;
    z-index: -2;
}

/* The scrim. Text over an uncontrolled photograph is unreadable somewhere in the frame, and a
   photograph we cannot re-shoot is one the layout has to cope with rather than depend on. */
.photo-band::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    background: linear-gradient(
        100deg,
        rgba(11, 20, 36, .92) 0%,
        rgba(11, 20, 36, .78) 42%,
        rgba(11, 20, 36, .38) 100%);
}

.photo-band .wrap {
    position: relative;
    padding-top: clamp(64px, 9vw, 120px);
    padding-bottom: clamp(64px, 9vw, 120px);
}

.photo-band h2, .photo-band h3 { color: var(--on-navy); }
.photo-band p { color: var(--on-navy-dim); }
.photo-band .eyebrow { color: #FF8A9B; }

/* An inline photograph inside a normal section. */
.photo {
    display: block;
    width: 100%;
    height: auto;
    aspect-ratio: 16 / 9;
    object-fit: cover;
    border-radius: var(--r-card);
    box-shadow: var(--lift-raised);
}

.photo-caption { font-size: 14px; color: var(--slate-500); margin-top: var(--s3); }

@media (max-width: 639px) {
    /* The scrim goes top-to-bottom on a phone, because the text sits under the subject rather
       than beside it. */
    .photo-band::after {
        background: linear-gradient(
            180deg,
            rgba(11, 20, 36, .72) 0%,
            rgba(11, 20, 36, .93) 55%);
    }
}
