/* RoadCoach Admin -- canonical stylesheet (Docs/Design/UI-STANDARDS.md).
   Tokens live in :root; every component reads them via var(--token), never a hardcoded brand hex,
   so changing a token here (or flipping [data-accent] on <body>) reskins the whole site.

   TWO-TIER TOKENS (THEME-DESIGNER-PLAN.md Phase A1): every component FAMILY below reads its OWN
   token (e.g. --btn-bg), and with no override set anywhere every component-family token resolves to
   the exact value it read directly before this refactor -- the render is pixel-identical. The
   Phase C editor generates its per-object controls FROM the TOKEN MAP below, so keep the map's
   format machine readable: exactly one token per line, `--token-name | color|size | what it
   controls`.

   TWO KINDS OF TOKEN, TWO DECLARATION SHAPES (read this before adding a token -- a real CSS
   browser-behavior gotcha, verified against a live Chromium render while building this refactor):
   1. LITERAL-DEFAULT tokens (a fixed px size or hex with no other token behind it, e.g.
      --input-font-size, --stat-value-size) ARE declared in :root below with their literal value,
      and components read them with a plain var(--token).
   2. DERIVED tokens (their "default" IS another token, e.g. --btn-bg defaults to --accent) are
      DELIBERATELY NOT declared in :root. A CSS custom property that is only ever declared once
      (e.g. `--btn-bg: var(--accent);` at :root) gets that var() substituted ONCE, using :root's
      OWN --accent, and children then inherit the already-frozen result -- a LATER override of
      --accent on a descendant (a [data-accent] block on <body>, or a future per-school/per-theme
      override) would NOT reach it, because inheritance propagates the resolved value, not the
      formula. The fix is the standard var()-fallback idiom: the CONSUMING rule reads
      `var(--btn-bg, var(--accent))` directly -- if --btn-bg was never set anywhere, the fallback
      var(--accent) is evaluated fresh, at THIS element, walking its own live ancestor chain, so it
      correctly tracks whatever the nearest [data-accent] (or future saved-theme) override is. If a
      later Phase C control DOES set --btn-bg (at any scope), that explicit value wins over the
      fallback, exactly as a per-object override should. Do not "fix" this by pre-declaring a
      derived token in :root -- that reintroduces the freeze bug.

   TOKEN MAP -- do not hand-edit tokens without updating this map; SiteCssCatalogTests proves every
   mapped token is actually wired into the file (a :root declaration for literal tokens, or a
   var(--token, ...) fallback consumption for derived tokens) so the map and the CSS never drift.

   -- Brand semantic layer (SUP-007, SUPPORTABILITY-PLAN.md sec 6 decision D4) --
   -- ~6-10 master tokens ABOVE the family tokens below. A subset of the family tokens (the ones
   -- that make up "the school's look": buttons, info badges, active chips/sidebar links, card/
   -- input/chip surfaces and borders, card radius) fall back to ONE of these instead of falling
   -- back to a base global directly, so a full rebrand is a handful of edits instead of touching
   -- every base global (--bg/--surface/--surface-2/--text/--text-dim/--border/--accent/--accent-
   -- weak/--accent-contrast/--radius) independently. LITERAL-default like the base globals below
   -- (see "TWO KINDS OF TOKEN" above) -- NOT derived from --accent/--surface/etc, so they cannot
   -- freeze. Each [data-accent] block further down ALSO sets --brand-primary/-weak alongside
   -- --accent/-weak so per-school template switching keeps reskinning Brand-fallback components
   -- exactly as it does today; --brand-primary-contrast/--brand-surface/--brand-surface-2/
   -- --brand-text/--brand-border/--brand-radius do not vary by template, same as --accent-contrast/
   -- --surface/--surface-2/--text/--border/--radius do not today.
   --brand-primary | color | master brand accent -- btn-bg, badge-info-fg, chip-active-bg/border, sidebar-active-fg fall back to this
   --brand-primary-weak | color | tinted master brand background -- badge-info-bg, sidebar-active-bg fall back to this
   --brand-primary-contrast | color | text/icon color on brand-primary -- btn-fg, chip-active-fg fall back to this
   --brand-surface | color | master card/panel surface -- card-bg falls back to this
   --brand-surface-2 | color | master secondary surface -- input-bg, table-header-bg, table-row-hover-bg, chip-bg, badge-neutral-bg fall back to this
   --brand-text | color | master brand text tone -- chip-fg falls back to this
   --brand-border | color | master hairline border -- card-border, input-border, chip-border fall back to this
   --brand-radius | size | master corner radius -- card-radius falls back to this

   -- Base globals (Docs/Design/UI-STANDARDS.md) --
   --bg | color | page background
   --surface | color | card/panel background
   --surface-2 | color | secondary surface (hover states, input backgrounds, table headers)
   --text | color | primary text
   --text-dim | color | secondary/muted text
   --border | color | hairline borders
   --accent | color | brand accent (buttons, links, active states) -- swapped by [data-accent]
   --accent-weak | color | tinted accent background (badges, hover glow) -- swapped by [data-accent]
   --accent-contrast | color | text/icon color rendered on top of --accent
   --radius | size | default corner radius

   -- Status colors (fixed, NOT accent-themed per UI-STANDARDS.md) --
   --status-success | color | success text/icon
   --status-success-bg | color | success tinted background
   --status-warning | color | warning text/icon
   --status-warning-bg | color | warning tinted background
   --status-error | color | error text/icon
   --status-error-bg | color | error tinted background
   --status-neutral | color | neutral/muted status text
   --status-beta | color | beta status text/icon
   --status-beta-bg | color | beta tinted background

   -- Buttons --
   --btn-bg | color | primary button background
   --btn-fg | color | primary button text

   -- Inputs / select / textarea --
   --input-font-size | size | input, select, and textarea text size
   --input-border | color | input, select, and textarea border color
   --input-bg | color | input, select, and textarea background

   -- Labels --
   --label-size | size | form label text size
   --label-color | color | form label text color

   -- Cards --
   --card-bg | color | .rc-card background
   --card-border | color | .rc-card border color
   --card-radius | size | .rc-card corner radius

   -- Tables --
   --table-header-bg | color | table header row background
   --table-row-hover-bg | color | table body row hover background

   -- Badges (per-variant; status variants default to the fixed status tokens above, not accent) --
   --badge-success-bg | color | success badge background
   --badge-success-fg | color | success badge text
   --badge-warning-bg | color | warning badge background
   --badge-warning-fg | color | warning badge text
   --badge-high-bg | color | high-priority badge background
   --badge-high-fg | color | high-priority badge text
   --badge-error-bg | color | error badge background
   --badge-error-fg | color | error badge text
   --badge-info-bg | color | info badge background (accent-tinted)
   --badge-info-fg | color | info badge text (accent)
   --badge-neutral-bg | color | neutral badge background
   --badge-neutral-fg | color | neutral badge text
   --badge-beta-bg | color | beta badge background
   --badge-beta-fg | color | beta badge text

   -- Chips --
   --chip-bg | color | chip background
   --chip-fg | color | chip text
   --chip-border | color | chip border color
   --chip-active-bg | color | selected chip background (accent)
   --chip-active-fg | color | selected chip text (accent-contrast)

   -- Banners --
   --banner-success-bg | color | success banner background
   --banner-success-fg | color | success banner text
   --banner-error-fg | color | error banner text
   --banner-error-border | color | error banner border

   -- Stat tiles --
   --stat-value-size | size | stat tile headline number size

   -- Sidebar (rule + var(--token, fallback) consumption both live in sidebar.css) --
   --sidebar-active-bg | color | active sidebar link background (accent-tinted)
   --sidebar-active-fg | color | active sidebar link text (accent)
*/

:root {
    --bg: #f4f6f9;
    --surface: #ffffff;
    --surface-2: #eef2f7;
    --text: #0e2238;
    --text-dim: #5d6e80;
    --border: #e0e6ee;
    /* TASK-WEB-A11Y-001: darkened from #e8621c (same hue/saturation, lower lightness only -- WCAG
       AA 4.5:1 requires it). #e8621c measured 3.39:1 for accent-colored text on white/page bg and
       3.39:1 for white text on an accent button background -- both fail. #a64411 clears every
       usage this token feeds (wordmark text, plain links, sidebar-active fg on --accent-weak,
       button fg/bg, chip-active) with a >=5.2:1 margin on all of them -- picked with headroom, not
       the bare 4.5:1 minimum, after a live axe rerun showed one specific badge instance (a table's
       LAST row, near the flush card's rounded-corner overflow clip) rendering against a slightly
       different effective background than the other rows despite IDENTICAL computed CSS
       (getComputedStyle confirmed identical color/background-color/opacity on all three) -- axe's
       contrast check evidently samples more than the immediate ancestor chain in that geometry, so
       every status/accent token below now targets ~5.2-5.6:1 rather than the theoretical 4.5:1
       floor. See Docs/A11Y-BASELINE.md for the finding this fixes. */
    --accent: #a64411;
    --accent-weak: #fdeadd;
    --accent-contrast: #ffffff;
    --radius: 12px;

    /* ---- Brand semantic layer (SUP-007 D4): LITERAL-default master tokens, deliberately NOT
       derived from --accent/--surface/etc (see the TOKEN MAP note above and the "TWO KINDS OF
       TOKEN" note). Defaults match the base globals they stand in for so this refactor moves zero
       pixels on the platform default; [data-accent] blocks below keep --brand-primary/-weak in
       lockstep with --accent/--accent-weak per template. ---- */
    /* TASK-WEB-A11Y-001: kept in lockstep with --accent above (same darkened value; SUP-007's own
       rule -- see the block above [data-accent] further down). */
    --brand-primary: #a64411;
    --brand-primary-weak: #fdeadd;
    --brand-primary-contrast: #ffffff;
    --brand-surface: #ffffff;
    --brand-surface-2: #eef2f7;
    --brand-text: #0e2238;
    --brand-border: #e0e6ee;
    --brand-radius: 12px;

    /* TASK-WEB-A11Y-001: success/warning/error darkened (same hue/saturation, lower lightness
       only) -- each one is used BOTH as text on a light tint background (badge fg on its own
       *-bg tint) AND as white text on a solid background (system-health banner/pill) AND as plain
       text on the page background (verdict/summary text) -- the *-bg rgba literals below are
       updated to the SAME new RGB (alpha unchanged) so a badge's own tint stays derived from its
       fg color, not stale. Old values: success #16a34a, warning #d97706, error #e11d48 -- all
       measured serious color-contrast failures in Docs/A11Y-BASELINE.md. Targeted at >=5.2:1 (not
       the 4.5:1 floor) against the WORST-case tint background (the tint blended over --bg #f4f6f9,
       not just --surface #ffffff) -- see the --accent comment above for why the extra margin: a
       live axe rerun flagged one specific badge instance despite pixel-identical computed CSS to
       its passing siblings, and the larger margin made that instance clean too without having to
       fully root-cause axe's exact sampling geometry. */
    --status-success: #0e6930;
    --status-success-bg: rgba(14, 105, 48, .12);
    --status-warning: #884a04;
    --status-warning-bg: rgba(136, 74, 4, .14);
    --status-error: #b01738;
    --status-error-bg: rgba(176, 23, 56, .12);
    --status-neutral: #5d6e80;
    /* TASK-WEB-A11Y-001: darkened from #8b5cf6 (same hue/saturation) -- missed in the first pass of
       this ticket's fix (the seed-data admin-users scan never rendered a beta badge; a full-suite
       gate run at realistic user volume did). badge-beta-fg on its own tint background measured
       well under 4.5:1 with the old value; #5e1df2 clears >=5.2:1 everywhere the same way the other
       three status colors do. */
    --status-beta: #5e1df2;
    --status-beta-bg: rgba(94, 29, 242, .14);

    /* ---- Component-family tokens (two-tier refactor): LITERAL-default tokens only -- see the
       "TWO KINDS OF TOKEN" note above. Every DERIVED token (--btn-bg, --card-bg, --badge-*-bg/-fg,
       --chip-*, --banner-*, --sidebar-active-*, --input-border, --input-bg, --label-color,
       --card-border, --card-radius, --table-*) is deliberately NOT declared here; it is consumed
       via var(--token, var(--fallback)) directly at its component rule instead. ---- */
    --input-font-size: 14px;
    --label-size: 12.5px;
    --badge-high-bg: #ffedd5;
    --badge-high-fg: #c2410c;
    --stat-value-size: 30px;
}

/* SUP-007: each block also sets --brand-primary/-weak to the SAME value as --accent/-weak so
   template switching keeps reskinning the Brand-fallback family tokens (btn-bg, badge-info,
   chip-active, sidebar-active) exactly as it did before the Brand layer existed. */
[data-accent="orange"] { --accent: #a64411; --accent-weak: #fdeadd; --brand-primary: #a64411; --brand-primary-weak: #fdeadd; }
[data-accent="navy"] { --accent: #02284a; --accent-weak: #dde6f1; --brand-primary: #02284a; --brand-primary-weak: #dde6f1; }
[data-accent="sky"] { --accent: #1893c4; --accent-weak: #e0f3fb; --brand-primary: #1893c4; --brand-primary-weak: #e0f3fb; }
[data-accent="green"] { --accent: #6b9d1f; --accent-weak: #eef5dd; --brand-primary: #6b9d1f; --brand-primary-weak: #eef5dd; }

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

* { box-sizing: border-box; margin: 0; padding: 0; }

body {
    font-family: 'Inter', system-ui, sans-serif;
    background: var(--bg);
    color: var(--text);
    line-height: 1.5;
}

h1, h2, h3 { letter-spacing: -0.02em; font-weight: 800; }
h1 { font-size: 22px; margin-bottom: 16px; }
h2 { font-size: 18px; margin-bottom: 12px; }
h3 { font-size: 15px; margin-bottom: 8px; }
a { color: var(--accent); }

/* TASK-WEB-A11Y-001 (page-has-heading-one): lessons-create and feedback-submit used a bare <h2> as
   their ONLY heading, skipping level 1 (axe: moderate). Standard visually-hidden/"sr-only" clip
   technique -- the element stays in the accessibility tree (screen readers announce it, axe counts
   it as a real level-one heading) but is clipped to 1x1px and off the visual flow, so sighted users
   see no change at all. Used to add a real <h1> next to an existing, unchanged, visible <h2> instead
   of re-leveling the visible heading (see the comment in Pages/Lessons/Create.cshtml for why:
   dozens of existing Playwright specs wait on/assert "h2" for these exact pages). */
.rc-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;
}

/* ---- Header / nav ---- */

.rc-header {
    position: sticky;
    top: 0;
    z-index: 20;
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    padding: 0 28px;
    height: 64px;
    display: flex;
    align-items: center;
    gap: 16px;
}

.rc-emblem { height: 38px; width: auto; flex: none; }

.rc-wordmark { font-weight: 800; font-size: 17px; letter-spacing: -0.02em; }
.rc-wordmark__road { color: #02284a; }
.rc-wordmark__coach { color: var(--accent); }

.rc-nav { display: flex; align-items: center; gap: 4px; margin-left: 12px; }

.rc-nav-link {
    color: var(--text-dim);
    text-decoration: none;
    padding: 8px 12px;
    border-radius: 9px;
    font-size: 13.5px;
    font-weight: 600;
    font-family: inherit;
    border: none;
    background: none;
    cursor: pointer;
}

.rc-nav-link:hover { background: var(--surface-2); color: var(--text); }

.rc-spacer { flex: 1; }

.rc-nav-dropdown { display: inline-block; position: relative; }
.rc-nav-dropdown > summary { list-style: none; cursor: pointer; }
.rc-nav-dropdown > summary::-webkit-details-marker { display: none; }
.rc-nav-dropdown[open] > summary::after { content: " \25B2"; font-size: 9px; }
.rc-nav-dropdown > summary::after { content: " \25BC"; font-size: 9px; }

.rc-nav-dropdown-menu {
    position: absolute;
    top: 100%;
    left: 0;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 10px;
    box-shadow: 0 8px 24px rgba(20, 30, 50, .10);
    min-width: 180px;
    z-index: 50;
    padding: 6px;
}

/* Items sit on a surface-colored menu regardless of accent -- force the readable text color so a
   themed accent never makes a menu item blend into its own background. */
.rc-nav-dropdown-menu .rc-nav-link { display: block; color: var(--text); padding: 8px 10px; white-space: nowrap; }
.rc-nav-dropdown-menu .rc-nav-link:hover { background: var(--surface-2); color: var(--text); }

/* PORTAL-SPLIT Phase 1: the personal accent-switcher styles are REMOVED with the feature -- the
   template is server truth now (platform default -> school override); see /system/templates.
   TemplateParityTests scans this file so the swatch rules cannot creep back in a merge. */

/* ---- UI Templates gallery (/system/templates, TPL-100) ---- */

.rc-template-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: 16px;
    margin-bottom: 90px; /* room for the fixed apply bar so it never covers the last row */
}

/* Each card carries its own data-accent="<key>" -- the [data-accent] blocks above re-scope
   --accent/--accent-weak for everything inside it, so the mini-mock renders the template's REAL
   tokens (no duplicated hex values here; duplicating them would violate the parity rule). */
.rc-template-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 14px;
    cursor: pointer;
    text-align: left;
    font-family: inherit;
}

.rc-template-card:hover { border-color: var(--accent); }
.rc-template-card--previewing { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-weak); }

.rc-mini-mock {
    display: flex;
    gap: 8px;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 10px;
    height: 96px;
    pointer-events: none;
}

.rc-mini-mock__sidebar {
    width: 34%;
    background: var(--accent);
    border-radius: 6px;
    padding: 7px 6px;
    display: flex;
    flex-direction: column;
    gap: 5px;
}

.rc-mini-mock__navline { height: 5px; border-radius: 3px; background: rgba(255, 255, 255, .45); }
.rc-mini-mock__navline--active { background: var(--accent-contrast); }

.rc-mini-mock__body { flex: 1; display: flex; flex-direction: column; gap: 6px; padding-top: 2px; }
.rc-mini-mock__title { height: 7px; width: 70%; border-radius: 3px; background: var(--text-dim); opacity: .55; }
.rc-mini-mock__chip { height: 12px; width: 46%; border-radius: 6px; background: var(--accent-weak); }
.rc-mini-mock__btn { height: 14px; width: 58%; border-radius: 6px; background: var(--accent); margin-top: auto; }

.rc-template-card__meta { display: flex; flex-direction: column; gap: 3px; margin-top: 10px; }
.rc-template-card__name { font-weight: 700; font-size: 14px; }
.rc-template-card__usage { font-size: 12.5px; color: var(--text-dim); }

/* Fixed apply/discard bar -- appears while previewing; the preview is PAGE-ONLY by design
   (operator decision 2026-07-03): nothing is stored client-side, navigating away abandons it. */
.rc-template-applybar {
    position: fixed;
    left: 50%;
    transform: translateX(-50%);
    bottom: 18px;
    z-index: 60;
    display: flex;
    align-items: center;
    gap: 14px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 12px;
    box-shadow: 0 10px 30px rgba(20, 30, 50, .18);
    padding: 12px 16px;
    max-width: calc(100vw - 32px);
}

/* ---- Page container ---- */

.rc-container {
    max-width: 1100px;
    margin: 24px auto;
    padding: 0 24px;
}

/* ---- Card ---- */

.rc-card {
    background: var(--card-bg, var(--brand-surface));
    border: 1px solid var(--card-border, var(--brand-border));
    border-radius: var(--card-radius, var(--brand-radius));
    padding: 20px;
    margin-bottom: 16px;
}

.rc-card--flush { padding: 0; overflow: hidden; }
.rc-card--center { text-align: center; padding: 40px; }
.rc-card--dashed { border-style: dashed; }

/* ---- Stat tiles (mockup 1.2: platform dashboard) ---- */

.rc-stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 16px; }

.rc-stat-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 18px 20px;
}

.rc-stat-value { font-size: var(--stat-value-size); font-weight: 800; letter-spacing: -0.02em; line-height: 1.1; }
.rc-stat-label { font-size: 13px; color: var(--text-dim); margin-top: 4px; }
.rc-stat-sub { font-size: 12px; color: var(--text-dim); margin-top: 6px; }

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

.rc-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 7px;
    height: 40px;
    padding: 0 16px;
    border-radius: 9px;
    font-family: inherit;
    font-size: 13.5px;
    font-weight: 600;
    border: none;
    cursor: pointer;
    text-decoration: none;
}

.rc-btn--block { width: 100%; }
.rc-btn--primary, .rc-feedback-submit-btn { background: var(--btn-bg, var(--brand-primary)); color: var(--btn-fg, var(--brand-primary-contrast)); }
/* SUP-008: .rc-state-hover/.rc-state-force-hover are STATIC aliases of the real :hover rule so
   the catalog can render a frozen hover sample (edit mode swallows real hovers) and a test can
   force the real state for a computed-style parity assertion. Keep alias and :hover in the SAME
   selector list -- one rule, never a second diverging copy. Same pattern for focus/disabled. */
.rc-btn--primary:hover, .rc-feedback-submit-btn:hover,
.rc-btn--primary.rc-state-hover, .rc-btn--primary.rc-state-force-hover { filter: brightness(0.92); }
.rc-btn--secondary { background: var(--surface-2); color: var(--text); border: 1px solid var(--border); }
.rc-btn--secondary:hover, .rc-btn--secondary.rc-state-hover { background: var(--border); }
.rc-btn--outline { background: transparent; color: var(--accent); border: 1px solid var(--accent); }
.rc-btn--ghost { background: transparent; color: var(--text-dim); }
.rc-btn--destructive { background: var(--status-error); color: #fff; }

/* ---- Badges ---- */

.rc-badge {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    font-weight: 700;
    padding: 3px 10px;
    border-radius: 20px;
}

.rc-badge__dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }

.rc-badge--success { background: var(--badge-success-bg, var(--status-success-bg)); color: var(--badge-success-fg, var(--status-success)); }
.rc-badge--warning { background: var(--badge-warning-bg, var(--status-warning-bg)); color: var(--badge-warning-fg, var(--status-warning)); }
.rc-badge--high { background: var(--badge-high-bg); color: var(--badge-high-fg); }
.rc-badge--error { background: var(--badge-error-bg, var(--status-error-bg)); color: var(--badge-error-fg, var(--status-error)); }
.rc-badge--info { background: var(--badge-info-bg, var(--brand-primary-weak)); color: var(--badge-info-fg, var(--brand-primary)); }
.rc-badge--neutral { background: var(--badge-neutral-bg, var(--brand-surface-2)); color: var(--badge-neutral-fg, var(--status-neutral)); }
.rc-badge--beta { background: var(--badge-beta-bg, var(--status-beta-bg)); color: var(--badge-beta-fg, var(--status-beta)); }

/* TASK-WEB-SYL-002 (SYL-102, G24): routed skills vs checklist items must be visually distinct
   CONTAINERS, not just distinct text -- a solid accent-bordered block for "on the route" (the
   engine drives to these) vs a dashed, muted block for "not routed" (checklist-only). Theme-safe:
   reuses existing tokens, no new colors. */
.rc-segment-skills {
    border-left: 3px solid var(--brand-primary);
    padding: 4px 0 4px 10px;
    margin-bottom: 6px;
    /* Lay the skill pills out as a wrapping row with real gaps, rather than inline text flow --
       at phone width these wrap to 2-3 lines and needed honest spacing between them. */
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

.rc-segment-checklist {
    border: 1px dashed var(--border);
    background: var(--brand-surface-2);
    border-radius: 6px;
    padding: 6px 10px;
}

/* TASK-WEB-SYL-002 REWORK 2 (Entry 231, I5): at narrow width the checklist-item edit table's
   Label/Note fields were shrinking to fit whatever column width the table's auto layout assigned,
   which visually truncated their OWN typed text (the full value stayed bound/editable, just not
   readable without clicking in). This min-width forces the cell -- and so the whole table --
   wider than a 390px viewport, so the EXISTING .rc-table-wrap { overflow-x: auto } scrolls the
   table instead of squeezing the field, matching how every other .rc-table on this site already
   behaves at narrow width.
   TASK-WEB-SYL-002 REWORK 3 (Entry 242, I5 -- 2nd occurrence): 220px was tuned to fit the ORPC
   seed's own ~24-char labels; a realistic-but-longer label still clipped, because a fixed width
   on a single-line control can only ever be "wide enough for whatever length it was tested
   against." The length-independent fix is in the markup (Label/Note are now <textarea>, which
   wraps instead of overflowing horizontally, so scrollWidth<=clientWidth holds regardless of
   content length) -- this min-width is now just a comfortable minimum EDITING width, not a
   truncation-prevention mechanism, so its exact value is no longer load-bearing for correctness.
   Widened to 420px purely for comfortable line length (about 2 lines of a full label at this
   font size before wrapping), still relying on the same .rc-table-wrap horizontal scroll. */
.rc-checklist-item-cell { min-width: 420px; }

/* ---- Table ---- */

.rc-table { width: 100%; border-collapse: collapse; }
.rc-table th, .rc-table td { text-align: left; padding: 13px 18px; border-bottom: 1px solid var(--border); font-size: 13.5px; }
.rc-table th {
    font-family: 'JetBrains Mono', monospace;
    font-size: 10px;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--text-dim);
    background: var(--table-header-bg, var(--brand-surface-2));
}
.rc-table tbody tr:hover, .rc-table tbody tr.rc-state-hover { background: var(--table-row-hover-bg, var(--brand-surface-2)); }
.rc-table--clickable tbody tr { cursor: pointer; }

/* TASK-WEB-LES-UX-LIST: /lessons LIST screen phone layout. Measured defect: the 7-column
   .rc-table (Student/Instructor/School/Scheduled Start/Status/Skills/Actions) rendered 706px wide
   inside a 390px viewport -- document.documentElement.scrollWidth 647 vs a 390 clientWidth (257px of
   PAGE-level horizontal scroll; 320px was worse, 327px). .rc-table-wrap's overflow-x:auto
   (TASK-WEB-RESP-001 above) only contains a table that can shrink to fit its wrapper; it cannot
   shrink a table whose per-column min-content simply exceeds the viewport (the same "min-content is
   the narrowest UNBREAKABLE token per column" lesson already documented at this file's 767.98px
   media query for the Dashboard's "Recent feedback" grid item). The PREFERRED fix per the ticket
   (a responsive card layout, not just containment) is below: `.rc-lessons-table-view` (wrapping the
   existing table) is hidden below 768px and `.rc-lessons-cards` (Index.cshtml's new stacked-card
   markup, same seven fields, one card per lesson) takes its place -- desktop/tablet render is
   unchanged, only the phone breakpoint differs. Scoped to these NEW selectors only; the shared
   `.rc-table`/`.rc-table-wrap` rules above are untouched so no other screen's table is affected. */
.rc-lessons-cards {
    display: none;
    list-style: none;
    margin: 0;
    padding: 0;
    flex-direction: column;
    gap: 12px;
}

.rc-lesson-card__link,
.rc-lesson-card__link--static {
    display: block;
    box-sizing: border-box;
    min-height: 44px;
    background: var(--card-bg, var(--brand-surface));
    border: 1px solid var(--card-border, var(--brand-border));
    border-radius: var(--card-radius, var(--brand-radius));
    padding: 14px 16px;
    color: inherit;
    text-decoration: none;
}

/* Only an actionable card (Model.CanManageLessons -- the same flag that gates the table's own Edit
   link) gets the hover/active affordance; the read-only rc-lesson-card__link--static variant
   deliberately looks identical otherwise (same fields, same card), just not interactive. */
.rc-lesson-card__link:hover, .rc-lesson-card__link:active { background: var(--table-row-hover-bg, var(--brand-surface-2)); }

.rc-lesson-card__top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    margin-bottom: 8px;
}

/* min-width:0 overrides the flex-item default (min-width:auto), which otherwise refuses to shrink
   below the STUDENT NAME's own min-content width -- a single unbreakable long token (a real
   hyphen-free name/handle, proven live by a leftover-fixture row from another test in this same
   Playwright collection during full-suite VERIFY) would otherwise force this row -- and so the
   whole card/page -- wider than the viewport, exactly the class of bug site.css's 767.98px media
   query already documents for the Dashboard's "Recent feedback" grid item. overflow-wrap:anywhere
   is the length-independent fix: it wraps EVEN a single unbroken token once it would otherwise
   overflow, instead of only wrapping at existing spaces/hyphens. */
.rc-lesson-card__student { font-weight: 700; font-size: 15px; min-width: 0; overflow-wrap: anywhere; }

.rc-lesson-card__row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
    padding: 5px 0;
    border-top: 1px solid var(--border);
    font-size: 13.5px;
}

/* UX pass 2026-09-07: the phone card's two-line body. __when is the field an instructor is
   actually scanning for (which lesson, when), so it gets real weight instead of being one of
   four identical grey label/value rows; __meta carries skills/instructor/school in one muted
   line so nothing from the desktop table is lost. Replaces four stacked rows per card, which
   at 24 lessons was 48 rows repeating the same instructor and school. */
.rc-lesson-card__when { font-size: 15px; font-weight: 600; margin-top: 2px; }
.rc-lesson-card__meta { font-size: 13px; color: var(--muted); margin-top: 2px; }

.rc-lesson-card__row:first-of-type { border-top: none; }

/* TASK-WEB-LES-006: the record link is a SIBLING of .rc-lesson-card__link (never nested inside it
   -- an <a> cannot nest inside another <a>), rendered as its own full-width tappable strip below
   the main card body. Same >=44px touch-target floor as .rc-lesson-card__link above. */
.rc-lesson-card__record-link {
    display: block;
    box-sizing: border-box;
    min-height: 44px;
    margin-top: 8px;
    padding: 12px 16px;
    text-align: center;
    background: var(--card-bg, var(--brand-surface));
    border: 1px solid var(--card-border, var(--brand-border));
    border-radius: var(--card-radius, var(--brand-radius));
    color: inherit;
    text-decoration: none;
    font-size: 13.5px;
    font-weight: 600;
}
.rc-lesson-card__record-link:hover, .rc-lesson-card__record-link:active { background: var(--table-row-hover-bg, var(--brand-surface-2)); }

.rc-lesson-card__label {
    flex: 0 0 auto;
    color: var(--text-dim);
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: .05em;
}

/* Same min-width:0 / overflow-wrap:anywhere reasoning as .rc-lesson-card__student above, applied to
   every value cell (instructor/school/scheduled/skills) -- any of them can carry an arbitrarily long
   real value (a long school name, a long instructor name). flex:1 1 auto lets this be the one side
   of the row that actually shrinks (the label side is flex:0 0 auto, it never needs to). */
.rc-lesson-card__value { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; text-align: right; }

@media (max-width: 767.98px) {
    .rc-lessons-table-view { display: none; }
    .rc-lessons-cards { display: flex; }
}

/* TASK-WEB-DEMO-012 (TD-393): FAM-101 (Pages/Family/Lessons.cshtml) reuses every selector above
   verbatim for its own Upcoming/History card lists -- same overflow problem, same fix, not a
   second copy (R13). The one FAM-101-only addition is this actions row: a parent-planned Scheduled
   upcoming card carries Edit + Cancel below the Details link (the table row's own
   "Details . Edit . Cancel" text links), which the Admin card never needed. Reuses the existing
   .rc-link-btn/.rc-link-btn--danger inline-text-link style (below) so no new button styling. */
.rc-lesson-card__actions {
    margin-top: 8px;
    padding-top: 8px;
    border-top: 1px solid var(--border);
    text-align: center;
    font-size: 13.5px;
}
.rc-lesson-card__actions .rc-link-btn { display: inline-block; padding: 6px 4px; }

/* ---- Form fields ---- */

.rc-form-group { margin-bottom: 12px; }

.rc-label {
    display: block;
    font-size: var(--label-size);
    font-weight: 600;
    color: var(--label-color, var(--text-dim));
    text-transform: uppercase;
    letter-spacing: .05em;
    margin-bottom: 6px;
}

/* TASK-SRV-PLAN-002 (REV-1 LOW): visible required-field marker (a red asterisk in a label). */
.rc-required { color: var(--status-error); font-weight: 700; margin-left: 2px; }

/* TASK-WEB-PLAN-003: a table cell can only POST via a <form>, not a plain <a> -- these two rules
   let a Cancel <form><button> sit inline among <a> links in the same cell and look like one of
   them (an unstyled button, not a boxed .rc-btn), matching the surrounding "Details | Edit | Cancel"
   text-link row. */
.rc-inline-form { display: inline; }
.rc-link-btn {
    background: none; border: none; padding: 0; margin: 0; font: inherit;
    color: var(--accent); text-decoration: underline; cursor: pointer;
}
.rc-link-btn:hover { opacity: 0.8; }
.rc-link-btn--danger { color: var(--status-error); }

.rc-input, .rc-select, .rc-textarea {
    width: 100%;
    height: 42px;
    padding: 0 13px;
    border: 1px solid var(--input-border, var(--brand-border));
    background: var(--input-bg, var(--brand-surface-2));
    border-radius: 9px;
    font-family: inherit;
    font-size: var(--input-font-size);
    color: var(--text);
}

.rc-textarea { height: auto; padding: 11px 13px; resize: vertical; }

/* TASK-WEB-SYL-002 REWORK 4 (Entry 249): `.rc-textarea`'s `height: auto` only picks up the
   element's OWN intrinsic size, which for a <textarea> is its `rows` attribute, not its content --
   so a fixed `rows="2"` clips a genuinely multi-line value vertically no matter how large `rows` is
   set to (same shape as Entry 242's horizontal `min-width` lesson: a fixed number is never
   length-independent). `data-rc-autogrow` (wwwroot/js/roadcoach.js) measures the element's own
   scrollHeight and sets a real height instead. `overflow-y: hidden` here only suppresses the
   scrollbar flash between initial layout and that JS running -- it does not itself fix truncation. */
.rc-textarea[data-rc-autogrow] { overflow-y: hidden; }

.rc-input:focus, .rc-select:focus, .rc-textarea:focus,
.rc-input.rc-state-focus, .rc-select.rc-state-focus, .rc-textarea.rc-state-focus {
    outline: none;
    border: 2px solid var(--accent);
    box-shadow: 0 0 0 4px var(--accent-weak);
}

.rc-input--error, .rc-textarea--error {
    border: 1.5px solid var(--status-error);
    background: #fff5f7;
}

.rc-field-error { font-size: 11.5px; color: var(--status-error); margin-top: 6px; }

/* 64px, not 50: Chromium reserves spinner width inside number inputs, which clipped the
   "max" placeholder to "ma" at 50px. */
.rc-input--xs { width: 64px; height: 30px; padding: 0 6px; font-size: 12px; }
.rc-input--sm { width: 90px; }
/* State (two-letter) boxes: WIDTH-only narrowing -- rc-input--xs also shrinks height/font, which
   made the ST inputs visibly shorter than their row siblings (operator 2026-07-03). */
.rc-input--st { width: 64px; text-transform: uppercase; }
.rc-input--md { width: 130px; }
.rc-input--lg { width: 170px; }
.rc-input--xl { width: 260px; }
/* TASK-WEB-SEC-002: replaces a page-authored style="max-width: 220px;" on the route-code search
   box (Pages/RcSystem/Routes/Index.cshtml, Detail.cshtml) -- CSP's style-src can drop
   'unsafe-inline' only if no page carries an inline style="" attribute. */
.rc-input--route-code { max-width: 220px; }

/* UX-SHARED (2026-09-07): a flex row whose children are (checkbox element, BARE TEXT NODE) is only
   2 flex items today, so it does not yet exhibit the interleaved-text wrap bug (that needs a 3rd
   item -- see .rc-map-legend__row below, and .rc-lesson-record__checklist-item which already had
   it). It is still latent: any future hint/note span added next to the label text would create the
   same 3-item shape. rc-check-row__label pre-empts that -- the label text is now its OWN flex item
   (flex:1 1 auto so it owns all remaining row width; min-width:0 so IT wraps within that width
   instead of being squeezed into a narrow shared column) -- so a future note/hint span can nest
   INSIDE rc-check-row__label (same pattern as .rc-map-legend__label) without ever reintroducing a
   3rd top-level flex item. min-height:44px makes the row (a <label> wrapping its own checkbox) a
   real tap target, matching the pattern .rc-lesson-record__checklist-item already ships. */
.rc-check-row { display: flex; align-items: center; gap: 6px; font-size: 13.5px; padding: 2px 0; min-height: 44px; }
.rc-check-row input[type="checkbox"] { flex: 0 0 auto; }
.rc-check-row__label { flex: 1 1 auto; min-width: 0; }
.rc-check-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 4px; margin-bottom: 12px; }
.rc-check-list { display: flex; flex-direction: column; gap: 6px; }
.rc-fieldset { border: none; padding: 0; margin: 0; }
.rc-legend { font-weight: 500; margin-bottom: 4px; font-size: 13.5px; }

/* ---- Skill Mapping accordion ---- */

.rc-mapping-success { border-left: 4px solid var(--status-success); }
.rc-mapping-details { margin-bottom: 16px; }
.rc-mapping-summary {
    cursor: pointer;
    padding: 14px 18px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    list-style: none;
}
.rc-mapping-body { margin-top: 4px; padding: 20px; }
.rc-mapping-span-2 { grid-column: span 2; }

/* ---- Login ---- */

.rc-login { max-width: 400px; margin: 80px auto; padding: 0 24px; }
.rc-login__title { text-align: center; margin-bottom: 24px; }
.rc-login__error { color: var(--status-error); margin-bottom: 12px; }

/* ---- Misc shared bits ---- */

.rc-muted { color: var(--text-dim); }
.rc-mono { font-family: 'JetBrains Mono', monospace; }
.rc-text-sm { font-size: 12.8px; }
.rc-text-xs { font-size: 11.5px; }
.rc-success-banner, .rc-feedback-success-banner { background: var(--banner-success-bg, var(--status-success-bg)); color: var(--banner-success-fg, var(--status-success)); padding: 12px; border-radius: 6px; margin-bottom: 16px; }
.rc-screenshot-preview { max-width: 600px; max-height: 400px; border: 1px solid var(--border); border-radius: 9px; margin-top: 4px; }
.rc-branding-logo-preview { max-width: 240px; max-height: 96px; border: 1px solid var(--border); border-radius: 9px; padding: 8px; background: var(--surface); }
.rc-description-block { white-space: pre-wrap; background: var(--bg); padding: 12px; border-radius: 9px; margin-top: 4px; }
.rc-loading-text { color: var(--text-dim); font-size: 14px; }
.rc-hidden { display: none; }
.rc-error-banner, .rc-feedback-error-banner {
    color: var(--banner-error-fg, var(--status-error));
    margin-bottom: 12px;
    padding: 10px 14px;
    border: 1px solid var(--banner-error-border, var(--status-error));
    border-radius: 9px;
    font-size: 14px;
}
.rc-skill-result {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 10px 0;
    border-bottom: 1px solid var(--border);
}
.rc-skill-result__name { font-weight: 500; }

.rc-skill-row { padding: 2px 0; }
/* flex:1 pushes the min/max/count cluster to the column's right edge so the number
   boxes align vertically across every row (operator request 2026-07-02). */
.rc-skill-row__label { font-weight: 400; cursor: pointer; min-width: 168px; flex: 1 1 auto; }
.rc-skill-row__count { color: var(--text); font-weight: 700; min-width: 28px; display: inline-block; }

.rc-skill-col { display: flex; flex-direction: column; gap: 4px; min-width: 340px; }

/* Skill editor: category groups with drag-and-drop re-filing (2026-07-02). */
.rc-skill-cat-group { margin-bottom: 20px; padding: 8px; border: 1px dashed transparent; border-radius: var(--radius); }
.rc-skill-cat-group__head { font-size: 16px; margin: 0 0 8px 2px; }
.rc-skill-cat-group.rc-drop-target { border-color: var(--accent); background: var(--surface-2, rgba(0,0,0,0.03)); }
.rc-drag-grip { cursor: grab; color: var(--text-dim); font-weight: 700; letter-spacing: 1px; user-select: none; }
.rc-dragging { opacity: 0.45; }

/* Route generator: featured Milestones & Major section -- the skills the engine routes around. */
.rc-skill-featured { border-left: 4px solid var(--accent); padding-left: 12px; margin-bottom: 14px; }
.rc-skill-featured__head { font-size: 14px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.4px; margin: 0 0 6px; }
.rc-skill-col__head {
    font-weight: 700;
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--text-dim);
    border-bottom: 1px solid var(--border);
    padding-bottom: 3px;
    margin-bottom: 2px;
}

/* ---- Address autocomplete (Route Generator, GEO-004) ---- */

.rc-geo-box { position: relative; }
.rc-geo-suggest {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    z-index: 1000;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 6px;
    box-shadow: 0 4px 10px rgba(0, 0, 0, .12);
    max-height: 220px;
    overflow-y: auto;
    margin-top: 2px;
}
.rc-geo-suggest__item { padding: 6px 10px; cursor: pointer; font-size: 13px; }
.rc-geo-suggest__item:hover, .rc-geo-suggest__item.active { background: var(--surface-2); }
.rc-geo-suggest__empty { padding: 8px 10px; font-size: 13px; color: var(--text-dim); font-style: italic; }

.rc-details-summary { cursor: pointer; font-weight: 600; font-size: 13px; color: var(--text-dim); }

/* ---- Layout utilities (a small, deliberately constrained set -- new one-off pixel values get a
   named class here, not an inline style, per UI-STANDARDS.md rule 2) ---- */

.rc-flex { display: flex; }
.rc-flex--col { flex-direction: column; }
.rc-flex--wrap { flex-wrap: wrap; }
.rc-items-center { align-items: center; }
.rc-items-end { align-items: flex-end; }
.rc-items-start { align-items: flex-start; }
.rc-justify-between { justify-content: space-between; }
.rc-justify-end { justify-content: flex-end; }
.rc-flex-1 { flex: 1; }

.rc-gap-1 { gap: 4px; }
.rc-gap-2 { gap: 8px; }
.rc-gap-3 { gap: 12px; }
.rc-gap-4 { gap: 16px; }
.rc-gap-5 { gap: 20px; }
.rc-gap-6 { gap: 24px; }

.rc-grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.rc-grid-2--narrow-right { display: grid; grid-template-columns: 1fr 320px; gap: 16px; }
.rc-grid-2--sidebar { display: grid; grid-template-columns: 260px minmax(0, 1fr); gap: 16px; }

/* TASK-WEB-DEMO-001: legend-BELOW-map layout for _PlannedRouteMap.cshtml callers whose card is
   too narrow for a fixed 320px side column (LES-103, FAM-102 -- ~530px cards, see that partial's
   own comment). Ordinary flex-column block stacking, not a grid -- it needs no media-query
   collapse rule the way .rc-grid-2--narrow-right does, because it is already one column at every
   width. */
.rc-map-legend-stack { display: flex; flex-direction: column; gap: 16px; }

/* TASK-WEB-GEN-004: a grid item's default cross-axis behavior in a CSS grid row is `stretch` --
   without this, a short grid item (e.g. the Route Generator's Legend-only side card, now that the
   turn-by-turn list that used to fill it is gone) silently stretches to match the tallest item in
   its row (the 560px map), leaving a large dead blank area below its own content. Generic, reusable
   utility -- opt-in per grid item, does not change `.rc-grid-2--narrow-right`'s (or any other grid
   utility's) default stretch behavior for existing consumers. */
.rc-self-start { align-self: start; }

.rc-mb-0 { margin-bottom: 0; }
.rc-filter-label { font-size: 13.5px; font-weight: 600; color: var(--text-dim); }
.rc-table__description-cell { max-width: 300px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.rc-mt-1 { margin-top: 8px; }
.rc-mt-2 { margin-top: 12px; }
.rc-mt-3 { margin-top: 16px; }
.rc-mt-4 { margin-top: 20px; }
.rc-mb-1 { margin-bottom: 8px; }
.rc-mb-2 { margin-bottom: 12px; }
.rc-mb-3 { margin-bottom: 16px; }
.rc-ml-1 { margin-left: 8px; }

.rc-w-full { width: 100%; }
.rc-min-w-0 { min-width: 0; }
.rc-inline { display: inline; }

/* TASK-WEB-RESP-001: reusable scroll container for a wide table -- the PAGE must never scroll
   sideways, but a table with many columns legitimately can, WITHIN its own box. Wrap the
   <table>, not the surrounding .rc-card (rc-card--flush already clips corners via overflow:
   hidden; nesting this overflow-x:auto div inside it does not fight that -- the card only clips
   ITS OWN box, this div manages its own independent scroll region). Applied at every width (not
   just the phone media query) since a wide table can also want to scroll on a narrow desktop
   window -- it is simply a no-op when the table already fits. */
.rc-table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.rc-cc-progress { font-weight: 600; }
.rc-mapping-rcs-id { font-weight: 400; }

/* ---- Fixed (non-themed) map / status colors -- used by the Route Generator's Leaflet map and
   legend. Map-pin colors are cartographic conventions (left/right/roundabout/start/end), not brand
   accents, so they stay fixed per UI-STANDARDS.md ("status colors NOT themed"). ---- */

.rc-dot { width: 13px; height: 13px; border-radius: 50%; border: 2px solid #fff; box-shadow: 0 0 0 1px #cbd5e1; display: inline-block; }
.rc-dot--left { background: #2563eb; }
.rc-dot--right { background: #16a34a; }
.rc-dot--roundabout { background: #ea580c; }
.rc-dot--start { background: #111827; }
.rc-dot--end { background: #b91c1c; }
/* TASK-WEB-GEN-005: Milestone/Major skill-pin tiers (distinct from the retained-but-dormant
   left/right/roundabout turn-kind colors above, so a re-enable of the maneuver pins never collides
   visually with the current skill-tier pins). */
.rc-dot--milestone { background: #ca8a04; }
.rc-dot--major { background: #7c3aed; }

.rc-map-legend { display: flex; flex-direction: column; gap: 2px; }
/* UX-SHARED (2026-09-07): the JS-generated #pin-legend row (RouteGenerator.cshtml's renderLegend())
   was (pin <span>, BARE skill-name text, tier <span>) -- three flex items. At a narrow viewport
   each item's own text wraps into its own narrow column instead of the row wrapping as one block,
   interleaving the pin/name/tier text ("1.2 Preparing to / Drive" beside "pre-drive / sequence" is
   the same shape's failure on .rc-lesson-record__checklist-item). rc-map-legend__label groups the
   name + tier badge into ONE flex item (flex:1 1 auto so it owns the remaining row width;
   min-width:0 so it wraps internally instead of being squeezed into a narrow shared column),
   leaving exactly two top-level flex items: the leading icon (.rc-dot / .rc-pin) and the label. */
.rc-map-legend__row { display: flex; align-items: center; gap: 8px; padding: 2px 0; }
.rc-map-legend__row > .rc-dot, .rc-map-legend__row > .rc-pin { flex: 0 0 auto; }
.rc-map-legend__label { flex: 1 1 auto; min-width: 0; }

.rc-pin {
    display: flex;
    align-items: center;
    justify-content: center;
    color: #fff;
    font-weight: 700;
    font-size: 11px;
    border: 2px solid #fff;
    border-radius: 50%;
    box-shadow: 0 0 0 1px rgba(0, 0, 0, .25);
}
.rc-pin--turn { width: 22px; height: 22px; } /* DORMANT (TASK-WEB-GEN-005) -- retained for re-enable, see renderMap's comment */
.rc-pin--endpoint { width: 26px; height: 26px; }
.rc-pin--left { background: #2563eb; } /* DORMANT (TASK-WEB-GEN-005) */
.rc-pin--right { background: #16a34a; } /* DORMANT (TASK-WEB-GEN-005) */
.rc-pin--roundabout { background: #ea580c; } /* DORMANT (TASK-WEB-GEN-005) */
.rc-pin--start { background: #111827; }
.rc-pin--end { background: #b91c1c; }
.rc-pin--skill { width: 22px; height: 22px; } /* TASK-WEB-GEN-005: Milestone/Major realized-location pins */
.rc-pin--milestone { background: #ca8a04; }
.rc-pin--major { background: #7c3aed; }
/* TASK-WEB-CMP-002: persisted custom-stop pin (RouteMapPinBuilder's "Custom" tier title) -- a
   distinct slate color so a custom stop is never confused with a real skill-tier pin (gold
   Milestone, violet Major) on the SAME map. Sized via .rc-pin--skill like every other
   realized-location pin above. */
.rc-pin--custom { background: #475569; }
/* TASK-WEB-CMP-001: Route Composer catalog site pins -- CLICKABLE candidate locations to add as a
   waypoint, a distinct concept from every pin class above (those are persisted/realized-route
   display only, no click interaction -- see RouteMapPinBuilder.cs's own doc comment). */
.rc-pin--catalog { width: 20px; height: 20px; background: #0d9488; }

/* TASK-WEB-CMP-001: composed waypoint list -- kind/state modifiers must be visually distinct
   (custom vs. catalog-picked, pad-inserted, refused-but-retained). */
.rc-composer-item {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 8px;
    padding: 8px 0;
    border-bottom: 1px solid var(--border);
}
.rc-composer-item--custom { border-left: 3px solid #7c3aed; padding-left: 8px; }
.rc-composer-item--pad { border-left: 3px solid #ea580c; padding-left: 8px; }
.rc-composer-item--refused { background: var(--status-error-bg); border-radius: 6px; padding: 6px 8px; }
.rc-composer-item__label { font-weight: 500; }
.rc-composer-item__refusal { font-size: 12.8px; margin-top: 2px; }
.rc-composer-item__actions button { margin-left: 4px; }
.rc-composer-verdict { padding: 4px 0; font-size: 13px; }
.rc-leg-row { padding: 4px 0; border-bottom: 1px solid var(--border); font-size: 13px; }

/* Over-target signal: an explicit painted background (not just border text like .rc-error-banner)
   so the state is visually distinct even at a glance, not merely present in the DOM. */
.rc-over-target-banner {
    background: var(--status-error-bg);
    color: var(--status-error);
    border: 1px solid var(--status-error);
    border-radius: 9px;
    padding: 10px 14px;
    font-size: 14px;
    font-weight: 600;
    margin-bottom: 12px;
}

.rc-turnlist { padding-left: 20px; margin: 6px 0; }
.rc-turnlist li { padding: 2px 0; line-height: 1.35; }
.rc-verdict--pass { color: var(--status-success); font-weight: 800; }
.rc-verdict--fail { color: var(--status-error); font-weight: 800; }
.rc-turn-num--left { color: #2563eb; }
.rc-turn-num--right { color: #16a34a; }
.rc-turn-num--roundabout { color: #ea580c; }

.rc-map { border: 1px solid var(--border); border-radius: var(--radius); }
.rc-map--lg { height: 560px; }
.rc-map--md { height: 420px; }

.rc-side-card { padding: 16px; }
.rc-side-card__title { font-size: 13px; margin: 0 0 6px; text-transform: uppercase; letter-spacing: .04em; color: var(--text-dim); }

/* TASK-WEB-SCH-001: found live while proving SCH-102 renders at 390px -- Pages/Admin/Schools/
   Details.cshtml's header is a non-wrapping flex row (title+badge, then the Deactivate/Reactivate
   form). A school name containing one long unbreakable token (no spaces -- e.g. an access-code-like
   suffix) hits the same min-content trap .rc-lesson-card__student/.rc-lesson-card__value already
   fix elsewhere: a flex item's default min-width:auto refuses to shrink below its content's
   min-content width, so the title pushed the badge and button off the right edge of the viewport.
   Same fix, same idiom: let the title group shrink (min-width:0) and let the long token wrap inside
   it (overflow-wrap:anywhere) instead of forcing the row wider. */
.rc-detail-title-group { min-width: 0; }
.rc-detail-title-group h1 { min-width: 0; overflow-wrap: anywhere; }

/* TASK-WEB-INS-001: same min-content trap as .rc-detail-title-group above, found live proving
   INS-101 at 390px -- a long unbroken value (a generated email address, no spaces to wrap on)
   inside an .rc-form-group/.rc-card pushed the card wider than the viewport. Reusable for any
   detail page rendering a read-only long token value (email, token, id). */
.rc-detail-value { overflow-wrap: anywhere; }

/* ---- Route Generator: three-variant chooser (TASK-WEB-GEN-001) -----------------------------
   Same auto-fit/minmax pattern as .rc-stat-grid/.rc-template-grid, which TASK-WEB-RESP-001 already
   confirmed reflows to fewer columns at narrow widths with no extra media query needed. */

.rc-chooser-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 14px; }

.rc-chooser-card {
    border: 1px solid var(--card-border, var(--brand-border));
    border-radius: var(--card-radius, var(--brand-radius));
    padding: 14px;
    background: var(--surface);
}

/* TASK-WEB-GEN-003: once a card has loaded data, the whole panel is a click target (parity with
   the "Use this route" button inside it) -- this affordance makes that discoverable without
   requiring the user to find the small button first. */
.rc-chooser-card--loaded {
    cursor: pointer;
    box-shadow: 0 1px 4px rgba(20, 30, 50, .08);
    transition: box-shadow .15s ease, border-color .15s ease;
}
.rc-chooser-card--loaded:hover { border-color: var(--brand-primary); box-shadow: 0 4px 12px rgba(20, 30, 50, .16); }

.rc-chooser-card--active { border-color: var(--brand-primary); box-shadow: 0 0 0 2px var(--brand-primary-weak, rgba(29, 78, 216, .18)); }
.rc-chooser-card--error { border-color: var(--status-error); }

.rc-chooser-card__head { display: flex; align-items: baseline; gap: 6px; margin-bottom: 2px; }
.rc-chooser-card__note { margin: 0 0 8px; }
.rc-chooser-card__body { display: flex; flex-direction: column; gap: 4px; }
.rc-chooser-card__stats { font-weight: 700; }

/* ---- System Health page (data-status / data-tier driven, no inline color styles) ---- */

.rcs-banner { padding: 16px 20px; border-radius: var(--radius); color: #fff; margin-bottom: 20px; }
.rcs-banner[data-status="healthy"] { background: var(--status-success); }
.rcs-banner[data-status="degraded"] { background: var(--status-warning); }
.rcs-banner[data-status="unhealthy"] { background: var(--status-error); }
.rcs-banner__title { font-size: 18px; font-weight: 800; }
.rcs-banner__meta { opacity: .85; font-size: 13px; }

.rcs-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 12px; }
.rcs-card { border: 1px solid var(--border); border-radius: var(--radius); padding: 12px 14px; background: var(--surface); }

.rcs-pill { display: inline-block; padding: 3px 10px; border-radius: 20px; color: #fff; font-size: 12px; font-weight: 700; }
.rcs-pill[data-status="healthy"] { background: var(--status-success); }
.rcs-pill[data-status="degraded"] { background: var(--status-warning); }
.rcs-pill[data-status="unhealthy"] { background: var(--status-error); }
.rcs-pill--action { background: var(--accent); border: none; cursor: pointer; padding: 8px 16px; font-size: 14px; font-family: inherit; }

.rcs-meta { color: var(--text-dim); font-size: 13px; margin-top: 6px; }
.rcs-meta div { margin: 1px 0; }
.rcs-meta__error { color: var(--status-error); }
/* W1-HEALTH: error div is always rendered (server AND JS auto-refresh both just set its text) so
   it can be updated in place without reload -- hide it entirely when there is no error text. */
.rcs-meta__error:empty { display: none; margin: 0; }
.rcs-section { margin-top: 28px; }

.rcs-conf { width: auto; }
.rcs-conf td { padding: 4px 12px 4px 0; font-size: 14px; border: none; }
.rcs-conf td.k { color: var(--text-dim); }

.cc-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 6px; }
.cc-tile { border-radius: 6px; padding: 6px 8px; color: #fff; font-size: 12px; position: relative; transition: background .15s, opacity .15s; }
.cc-tile[data-status="pass"] { background: var(--status-success); }
.cc-tile[data-status="fail"] { background: var(--status-error); }
.cc-tile[data-status="red"] { background: var(--status-neutral); }
.cc-tile[data-status="pending"] { background: #bdbdbd; opacity: .6; }
.cc-tile[data-running="true"] { outline: 2px solid var(--accent); opacity: 1; }
.cc-tile .id { font-weight: 700; }
.cc-tile .ttl { opacity: .9; font-size: 11px; line-height: 1.2; }
.cc-stripe { height: 4px; border-radius: 3px; margin-top: 5px; display: flex; gap: 2px; }
.cc-stripe__seg { flex: 1; border-radius: 3px; }
.cc-stripe__seg[data-tier="milestone"] { background: #6a1b9a; }
.cc-stripe__seg[data-tier="major"] { background: #1565c0; }
.cc-stripe__seg[data-tier="minor"] { background: #00838f; }
.cc-stripe__seg[data-tier="general"] { background: var(--status-neutral); }
.cc-badge { display: inline-block; background: rgba(0, 0, 0, .25); border-radius: 4px; padding: 0 4px; font-size: 10px; margin: 2px 2px 0 0; }
.cc-summary { display: flex; gap: 16px; flex-wrap: wrap; align-items: center; }
.cc-summary__pass { color: var(--status-success); font-weight: 600; }
.cc-summary__fail { color: var(--status-error); font-weight: 600; }
.cc-summary__red { color: var(--status-neutral); font-weight: 600; }
.cc-tier-key span[data-tier="milestone"] { color: #6a1b9a; }
.cc-tier-key span[data-tier="major"] { color: #1565c0; }
/* TASK-WEB-A11Y-001: darkened from #00838f (4.52:1 on white, but system-health's tier key sits on
   --bg #f4f6f9 where it measured a serious color-contrast failure) -- same teal hue, lower
   lightness only. #007b87 clears >=4.6:1 against both --bg and white. Fixed in place rather than
   tokenized: this is a fixed pedagogical tier color (milestone/major/minor/general), the same
   "not brand-themed" family as the map pin colors a few rules below -- not part of the
   school-brandable token surface, so a CSS custom property here would be new surface with no
   consumer (see DONE summary for the tokenize-vs-fix-in-place note). */
.cc-tier-key span[data-tier="minor"] { color: #007580; }

/* ---- Feedback widget (floating capture launcher + overlay, every page via _Layout) ---- */

.rc-feedback-launcher {
    position: fixed;
    right: 24px;
    bottom: 24px;
    /* Must sit above every other overlay in this file (Leaflet panes top out around z-index 1000;
       the highest local overlay is .rc-geo-suggest at 1000) so the launcher is never covered by a
       map or dropdown on any page. Operator feedback: the widget was "partially hidden by the map". */
    z-index: 10000;
    width: 52px;
    height: 52px;
    border-radius: 50%;
    background: var(--accent);
    color: var(--accent-contrast);
    border: none;
    box-shadow: 0 6px 18px rgba(20, 30, 50, .28);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
}
.rc-feedback-launcher:hover { filter: brightness(0.92); }
.rc-feedback-launcher__icon { font-family: 'JetBrains Mono', monospace; font-weight: 700; font-size: 20px; }

/* TASK-WEB-DEMO-004: reserve scroll clearance for the fixed launcher above at phone width, so the
   LAST content on ANY page (a lesson record's last skill row, a lesson list's last History row, a
   map's bottom-right corner) can always scroll clear of it. Launcher footprint: 52px tall + 24px
   bottom offset = 76px; +20px breathing room = 96px. TWO rules, two distinct scroll mechanisms:
   - html { scroll-padding-bottom } governs browser-NATIVE scrollIntoView -- keyboard Tab-focusing
     a control, a script's own el.scrollIntoView() (Playwright's actionability scroll included),
     `:target` anchor navigation -- the browser stops short of flush-against-the-edge alignment by
     this many px. Set on the scrolling element (html/document.scrollingElement is the actual
     scroll container here -- .rc-container itself does not scroll), so scoping it under the SAME
     max-width query as the .rc-container rule below (not a bare unscoped :root rule) keeps it from
     touching desktop, where the launcher sits at a harmless top-of-content offset.
   - .rc-shell .rc-container { padding-bottom } governs a plain manual drag-to-the-bottom gesture
     (mouse wheel, touch fling, scrollbar drag) -- none of those consult scroll-padding at all, so
     a page whose content ends right at (not before) the fold -- e.g. FAM-101's History table, the
     last thing on that page -- still needs REAL reserved whitespace, not just a scroll-into-view
     hint, to keep its last row clear once scrolled to the document's true end.
   ".rc-shell .rc-container" (matching the real DOM, _Layout.cshtml's <div class="rc-shell"><nav
   class="rc-sidebar">...<main class="rc-container">) is deliberately MORE SPECIFIC than the bare
   ".rc-container" the TASK-WEB-RESP-001 phone-width block sets further down this file (that block
   sets "padding: 0 16px", a shorthand that zeroes padding-bottom) -- placing this rule next to the
   launcher's own rules (per this ticket's file fence) means it is EARLIER in source order, so bare
   ".rc-container" would win the cascade and reset this back to 0 if not for the extra specificity.
   Same specificity-vs-source-order idiom already used elsewhere in this file (see the
   ":not(.rc-hidden)" comments on .rc-feedback-overlay/.rc-feedback-preview above). */
@media (max-width: 767.98px) {
    html {
        scroll-padding-bottom: 96px;
    }
    .rc-shell .rc-container {
        padding-bottom: 96px;
    }
}

.rc-feedback-overlay {
    position: fixed;
    inset: 0;
    /* Backdrop + dialog both live in this stacking context; one level above the launcher (also
       raised, above) so the modal always wins over Leaflet panes (~1000) and every other overlay
       in this file (.rc-geo-suggest at 1000, .rc-nav-dropdown-menu at 50). */
    z-index: 10001;
    background: rgba(14, 34, 56, .45);
    align-items: center;
    justify-content: center;
    padding: 24px;
}
/* :not(.rc-hidden) instead of a bare "display: flex" above -- .rc-hidden { display: none } sits
   earlier in this file at equal specificity, so a bare rule here would win by source order and the
   overlay would never actually hide. */
.rc-feedback-overlay:not(.rc-hidden) { display: flex; }

.rc-feedback-panel {
    background: var(--surface);
    border-radius: var(--radius);
    padding: 20px;
    width: 100%;
    max-width: 440px;
    max-height: 90vh;
    overflow-y: auto;
    box-shadow: 0 20px 60px rgba(0, 0, 0, .3);
}

.rc-feedback-panel__header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 12px;
}

.rc-feedback-preview {
    max-width: 100%;
    max-height: 160px;
    border: 1px solid var(--border);
    border-radius: 9px;
    margin-top: 4px;
}
/* Same rc-hidden specificity-vs-source-order issue as .rc-feedback-overlay above. */
.rc-feedback-preview:not(.rc-hidden) { display: block; }

/* ---- TASK-WEB-LES-013: LES-101 pickup main/alternate switch + its confirm banner ----
   Deliberately NEW, dedicated classes (not .rc-chip-row/.rc-flex): .rc-chip-row's own
   "display: flex" is defined LATER in this file than .rc-hidden, so combining the two on one
   element would recreate the exact LES-012 Round-1 bug (equal specificity, later-in-file wins,
   .rc-hidden never actually hides it). Same ":not(.rc-hidden)" idiom as .rc-feedback-overlay/
   .rc-feedback-preview above, applied here instead of relying on nobody ever pairing the two
   classes again. */
.rc-pickup-source-switch { display: none; gap: 6px; flex-wrap: wrap; align-items: center; }
.rc-pickup-source-switch:not(.rc-hidden) { display: flex; }
.rc-pickup-source-confirm { display: none; }
.rc-pickup-source-confirm:not(.rc-hidden) { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }

/* ---- What's New ---- */

.wn-release { border-left: 3px solid var(--accent); padding: 4px 0 4px 16px; margin: 18px 0; }
.wn-ver { font-size: 18px; font-weight: 700; }
.wn-date { color: var(--text-dim); font-size: 13px; margin-left: 8px; }
.wn-summary { color: var(--text); margin: 6px 0; }
.wn-release ul { margin: 6px 0 0 18px; }
.wn-release li { margin: 4px 0; color: var(--text-dim); font-size: 14px; }

/* ---- TICKET H: Route Generator time-limit chips + stepper (Docs/UI-OPERATION-CHECKLIST.md) ----
   Operator: "time picker needs to be more simple." Chips are one-tap presets; the stepper nudges
   by 5 minutes between presets. Both write into the SAME hidden #time input the existing
   Generate-route JS already reads (document.getElementById('time').value) -- this section adds
   presentation only, no behavior-contract change. Scope: this control only. */

.rc-time-limit { display: flex; flex-direction: column; gap: 8px; }

.rc-chip-row { display: flex; gap: 6px; flex-wrap: wrap; }

/* TASK-WEB-LES-004 (LES-101 "Start from" -- blank-lesson segment editor, G12): a checkbox paired
   inline with its own label text (highway/final-assessment toggles, per-segment skill checkboxes)
   -- distinct from .rc-form-group's block <label> above an <input>, which does not fit a checkbox. */
.rc-checkbox-label { display: inline-flex; align-items: center; gap: 6px; font-size: 14px; cursor: pointer; }
.rc-checkbox-label input[type="checkbox"] { margin: 0; }

.rc-chip {
    /* UX pass 2026-09-07: .rc-chip carried no `display`, so on a <span> (how
       lesson-start-from.js renders the per-segment skill pills) it was `inline`. An inline box
       with padding + border-radius FRAGMENTS when it wraps: the fragments paint across the line
       above and the pills visibly collide -- measured at 390px, "Left Turns" reported a 251x52
       box overlapping "Right Turns". inline-block keeps each pill one unbroken box. Harmless on
       the <button> chips (already inline-block) and on chips inside .rc-chip-row (flex items). */
    display: inline-block;
    vertical-align: middle;
    font-family: inherit;
    font-size: 13px;
    font-weight: 600;
    color: var(--chip-fg, var(--brand-text));
    background: var(--chip-bg, var(--brand-surface-2));
    border: 1px solid var(--chip-border, var(--brand-border));
    border-radius: 20px;
    padding: 5px 12px;
    cursor: pointer;
}

.rc-chip:hover, .rc-chip.rc-state-hover { background: var(--border); }

.rc-chip--active {
    background: var(--chip-active-bg, var(--brand-primary));
    border-color: var(--chip-active-bg, var(--brand-primary));
    color: var(--chip-active-fg, var(--brand-primary-contrast));
}

/* ---- TASK-WEB-CMP-001 REWORK-4 (Architect ruling S2, Entry 148): catalog-load in-flight busy
   state ---- A chip disabled while a catalog reload is in flight must LOOK disabled -- .rc-chip's
   own background/color rules otherwise leave it visually identical to a clickable chip, unlike
   .rc-btn which already has this treatment (see the UI-OPERATION-CHECKLIST item 14 rule below). */
.rc-chip:disabled,
.rc-chip[disabled] {
    opacity: 0.6;
    cursor: not-allowed;
}

.rc-stepper {
    display: inline-flex;
    align-items: center;
    gap: 8px;
}

.rc-stepper__btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    border-radius: 8px;
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--text);
    font-family: inherit;
    font-size: 13px;
    font-weight: 700;
    cursor: pointer;
}

.rc-stepper__btn:hover, .rc-stepper__btn.rc-state-hover { background: var(--surface-2); }

.rc-stepper__value {
    min-width: 34px;
    text-align: center;
    font-size: 14px;
    font-weight: 700;
    color: var(--text);
}

/* ---- TICKET I: UI-OPERATION-CHECKLIST item 14 (loading/busy state) ----
   A button disabled while a request is in flight (roadcoach.js double-submit guard, per-page
   fetch handlers) must LOOK disabled -- .rc-btn's own background/color rules otherwise leave a
   disabled primary/secondary/destructive button visually identical to an enabled one. */
.rc-btn:disabled,
.rc-btn[disabled],
.rc-btn.rc-state-disabled {
    opacity: 0.6;
    cursor: not-allowed;
    filter: none;
}

/* ---- W3-PAGED: reusable server-pagination pager (Pages/Shared/_Pager.cshtml) ----
   Flex + flex-wrap so it reflows (never overlaps) at narrow widths -- stays in normal document
   flow (no position:fixed), unlike the floating feedback launcher (TD-021), so this component
   cannot add a second instance of that overlap class. */
.rc-pager {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 12px;
    margin-top: 16px;
}
.rc-pager__status { font-size: 13px; white-space: nowrap; }

/* ---- TICKET M: skill-mapping tier sections grouped by category (Pages/Skills/Mapping.cshtml) ----
   Reuses .rc-skill-col / .rc-skill-col__head (defined above for the Route Generator side-options
   picker) so the "group cards by category" treatment is the SAME everywhere (one component per
   concept). On the mapping page the columns are NOT placed inside a flex row -- unlike the picker's
   side-options, each card here is a <details> that expands into a full editing form, so a column
   renders as a full-width, top-to-bottom stack instead of a narrow side-by-side column. Spacing
   between stacked columns within one tier section: */
.rc-skill-cat-group .rc-skill-col + .rc-skill-col { margin-top: 14px; }

/* ---- THEME DESIGNER Phase C2: /system/templates token editor layout (Pages/RcSystem/Templates.cshtml) ----
   Purely structural -- every color/size VALUE these rows show still comes from the token itself
   (var(--token, ...)), never a hardcoded hex here. Grouped-by-family grid so 57+ tokens stay
   scannable instead of one long list. */
.rc-theme-editor-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 18px; }

.rc-theme-family__title { text-transform: capitalize; font-size: 14px; margin-bottom: 8px; }

.rc-theme-row {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 8px 0;
    border-bottom: 1px solid var(--border);
}
.rc-theme-row:last-child { border-bottom: none; }

.rc-theme-row__meta { display: flex; flex-direction: column; gap: 1px; }
.rc-theme-row__desc { font-size: 13px; }

.rc-theme-row__control { display: flex; align-items: center; gap: 6px; }

.rc-theme-color { width: 32px; height: 32px; padding: 0; border: 1px solid var(--border); border-radius: 6px; cursor: pointer; }
.rc-theme-hex { width: 90px; }

.rc-theme-stepper { margin-right: 4px; }

.rc-theme-row__error { margin-top: 0; }

/* ---- SUP-008: visual click-to-edit theme editor (TPL-100, SUPPORTABILITY-PLAN.md sec 6) ----
   Purely structural/affordance styles; every color still reads a token (never a hardcoded brand
   hex). Three pieces: (1) the All-tokens/Visual-editor tabs, (2) edit-mode hover outline + name
   badge on catalog exemplars (outline + a pointer-events:none ::after badge, so nothing here can
   ever intercept a click), (3) the fixed right-hand inspector, which is the SAME
   .rc-theme-editor-grid the flat editor renders (same DOM, filtered by JS -- the parity rule),
   restyled as a panel when .rc-inspector-mode is set. */

.rc-theme-tabs { display: flex; gap: 8px; margin-bottom: 12px; }

.rc-theme-tab {
    font-family: inherit;
    font-size: 13px;
    font-weight: 600;
    padding: 8px 14px;
    border-radius: 9px;
    border: 1px solid var(--border);
    background: var(--surface-2);
    color: var(--text-dim);
    cursor: pointer;
}

.rc-theme-tab--active {
    background: var(--surface);
    color: var(--text);
    border-color: var(--accent);
    box-shadow: inset 0 -2px 0 var(--accent);
}

.rc-theme-row__layer { margin-left: 6px; }
.rc-theme-row__modified-badge { margin-left: 6px; vertical-align: middle; }

/* Edit mode: catalog exemplars become selection targets. Children go pointer-events:none (the
   property inherits, so the whole sample subtree is inert -- a select cannot even open its native
   dropdown on mousedown, which a click-capture guard alone cannot stop) and the wrapper takes the
   hover/focus outline + floating name badge. */
body.rc-edit-mode .rc-exemplar { position: relative; cursor: pointer; border-radius: 6px; }
body.rc-edit-mode .rc-exemplar > * { pointer-events: none; }

body.rc-edit-mode .rc-exemplar:hover,
body.rc-edit-mode .rc-exemplar:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 4px;
}

body.rc-edit-mode .rc-exemplar--selected,
body.rc-edit-mode .rc-exemplar--selected:hover {
    outline: 3px solid var(--accent);
    outline-offset: 4px;
}

body.rc-edit-mode .rc-exemplar:hover::after,
body.rc-edit-mode .rc-exemplar:focus-visible::after,
body.rc-edit-mode .rc-exemplar--selected::after {
    content: attr(data-name);
    position: absolute;
    top: -12px;
    left: 10px;
    z-index: 55;
    background: var(--accent);
    color: var(--accent-contrast);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .04em;
    padding: 2px 10px;
    border-radius: 12px;
    pointer-events: none;
}

/* In edit mode the flat grid hides until a selection opens it as the inspector panel. */
body.rc-edit-mode .rc-theme-editor-grid:not(.rc-inspector-mode) { display: none; }

/* The inspector IS the flat editor grid, fixed to the right edge. z-index 70: above the template
   applybar (60), below the feedback overlay stack (10000+). */
.rc-theme-editor-grid.rc-inspector-mode {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    z-index: 70;
    width: 380px;
    max-width: 92vw;
    display: flex;
    flex-direction: column;
    gap: 14px;
    background: var(--surface);
    border-left: 1px solid var(--border);
    box-shadow: -10px 0 28px rgba(14, 34, 56, 0.14);
    padding: 0 18px 24px;
    overflow-y: auto;
}

/* Same specificity-vs-source-order gotcha as .rc-feedback-overlay: .rc-hidden { display: none }
   is declared EARLIER in this file, so a bare display: flex here would defeat it.
   STICKY: the chrome (title/Undo/Close) must stay pinned while the operator scrolls a long token
   list -- and a scrolled-away Undo button is also exactly what made the Playwright click flake
   (the panel auto-scroll raced the click's hit test). */
.rc-inspector-chrome:not(.rc-hidden) {
    display: flex;
    align-items: center;
    gap: 8px;
    position: sticky;
    top: 0;
    z-index: 2;
    background: var(--surface);
    padding-top: 16px;
    padding-bottom: 10px;
    border-bottom: 1px solid var(--border);
}

.rc-inspector-chrome strong { flex: 1; font-size: 14px; }

/* Catalog exemplar for the sidebar family (SUP-008 rework: every token family must be reachable
   via click-to-edit). Purely structural -- the items inside reuse the REAL .rc-sidebar__link
   rules from sidebar.css, so their colors come from the same tokens as the live sidebar. */
.rc-sidebar-exemplar {
    max-width: 260px;
    display: flex;
    flex-direction: column;
}

.rc-theme-violation-list { margin: 6px 0 0; padding-left: 20px; }

/* SUP-006: Log Hub tab nav -- plain links styled as a tab strip (no JS tab framework; each tab is
   its own full page load carrying the filter query string, matching the "filters live in the URL"
   design). */
.rc-loghub-tabs { display: flex; gap: 4px; border-bottom: 2px solid var(--border); }
.rc-loghub-tab {
    display: inline-block;
    padding: 8px 16px;
    font-weight: 600;
    color: var(--text-dim);
    text-decoration: none;
    border-bottom: 2px solid transparent;
    margin-bottom: -2px;
}
.rc-loghub-tab:hover { color: var(--text); }
.rc-loghub-tab--active { color: var(--brand-primary, var(--accent)); border-bottom-color: var(--brand-primary, var(--accent)); }

.rc-loghub-filters .rc-filter-label { display: flex; flex-direction: column; gap: 2px; font-size: 12px; font-weight: 600; color: var(--text-dim); }
.rc-loghub-drill-link { font-size: 12px; }

/* ---- TASK-WEB-RESP-001: app-wide responsive layer (phone widths, ~<768px) ----
   Additive only -- every rule above this block is the pre-existing desktop stylesheet, untouched,
   so >=768px renders exactly as before this ticket. The sidebar's own off-canvas drawer rules live
   in sidebar.css (next to the sidebar rules they override); this block covers the SHARED container
   + the fixed-column utility grids that would otherwise squeeze into unreadably narrow columns (a
   260px sidebar-width column, or two 1fr columns, both fit fine at a 1100px desktop container but
   not inside a ~340px phone content area). */
@media (max-width: 767.98px) {
    .rc-container {
        max-width: 100%;
        margin: 16px auto;
        padding: 0 16px;
    }

    /* Fixed-ratio/fixed-px two-column grids collapse to one column -- at phone width there is not
       enough room for a 260px (or even a 320px) side column next to anything else. rc-stat-grid /
       rc-template-grid are left alone: their auto-fit + minmax already reflow to fewer columns
       without overflowing (minmax(160px,1fr) etc. never demands more than the available track). */
    .rc-grid-2,
    .rc-grid-2--narrow-right,
    .rc-grid-2--sidebar,
    .rc-check-grid,
    .rc-theme-editor-grid {
        grid-template-columns: 1fr;
    }

    /* REWORK (Tester finding 1, 2026-07-22): a grid ITEM defaults to min-width:auto, which refuses
       to shrink below its own content's min-content width -- a plain <div> grid item (no overflow
       of its own) bubbles up the min-content width of ANY descendant, including a `.rc-table-wrap`-
       wrapped table several levels down, regardless of that wrapper's own overflow-x:auto. Proven
       live: the Dashboard's "Recent feedback" grid item forced the page to scrollWidth=758 at a
       390px viewport once a real feedback row carried a long unbreakable ActiveScreen value (a
       table's min-content is its narrowest UNBREAKABLE token per column, not each cell's wrapped
       height -- so ordinary multi-word content wraps and stays narrow, but one long token does not).
       min-width:0 on the grid item overrides that default, letting it shrink to its track's actual
       width -- the descendant `.rc-table-wrap` then does its OWN job of scrolling internally. Scoped
       to `> *` (immediate children only) so it reaches every current/future consumer of these grid
       classes without requiring each call site to remember a utility class. */
    .rc-grid-2 > *,
    .rc-grid-2--narrow-right > *,
    .rc-grid-2--sidebar > *,
    .rc-check-grid > * {
        min-width: 0;
    }

    /* The theme editor's fixed right-hand inspector panel (desktop: 380px docked to the edge)
       would itself force page-width overflow at phone width -- make it a full-width panel instead
       of a narrow sliver pinned off the right edge. */
    .rc-theme-editor-grid.rc-inspector-mode {
        width: 100%;
        max-width: 100%;
    }

    /* REWORK (Tester finding 2, 2026-07-22): the shared skill picker's per-skill row
       (`_SkillPicker.cshtml`, `.rc-flex rc-items-center rc-gap-2 rc-skill-row`) is a nowrap flex row
       by default (`.rc-flex` sets no wrap) -- fine at desktop, where `.rc-card` is wide, but this
       ticket's shell reflow narrows `.rc-card` at phone width for the first time, and the row's
       fixed-width min/max number inputs (`.rc-input--xs`) plus the label's own 168px min-width no
       longer fit on one line, pushing the min/max inputs' right edge past the card (previously
       masked because the WHOLE PAGE scrolled instead of the card alone). Nothing here changes
       desktop rendering: flex-wrap only has an effect once a line's content actually exceeds the
       row's width (never true at >=768px), and .rc-skill-row__break only has a non-zero
       flex-basis inside this same media query. */
    .rc-skill-row {
        flex-wrap: wrap;
    }

    /* First attempt (flex-wrap alone) left a 3px residual overflow on the min-input -- the
       browser's own line-packing decision (based on each skill NAME's rendered width, which
       varies per skill) is not deterministic enough to guarantee the numeric cluster always lands
       on a fresh line. `.rc-skill-row__break` (an always-empty span the partial renders right
       before the min-input) is the standard flex "line break" technique: forcing IT to claim a
       full line's width deterministically pushes every item after it (min/dash/max/count) onto a
       new line together, regardless of how wide the checkbox+label content happens to be. */
    .rc-skill-row__break {
        flex-basis: 100%;
    }
}

/* ---- TASK-WEB-DEMO-023: phone tap-target floor for shared controls (TD-274, TD-276..279) -----
   TouchTargetAssertions (Ui/TouchTargetAssertions.cs) measured every shared control class a CSS
   px or two short of the 44px floor at phone width: .rc-btn is 40px tall (rule above, line ~446),
   .rc-input/.rc-select/.rc-textarea are 42px tall (rule above, line ~714). ONE phone-only rule set
   here (R13) -- every page that already uses these shared classes (Feedback/Lessons/Students/
   Templates filters, submit/action buttons, per-row assignment selects) gets the fix for free, no
   per-page patches. Desktop (>=768px, the rules above this block) is untouched -- proven by
   Desktop_ControlHeights_UnchangedFromToday in Ui/PhoneControlTapTargetPlaywrightTests.cs. */
@media (max-width: 767.98px) {
    .rc-btn {
        min-height: 44px;
    }

    .rc-input, .rc-select, .rc-textarea {
        min-height: 44px;
    }

    /* TD-274: the dashboard's "View all ->" header links (Pages/Admin/Dashboard/Index.cshtml)
       render as plain, class-less <a> tags today (76.6x24px, sized by their own text line-height)
       -- min-height alone cannot grow an inline text anchor's tap box, so it needs display:flex to
       take effect. .rc-view-all-link is the shared class added to all three instances of that link
       (R13 -- one class, not three copies of the same override). */
    .rc-view-all-link {
        display: inline-flex;
        align-items: center;
        min-height: 44px;
    }

    /* REWORK (Orchestrator finding, 2026-09-14): TD-279 was closed on the strength of a
       touch-target test that happened to run against an EMPTY templates table (no row, nothing to
       measure, so it could not have caught this) -- with real rows, each list page's own per-row
       link (Templates: `<td><a href="/templates/@row.Id">@row.Name</a></td>`) is a plain, class-
       less anchor sized by text line-height alone (measured 36.3px tall with real seeded rows), same
       shape as TD-274's dashboard "View all" links above. `.rc-table tbody td a` (not `.rc-table`
       generally -- header-row and non-anchor cells are untouched) covers every current and future
       list page's row link with one rule, matching this ticket's own "shared control classes, not
       per-page patches" preference. Scoped to `tbody` so it cannot touch a `<thead>` link (none
       exist today, but the selector should not silently start matching one later). Dashboard's own
       clickable rows use `data-rc-row-href` on the `<tr>` with no `<a>` inside the cell, so they are
       untouched by this selector; Lessons' `<table class="rc-table">` is itself `display:none` at
       this breakpoint (rule above, TASK-WEB-LES-UX-LIST) and replaced by `.rc-lesson-card__link`
       cards, which carry their own already-proven 44px sizing -- also untouched. */
    .rc-table tbody td a {
        display: inline-flex;
        align-items: center;
        min-height: 44px;
    }
}

/* ---- LES-103 lesson record (TASK-WEB-LES-005) -----------------------------------------------
   Scoped to this screen's own classes -- no edits to the shared .rc-btn/.rc-select/.rc-input rules
   above, which every other screen also uses. "Responsive is an acceptance criterion, not a
   nicety" (ticket Technical Notes): an instructor must be able to grade in a car at 390px with
   touch-sized targets, so this screen's own primary controls get a taller minimum than the
   sitewide 40px .rc-btn default. Additive only. */
.rc-lesson-record__touch-btn {
    min-height: 44px;
}

/* In-car usability pass (RESUME 2026-09-07): the header used to be a 4-column grid (Student /
   Instructor / Date / Duration) with its own label above every value -- alone it pushed the first
   grade button most of a screen-height down. Replaced with one compact block: the student's name
   as the page heading, everything else folded into a single muted meta line. */
.rc-lesson-record__header-compact {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.rc-lesson-record__header-student {
    margin: 0;
    font-size: 1.1rem;
}

.rc-lesson-record__header-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
}

/* No-route state: a whole empty card (heading + map placeholder) is wasted vertical space on a
   screen already 3+ screens tall -- one slim line instead of a card. */
.rc-lesson-record__route-slim {
    margin: 0;
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 8px;
}

.rc-lesson-record__inline-link {
    font-size: 0.75rem;
}

/* Grade key (legend): compact and collapsed by default via native <details> -- an instructor
   glances at the always-visible <summary> line; the full 1-5 breakdown is one tap away, never a
   per-row repetition. */
.rc-lesson-record__grade-legend {
    margin-bottom: 8px;
}

.rc-lesson-record__grade-legend-summary {
    cursor: pointer;
    font-size: 0.75rem;
    color: var(--text-dim);
}

.rc-lesson-record__grade-legend-list {
    margin: 6px 0 0;
    padding-left: 1.2em;
    font-size: 0.8rem;
}

.rc-lesson-record__progress {
    margin-top: 0;
    margin-bottom: 8px;
}

/* TASK-WEB-MOB-001: the offline write-queue indicator -- .rc-badge already provides the
   inline-flex/padding/color box (three states via the shared --warning/--error variants,
   checklist item 11); this just gives it its own line with breathing room, standalone from the
   progress text and the legend above it. Deliberately NO `display` here -- .rc-hidden (line 801)
   and .rc-badge (line 478) are the same specificity as this rule, and .rc-hidden must win by
   SOURCE ORDER when it is present; redeclaring `display` here at this later position would
   silently outrank it (measured -- the first cut of this rule did exactly that). */
.rc-lesson-record__queue-indicator {
    margin: 0 0 8px;
}

.rc-lesson-record__segment-progress {
    font-weight: normal;
    font-size: 0.75rem;
    color: var(--text-dim);
}

.rc-lesson-record__segment-desc {
    margin: 4px 0 8px;
}

.rc-lesson-record__segment-desc-summary {
    cursor: pointer;
    font-size: 0.75rem;
    color: var(--text-dim);
}

.rc-lesson-record__segment-desc p {
    margin: 6px 0 0;
}

.rc-lesson-record__skill-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 10px 0;
    border-top: 1px solid var(--border);
}

.rc-lesson-record__skill-row:first-of-type { border-top: none; }

/* Progress at a glance: an ungraded row carries a left accent so the instructor can see what is
   left without reading every label -- the SAME data that drives the "X of Y graded" text. */
.rc-lesson-record__skill-row--ungraded {
    border-left: 3px solid var(--status-warning);
    padding-left: 8px;
}

.rc-lesson-record__skill-label { flex: 1 1 auto; }

/* A1 (RESUME amendment): one-tap 1-5 grade buttons replace the native <select> -- every grade
   button is a real 44x44 CSS-px tappable element (not min-height on a shrinkable container), fixed
   size so it can never be squeezed by a long skill name at any viewport. .rc-btn/.rc-btn--primary/
   .rc-btn--secondary/.rc-btn--ghost are the sitewide button component (site.css above); this block
   only pins size/shape, appended after those rules so it wins on equal specificity (the SAME
   recipe .rc-lesson-record__touch-btn already uses against the sitewide 40px .rc-btn default).

   RESUME 2026-09-07: Clear moved to its own row (.rc-lesson-record__grade-row wraps both the
   radiogroup and Clear) instead of sharing the radiogroup's own flex-wrap row -- at phone width
   the old layout could wrap Clear onto a new line starting at the LEFT edge, landing directly
   under grade button "1", the single most likely mis-tap over a bump. align-self pins it to the
   right of its own row instead, where it can never sit under button 1. */
.rc-lesson-record__grade-row {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 4px;
}

.rc-lesson-record__grade-group {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
}

.rc-lesson-record__grade-btn {
    box-sizing: border-box;
    width: 44px;
    height: 44px;
    min-width: 44px;
    min-height: 44px;
    padding: 0;
}

/* VERIFY finding (2026-09-02): rendered with .rc-btn--ghost alone, "Clear" was plain muted
   text with no border/fill of its own -- at a glance in a moving car it read as a status word,
   not a tappable control (the one thing this row's whole A1 redesign exists to make obvious).
   The underline is the cheapest unambiguous "this is a link/action" signal that still keeps it
   visually smaller/lighter than the five numbered squares, per the amendment's own "small
   affordance" wording. */
.rc-lesson-record__grade-clear {
    box-sizing: border-box;
    min-width: 44px;
    min-height: 32px;
    padding: 0 10px;
    font-size: 12px;
    text-decoration: underline;
    align-self: flex-end;
}

.rc-lesson-record__checklist {
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin-top: 10px;
}

.rc-lesson-record__checklist-item {
    display: flex;
    align-items: center;
    gap: 10px;
    min-height: 44px;
}

.rc-lesson-record__checklist-item input[type="checkbox"] {
    width: 22px;
    height: 22px;
    flex: 0 0 auto;
}

/* TASK-WEB-DEMO-019 (SOW 71): the incident checkbox reuses .rc-lesson-record__checklist-item's row
   (44px-tall label, gap, flex text) for visual consistency, but NOT its 22x22 checkbox sizing --
   that 22x22 box is a documented PRE-EXISTING excluded touch-target violator (TD-358,
   LessonWriteQueuePlaywrightTests.cs's PreExistingLes103Violators), not something to copy onto a
   brand-new control. TouchTargetAssertions measures an <input>'s OWN bounding box (never an
   ancestor's), so the checkbox itself -- not just its wrapping label -- must be >=44x44. This ID
   selector overrides the shared class rule above for this one control only. */
#incident-flag-checkbox {
    width: 44px;
    height: 44px;
    flex: 0 0 auto;
}

/* VERIFY finding (2026-09-07): this label used to be THREE flex items (checkbox, a bare text
   node, span.rc-muted) -- the label text and the note wrapped independently at phone width and
   produced interleaved garbage ("1.2 Preparing to / Drive" beside "pre-drive / sequence"). Fixing
   it means the label text and the note are ONE flex item (this class), so the row is back to
   exactly two items (checkbox, text) and wraps as a single clean block. */
.rc-lesson-record__checklist-text {
    flex: 1 1 auto;
    min-width: 0;
}

.rc-lesson-record__radio-label {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    min-height: 44px;
}

.rc-lesson-record__note {
    padding: 8px 0;
    border-top: 1px solid var(--border);
}

.rc-lesson-record__note:first-of-type { border-top: none; }

/* TASK-WEB-LES-011 (LES-103): in-lesson segment run-state controls (Up/Down/Skip/Unskip/Done).
   flex-wrap so the button row wraps at narrow widths instead of forcing horizontal scroll (AC:
   "No horizontal overflow at 390px") -- same touch-target floor as every other primary control
   on this screen (.rc-lesson-record__touch-btn). */
.rc-lesson-record__segment-controls {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin: 8px 0;
}

/* Skipped segment: struck-through name, badge alongside it (rendered by
   lesson-detail-segments.js's segmentStatusBadgeHtml) -- grades/checklist rows below are
   untouched (Skip never hides them, CUR-013 Design decision 3: the grade lives on LessonSkill,
   never on the segment). */
.rc-lesson-record__segment-name--skipped {
    text-decoration: line-through;
    color: var(--text-dim);
}

@media (max-width: 767.98px) {
    /* Same breakpoint the rest of the stylesheet already uses (site.css's own narrow-width
       convention). The grade button row (fixed 44x44 buttons, flex-wrap already set above) needs
       no narrow-width override of its own. */
    .rc-lesson-record__skill-row {
        flex-direction: column;
        align-items: stretch;
        gap: 6px;
    }
}


/* ---- LES-101 create-lesson stepper (wwwroot/js/lesson-create-stepper.js) -------------------
   Progressive disclosure over the EXISTING form: the JS groups the form's own children into
   panes and toggles .rc-step--active. Inactive panes are display:none, which keeps every input
   in the DOM (so the POST is unchanged) -- the JS reveals them all again before the classic
   form validates, because a hidden `required` control is not focusable and blocks submission. */
/* Phone: one step at a time. Desktop (>=768px): the familiar full-length page, with the step
   chrome removed entirely -- a wizard earns its keep on a 390px screen and gets in the way on a
   1280px one. Doing this in a media query rather than in JS means a resize is handled for free,
   and it keeps ONE code path in the steppers. */
@media (max-width: 767px) {
    .rc-step { display: none; }
    .rc-step--active { display: block; }
    /* The step header and the card heading said the same word twice, costing ~30px. Drop MY
       duplicate, not the card's <h2>: hiding the h2 stripped the page of every visible heading
       at phone width -- bad for screen readers, and it broke specs that wait for one. The
       "Step 1 of 4" counter stays; the card's own heading names the section. */
    #rc-edit-steps > .rc-step-head > .rc-step-title { display: none; }
    /* School/Instructor when the actor cannot change them -- replaced by .rc-locked-context. */
    .rc-step-hidden-on-phone { display: none; }
    .rc-locked-context { margin: 0 0 10px; }
    /* The step title carries the meaning; the one-line hint under it is a nicety worth ~20px of
       a 852px screen, and the section's own explanatory paragraph repeats what the title says. */
    #rc-edit-steps > .rc-step-head > .rc-step-hint { display: none; }
    /* Advisory copy that costs more screen than it earns on a phone. Marked explicitly in the
       markup (.rc-phone-hide) rather than matched structurally, so moving the element does not
       silently un-hide it. */
    .rc-phone-hide { display: none; }
}
@media (min-width: 768px) {
    /* Desktop keeps the real controls and the card heading; the compact line is phone-only. */
    .rc-locked-context { display: none; }
}
@media (min-width: 768px) {
    .rc-step { display: block; }
    #rc-create-steps > .rc-step-head, #rc-create-steps > .rc-step-nav,
    #rc-edit-steps > .rc-step-head, #rc-edit-steps > .rc-step-nav { display: none; }
    .rc-step-fold > summary { display: none; }
}
.rc-step-head { margin-bottom: 12px; }
.rc-step-count { font-size: 12px; letter-spacing: .04em; text-transform: uppercase; color: var(--muted); margin: 0; }
.rc-step-title { font-size: 19px; margin: 2px 0 2px; }
.rc-step-hint { margin: 0; }
.rc-step-nav { display: flex; gap: 10px; margin-top: 16px; }
.rc-step-nav .rc-btn { flex: 1 1 auto; min-height: 48px; }
.rc-step-nav .rc-btn[hidden] { display: none; }

/* The tall skills picker on /lessons/edit, folded so step 1 fits a phone screen. Safe only
   because no `required` control lives inside it -- a required field in a closed <details> is
   unfocusable and would make form.requestSubmit() refuse silently. */
.rc-step-fold > summary { cursor: pointer; padding: 10px 0; font-weight: 600; min-height: 44px; }
.rc-step-fold[open] > summary { margin-bottom: 4px; }
