/* Self-hosted Inter (M-4, Codex independent review): previously loaded from
   fonts.googleapis.com/fonts.gstatic.com, which has no SRI-pinnable hash
   (Google serves per-request/per-UA CSS, so there is no single fixed file to
   hash) and so could never satisfy "every external stylesheet has SRI".
   Vendored the exact variable-font file Google's own CSS2 API serves modern
   (variable-font-capable) browsers for weights 300-700 latin -
   https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1ZL7.woff2,
   fetched 2026-09-26 with a Chrome User-Agent to get the woff2 (not ttf)
   variant - so the font now loads from this origin, with no external
   request and nothing left needing SRI. Only the latin subset is vendored
   (this is an English-only internal tool); Google's own CSS served the
   identical file for every one of the five requested weights, confirming it
   is a single variable font, not five static instances - one `font-weight:
   300 700` range covers all of them. */
@font-face {
    font-family: 'Inter';
    font-style: normal;
    font-weight: 300 700;
    font-display: swap;
    src: url('../fonts/inter-var-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308,
        U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ===========================================================================
   Unit S — GitHub Primer-derived monochrome token set, light + dark.
   See docs/design/UI-BRIEF.md ("Token table") for the source and the
   verified contrast numbers. These are OUR tokens (--tsf-*); the block below
   this one maps them onto Bootstrap 5.3's own CSS custom properties so every
   Bootstrap component retheme without forking a single Bootstrap rule.
   =========================================================================== */
:root {
    /* neutral ramp — light (Primer light theme, functional roles) */
    --tsf-canvas: #ffffff;          /* bgColor-default */
    --tsf-surface: #f6f8fa;         /* bgColor-muted / control-bgColor-rest */
    --tsf-surface-hover: #eff2f5;   /* control-bgColor-hover */
    --tsf-surface-active: #e6eaef;  /* control-bgColor-active */
    --tsf-border-subtle: #d1d9e0b3; /* borderColor-muted (alpha, not a new hex) */
    --tsf-border: #d1d9e0;          /* borderColor-default — dividers only, ~1.4:1, non-UI */
    --tsf-border-strong: #818b98;   /* borderColor-emphasis — interactive control borders, 3.2:1 on surface */
    --tsf-text-tertiary: #6f7883;   /* interpolated between border-strong and text-secondary */
    --tsf-text-secondary: #59636e;  /* fgColor-muted, 6.1:1 on canvas */
    --tsf-text: #1f2328;            /* fgColor-default, 15.8:1 on canvas */
    --tsf-inverse-surface: #25292e; /* bgColor-emphasis — inverse chips/tooltips */
    --tsf-accent: #0969da;          /* accent-fg — links, text, borders, focus rings */
    --tsf-accent-rgb: 9, 105, 218;
    --tsf-accent-on: #ffffff;       /* fgColor-onEmphasis, 5.2:1 on accent */
    /* accent-emphasis: the colour a SOLID fill (button bg, .text-bg-primary
       badge bg, skip-link bg) paired with white text uses — kept equal to
       --tsf-accent in light (already 5.2:1 with white) but redeclared darker
       in dark mode below, where the text-legible --tsf-accent itself only
       reaches 3.10:1 with white (Codex review, HIGH, 2026-09-26): a colour
       tuned to read as text/links against a near-black canvas is not
       automatically safe as a fill BEHIND white text, and Primer's own
       token set makes exactly this distinction (accent.fg vs
       accent.emphasis) for the same reason. */
    --tsf-accent-emphasis: var(--tsf-accent);
    --tsf-success-fg: #1a7f37; --tsf-success-bg: #dafbe1; --tsf-success-border: #4ac26b66;
    --tsf-danger-fg: #d1242f;  --tsf-danger-bg: #ffebe9;  --tsf-danger-border: #ff818266;
    /* Same split as accent-emphasis above, for .btn-outline-danger's hover/
       active solid fill (white text on danger-fg was 3.35:1 in dark mode -
       Codex review, HIGH). */
    --tsf-danger-emphasis: var(--tsf-danger-fg);
    --tsf-warning-fg: #9a6700; --tsf-warning-bg: #fff8c5; --tsf-warning-border: #d4a72c66;

    /* system-ui first, Inter as an available fallback rather than the
       critical-path font (brief token table note) — Inter is self-hosted
       (see @font-face above) but there is no reason to block first paint on
       it when the OS system font is already a fine match for this register. */
    --tsf-font: system-ui, -apple-system, "Segoe UI", Inter, sans-serif;
    --tsf-text-xs: .6875rem; --tsf-text-sm: .75rem; --tsf-text-base: .875rem; /* 11/12/14px */
    --tsf-text-md: 1rem; --tsf-text-lg: 1.125rem; --tsf-text-xl: 1.5rem;      /* 16/18/24px */
    --tsf-space-1: .25rem; --tsf-space-2: .5rem; --tsf-space-3: .75rem;      /* 4/8/12px */
    --tsf-space-4: 1rem; --tsf-space-6: 1.5rem; --tsf-space-8: 2rem;          /* 16/24/32px */
    --tsf-radius-sm: .375rem; --tsf-radius-md: .5rem; --tsf-radius-lg: .75rem; /* 6/8/12px */
    --tsf-control-h: 2rem;       /* 32px compact */
    --tsf-control-h-sm: 1.75rem; /* 28px — dense table row actions */
    --tsf-shadow: none;          /* flat panels — border does the separation, not shadow */
    /* The one allowed shadow (per UI-BRIEF "Component rules" / "Modals": cards
       and modals get border, not shadow — everything else that floats with
       no backdrop of its own — a dropdown menu, the onboarding tour tooltip —
       still needs ONE elevation cue, since a border alone reads as "part of
       the page" rather than "above it". Reused verbatim everywhere that
       applies instead of inventing a shadow per component. */
    --tsf-overlay-shadow: 0 4px 12px rgba(0, 0, 0, .15), 0 0 0 1px var(--tsf-border-subtle);
}

[data-bs-theme="dark"] {
    --tsf-canvas: #0d1117; --tsf-surface: #151b23;
    --tsf-surface-hover: #262c36; --tsf-surface-active: #2a313c;
    --tsf-border-subtle: #3d444db3; --tsf-border: #3d444d; --tsf-border-strong: #656c76;
    --tsf-text-tertiary: #7c828b; --tsf-text-secondary: #9198a1; --tsf-text: #f0f6fc;
    --tsf-inverse-surface: #3d444d;
    --tsf-accent: #4493f8; --tsf-accent-rgb: 68, 147, 248; --tsf-accent-on: #ffffff;
    /* Primer dark's accent.emphasis (#1f6feb) - white text on it is 4.63:1,
       vs 3.10:1 on --tsf-accent above. Verified via the WCAG relative-
       luminance formula, not eyeballed; tests/test_theme.py pins it. */
    --tsf-accent-emphasis: #1f6feb;
    --tsf-success-fg: #3fb950; --tsf-success-bg: #2ea0432b; --tsf-success-border: #2ea04366;
    --tsf-danger-fg: #f85149;  --tsf-danger-bg: #f851491f;  --tsf-danger-border: #f8514966;
    /* Primer dark's danger.emphasis (#da3633) - white text on it is 4.61:1,
       vs 3.35:1 on --tsf-danger-fg above (the `.btn-outline-danger:hover`
       fail Codex review flagged). */
    --tsf-danger-emphasis: #da3633;
    --tsf-warning-fg: #d29922; --tsf-warning-bg: #bb800930; --tsf-warning-border: #bb800966;
    --tsf-overlay-shadow: 0 4px 12px rgba(0, 0, 0, .4), 0 0 0 1px var(--tsf-border-subtle);
}

/* ===========================================================================
   Bootstrap 5.3 variable bridge — redeclare Bootstrap's OWN custom
   properties from the tokens above instead of overriding component classes
   with hardcoded hex. Bootstrap 5.3's root-level vars (body/border/link/
   focus-ring/card) are consumed live by most components via var(); the
   handful of pre-compiled per-variant components (button/alert color
   variants) are NOT wired to --bs-primary at runtime, so those get their own
   explicit local-variable overrides further down, exactly the way
   Bootstrap's own docs recommend customizing a variant.
   =========================================================================== */
:root {
    --bs-body-bg: var(--tsf-canvas);
    --bs-body-color: var(--tsf-text);
    --bs-emphasis-color: var(--tsf-text);
    --bs-secondary-color: var(--tsf-text-secondary);
    --bs-secondary-bg: var(--tsf-surface);
    --bs-tertiary-color: var(--tsf-text-tertiary);
    --bs-tertiary-bg: var(--tsf-surface);
    --bs-border-color: var(--tsf-border);
    --bs-border-color-translucent: var(--tsf-border-subtle);
    --bs-primary: var(--tsf-accent);
    --bs-primary-rgb: var(--tsf-accent-rgb);
    --bs-link-color: var(--tsf-accent);
    --bs-link-color-rgb: var(--tsf-accent-rgb);
    --bs-link-hover-color: var(--tsf-accent);
    --bs-focus-ring-color: rgba(var(--tsf-accent-rgb), .35);
    --bs-card-bg: var(--tsf-canvas);
    --bs-card-border-color: var(--tsf-border);
    --bs-card-cap-bg: var(--tsf-canvas);
    --bs-table-color: var(--tsf-text);
    --bs-table-bg: transparent;
    --bs-table-border-color: var(--tsf-border);
    --bs-table-striped-bg: var(--tsf-surface);
    --bs-table-hover-bg: var(--tsf-surface-hover);
    --bs-navbar-color: var(--tsf-text-secondary);
    --bs-navbar-hover-color: var(--tsf-text);
    --bs-navbar-active-color: var(--tsf-text);
    --bs-navbar-brand-color: var(--tsf-text);
    --bs-navbar-brand-hover-color: var(--tsf-text);
    --bs-dropdown-bg: var(--tsf-canvas);
    --bs-dropdown-border-color: var(--tsf-border);
    --bs-dropdown-link-color: var(--tsf-text);
    --bs-dropdown-link-hover-color: var(--tsf-text);
    --bs-dropdown-link-hover-bg: var(--tsf-surface-hover);
    --bs-dropdown-header-color: var(--tsf-text-secondary);
    --bs-dropdown-divider-bg: var(--tsf-border);
    --bs-modal-bg: var(--tsf-canvas);
    --bs-modal-border-color: var(--tsf-border);
    --bs-modal-header-border-color: var(--tsf-border);
    --bs-modal-footer-border-color: var(--tsf-border);
    --bs-form-select-bg: var(--tsf-canvas);
    --bs-pagination-color: var(--tsf-text);
    --bs-pagination-bg: var(--tsf-canvas);
    --bs-pagination-border-color: var(--tsf-border);
    --bs-pagination-hover-bg: var(--tsf-surface-hover);
    --bs-pagination-hover-border-color: var(--tsf-border-strong);
    --bs-pagination-disabled-bg: var(--tsf-surface);
    --bs-pagination-disabled-border-color: var(--tsf-border);
}

/* Global resets */
body {
    font-family: var(--tsf-font);
    font-size: var(--tsf-text-base);
    line-height: 1.5;
}

/* Icon alignment/spacing: most icon+label pairs in the templates carry their
   own explicit `me-*` utility, but a real minority (~40 across admin/auth
   templates) rely on a global default instead - not a Replit flourish, a
   genuine "icon glyph touches the following word" regression if dropped.
   Bootstrap's `.me-*` utilities carry `!important` so they always win over
   this base rule where a template does specify one. */
.fas, .fa, .bi {
    vertical-align: -0.125em;
    margin-right: var(--tsf-space-2);
}

.btn .fas,
.btn .fa,
.btn .bi {
    margin-right: .375rem;
}

/* Icon-only buttons (aria-label only, no visible text) must not carry the
   rule above - it would push the glyph off-centre inside the flex-centred
   `.btn`/`.btn-icon` box above. */
.btn-icon .fas,
.btn-icon .fa,
.btn-icon .bi,
.btn[aria-label] > .fas:only-child,
.btn[aria-label] > .fa:only-child,
.btn[aria-label] > .bi:only-child {
    margin: 0;
    line-height: 1;
}

/* ===========================================================================
   Navbar — ~48px compact utility bar (brief: current 3.5rem is oversized),
   bottom border only, no blur, no shadow.
   =========================================================================== */
.navbar {
    min-height: 3rem;
    padding-top: var(--tsf-space-1);
    padding-bottom: var(--tsf-space-1);
    background-color: var(--tsf-canvas);
}

.navbar-brand {
    font-size: var(--tsf-text-md);
    font-weight: 600;
}

.nav-link {
    font-size: var(--tsf-text-base);
    font-weight: 500;
    transition: color .15s ease, background-color .15s ease;
}

/* A 32px square icon-only control (theme toggle, account menu) — replaces
   the old `rounded-circle` + `btn-outline-light`/`btn-outline-dark` pair
   base.js used to swap on every toggle (removed: base.js now only ever
   changes the icon glyph and aria-*, never className). */
.btn-icon {
    width: var(--tsf-control-h);
    height: var(--tsf-control-h);
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
}

.app-footer {
    background: var(--tsf-canvas);
    color: var(--tsf-text-secondary);
    border-top: 1px solid var(--tsf-border);
}

/* ===========================================================================
   Cards → flat panels: border only, no shadow, no hover lift, no hover
   border-color change (a card here isn't a button/link).
   =========================================================================== */
.card {
    border-radius: var(--tsf-radius-md);
    box-shadow: none;
}

.card-header {
    font-weight: 600;
    font-size: var(--tsf-text-base);
    background-color: var(--tsf-canvas);
}

/* Stat/metric tiles (admin dashboard, analytics, security): a flat panel,
   one neutral icon treatment instead of four different saturated
   `text-{primary,success,warning,danger}` accents or solid
   `bg-{primary,success,warning,info} text-white` fills — "monochromatic to
   start" / "one accent, used only where it earns attention" (UI-BRIEF
   register notes), applied to every stat row in the app rather than one
   template at a time. */
.card .stat-tile-icon,
.card i.fa-2x,
.card i.fa-3x {
    color: var(--tsf-text-tertiary) !important;
    font-size: 1.5rem;
}

.stat-tile {
    height: 100%; /* equal-height tiles across a stat row regardless of label length */
}

.stat-tile-value {
    font-weight: 600;
}

/* ===========================================================================
   Buttons — compact 32px controls. Bootstrap bakes each `.btn-*` variant's
   colors as its OWN local `--bs-btn-*` custom properties at Sass compile
   time (not live references to --bs-primary), so retheming a variant means
   redeclaring those same variable names here — the officially documented way
   to customize a Bootstrap 5.3 button variant without forking the component.
   =========================================================================== */
.btn {
    --bs-btn-font-size: var(--tsf-text-base);
    --bs-btn-border-radius: var(--tsf-radius-sm);
    min-height: var(--tsf-control-h);
    padding-top: .25rem;
    padding-bottom: .25rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-weight: 500;
    transition: color .15s ease, background-color .15s ease, border-color .15s ease;
}

.btn-sm {
    --bs-btn-font-size: var(--tsf-text-sm);
    min-height: var(--tsf-control-h-sm);
    padding: .125rem .5rem;
}

/* One solid primary per screen (UI-BRIEF "Buttons"). Every SOLID fill here
   uses --tsf-accent-emphasis, not --tsf-accent - white text needs the
   darker emphasis shade in dark mode (see the token block comment above);
   the button's own focus ring stays on the lighter, more-visible
   --tsf-accent-rgb since a ring is a UI outline, not text-on-fill. */
.btn-primary {
    --bs-btn-color: var(--tsf-accent-on);
    --bs-btn-bg: var(--tsf-accent-emphasis);
    --bs-btn-border-color: var(--tsf-accent-emphasis);
    --bs-btn-hover-color: var(--tsf-accent-on);
    --bs-btn-hover-bg: var(--tsf-accent-emphasis);
    --bs-btn-hover-border-color: var(--tsf-accent-emphasis);
    --bs-btn-active-color: var(--tsf-accent-on);
    --bs-btn-active-bg: var(--tsf-accent-emphasis);
    --bs-btn-active-border-color: var(--tsf-accent-emphasis);
    --bs-btn-disabled-bg: var(--tsf-accent-emphasis);
    --bs-btn-disabled-border-color: var(--tsf-accent-emphasis);
    --bs-btn-focus-shadow-rgb: var(--tsf-accent-rgb);
}

.btn-outline-primary {
    /* Default (unhovered) state: text directly on canvas, no fill - the
       lighter --tsf-accent is correct AND more legible here. */
    --bs-btn-color: var(--tsf-accent);
    --bs-btn-border-color: var(--tsf-accent);
    /* Hover/active: a solid fill appears behind white text, so switch to
       the emphasis shade, same reasoning as .btn-primary above. */
    --bs-btn-hover-color: var(--tsf-accent-on);
    --bs-btn-hover-bg: var(--tsf-accent-emphasis);
    --bs-btn-hover-border-color: var(--tsf-accent-emphasis);
    --bs-btn-active-color: var(--tsf-accent-on);
    --bs-btn-active-bg: var(--tsf-accent-emphasis);
    --bs-btn-active-border-color: var(--tsf-accent-emphasis);
    --bs-btn-disabled-color: var(--tsf-accent);
    --bs-btn-disabled-border-color: var(--tsf-accent);
    --bs-btn-focus-shadow-rgb: var(--tsf-accent-rgb);
}

/* Secondary = outline, neutral border, text colour (UI-BRIEF "Buttons").
   Every variant below also redeclares --bs-btn-focus-shadow-rgb to the
   accent token: independent QA (Unit S verifier) caught that leaving it
   unset here fell back to Bootstrap's own untokenized gray
   (rgba(108,117,125,.5)) on these variants' focus ring, while
   primary/outline-primary correctly showed the accent ring - the fix is one
   consistent accent-colored focus ring across every button variant in the
   app, not just the ones that happened to redeclare it already. */
.btn-secondary {
    --bs-btn-color: var(--tsf-text);
    --bs-btn-bg: transparent;
    --bs-btn-border-color: var(--tsf-border-strong);
    --bs-btn-hover-color: var(--tsf-text);
    --bs-btn-hover-bg: var(--tsf-surface-hover);
    --bs-btn-hover-border-color: var(--tsf-border-strong);
    --bs-btn-active-color: var(--tsf-text);
    --bs-btn-active-bg: var(--tsf-surface-active);
    --bs-btn-active-border-color: var(--tsf-border-strong);
    --bs-btn-disabled-color: var(--tsf-text-secondary);
    --bs-btn-disabled-bg: transparent;
    --bs-btn-disabled-border-color: var(--tsf-border);
    --bs-btn-focus-shadow-rgb: var(--tsf-accent-rgb);
}

.btn-outline-secondary {
    --bs-btn-color: var(--tsf-text);
    --bs-btn-border-color: var(--tsf-border-strong);
    --bs-btn-hover-color: var(--tsf-text);
    --bs-btn-hover-bg: var(--tsf-surface-hover);
    --bs-btn-hover-border-color: var(--tsf-border-strong);
    --bs-btn-active-color: var(--tsf-text);
    --bs-btn-active-bg: var(--tsf-surface-active);
    --bs-btn-active-border-color: var(--tsf-border-strong);
    --bs-btn-disabled-color: var(--tsf-text-secondary);
    --bs-btn-disabled-border-color: var(--tsf-border);
    --bs-btn-focus-shadow-rgb: var(--tsf-accent-rgb);
}

/* Danger = outline; solid only where a template deliberately uses
   `.btn-danger` on a genuine confirm action (End Workflow, delete).
   Codex review round 2, consistency finding: a filled `.btn-danger` used to
   keep Bootstrap's own baked `#dc3545` instead of the token set - white on
   it happened to still pass AA (4.53:1) but "happens to pass" isn't the
   same guarantee `--tsf-danger-emphasis` carries (computed and pinned by
   tests/test_theme.py for both themes), and it meant every OTHER filled
   control in the app was token-driven except this one. Now every filled
   danger surface (`.btn-danger`, `.text-bg-danger` below) uses
   `--tsf-danger-emphasis` the same way `.btn-primary`/`.text-bg-primary`
   use `--tsf-accent-emphasis`. Default text/border on the OUTLINE variant
   still use --tsf-danger-fg (text-on-canvas, no fill); hover/active switch
   to --tsf-danger-emphasis for the same white-text-needs-a-darker-fill
   reason as the accent split above - Codex review round 1, HIGH: white on
   --tsf-danger-fg was 3.35:1 in dark mode. */
.btn-outline-danger {
    --bs-btn-color: var(--tsf-danger-fg);
    --bs-btn-border-color: var(--tsf-danger-fg);
    --bs-btn-hover-color: var(--tsf-accent-on);
    --bs-btn-hover-bg: var(--tsf-danger-emphasis);
    --bs-btn-hover-border-color: var(--tsf-danger-emphasis);
    --bs-btn-active-color: var(--tsf-accent-on);
    --bs-btn-active-bg: var(--tsf-danger-emphasis);
    --bs-btn-active-border-color: var(--tsf-danger-emphasis);
    --bs-btn-disabled-color: var(--tsf-danger-fg);
    --bs-btn-disabled-border-color: var(--tsf-danger-fg);
    --bs-btn-focus-shadow-rgb: var(--tsf-accent-rgb);
}

.btn-danger {
    --bs-btn-color: var(--tsf-accent-on);
    --bs-btn-bg: var(--tsf-danger-emphasis);
    --bs-btn-border-color: var(--tsf-danger-emphasis);
    --bs-btn-hover-color: var(--tsf-accent-on);
    --bs-btn-hover-bg: var(--tsf-danger-emphasis);
    --bs-btn-hover-border-color: var(--tsf-danger-emphasis);
    --bs-btn-active-color: var(--tsf-accent-on);
    --bs-btn-active-bg: var(--tsf-danger-emphasis);
    --bs-btn-active-border-color: var(--tsf-danger-emphasis);
    --bs-btn-disabled-bg: var(--tsf-danger-emphasis);
    --bs-btn-disabled-border-color: var(--tsf-danger-emphasis);
    --bs-btn-focus-shadow-rgb: var(--tsf-accent-rgb);
}

/* ===========================================================================
   Forms — 32px compact controls, border-strong border, accent focus ring
   (UI-BRIEF "Forms"). background/color/border-color otherwise inherit live
   from the Bootstrap bridge above (--bs-body-bg/-color/-border-color).
   =========================================================================== */
.form-control,
.form-select {
    min-height: var(--tsf-control-h);
    padding: .25rem .5rem;
    font-size: var(--tsf-text-base);
    border-color: var(--tsf-border-strong);
}

.form-control:focus,
.form-select:focus {
    border-color: var(--tsf-accent);
    box-shadow: 0 0 0 3px rgba(var(--tsf-accent-rgb), .25);
}

textarea.form-control {
    min-height: unset; /* rows="N" governs textarea height, not the 32px control floor */
}

.form-control-sm,
.form-select-sm {
    min-height: var(--tsf-control-h-sm);
    font-size: var(--tsf-text-sm);
}

.form-label {
    font-weight: 500;
    font-size: var(--tsf-text-sm);
    color: var(--tsf-text-secondary);
    margin-bottom: var(--tsf-space-1);
}

.input-group-text {
    min-height: var(--tsf-control-h);
    font-size: var(--tsf-text-base);
    background-color: var(--tsf-surface);
    border-color: var(--tsf-border-strong);
    color: var(--tsf-text-secondary);
}

/* ===========================================================================
   Tables — compact rows, plain-weight header (drop uppercase/tracking —
   Replit flourish, not a Primer/Carbon convention), bottom border only,
   hover via --tsf-surface-hover.
   =========================================================================== */
.table {
    font-size: var(--tsf-text-base);
    margin-bottom: 0;
}

.table > :not(caption) > * > th {
    font-weight: 600;
    color: var(--tsf-text-secondary);
    font-size: var(--tsf-text-sm);
    text-transform: none;
    letter-spacing: normal;
}

.table > :not(caption) > * > th,
.table > :not(caption) > * > td {
    padding: .5rem .75rem;
    vertical-align: middle;
}

/* ===========================================================================
   Badges — kept on Bootstrap 5.3's `text-bg-*` combo utility (accessibility
   work, docs/ACCESSIBILITY.md: it computes a contrasting foreground per
   background at the framework level and is covered by
   tests/test_template_accessibility.py's fixtures) rather than switching to
   `bg-*-subtle`/`text-*-emphasis` pairs — this only tightens sizing to the
   compact scale, it does not touch the colour system Unit R already
   verified and tested.
   =========================================================================== */
.badge {
    font-weight: 500;
    font-size: var(--tsf-text-xs);
    padding: .25rem .5rem;
    border-radius: var(--tsf-radius-sm);
}

/* `text-bg-primary` (Codex review, HIGH): Bootstrap's `text-bg-*` combo
   utility reads its background straight from `--bs-primary-rgb`, which the
   Bootstrap bridge above deliberately keeps on the TEXT-legible
   --tsf-accent (see that block's comment) - so this one utility inherited
   the same white-on-3.10:1 problem as .btn-primary. Bootstrap's own rule
   carries `!important`; matching it here is required to win, not optional -
   a plain declaration can never beat an `!important` one regardless of
   source order or specificity. */
.text-bg-primary {
    background-color: var(--tsf-accent-emphasis) !important;
}

/* `text-bg-danger` (Codex review round 2, consistency finding): the same
   reasoning as `.text-bg-primary` above, and the same reason `--bs-danger`/
   `--bs-danger-rgb` are deliberately left untouched globally - `.text-danger`
   (used 9 times across the app, as TEXT on canvas, never as a fill) needs
   the lighter/more-legible `--tsf-danger-fg`, not the fill-safe emphasis
   shade, so only this one utility gets its own explicit override. */
.text-bg-danger {
    background-color: var(--tsf-danger-emphasis) !important;
}

/* ===========================================================================
   Alerts — muted bg + matching border + emphasis fg from the semantic
   triplets (UI-BRIEF "Component rules"); no bright saturated backgrounds.
   =========================================================================== */
.alert {
    border-width: 1px;
    border-style: solid;
    border-radius: var(--tsf-radius-md);
    font-size: var(--tsf-text-base);
    padding: var(--tsf-space-3) var(--tsf-space-4);
}

.alert-success {
    --bs-alert-color: var(--tsf-success-fg);
    --bs-alert-bg: var(--tsf-success-bg);
    --bs-alert-border-color: var(--tsf-success-border);
    --bs-alert-link-color: var(--tsf-success-fg);
}

.alert-danger {
    --bs-alert-color: var(--tsf-danger-fg);
    --bs-alert-bg: var(--tsf-danger-bg);
    --bs-alert-border-color: var(--tsf-danger-border);
    --bs-alert-link-color: var(--tsf-danger-fg);
}

.alert-warning {
    --bs-alert-color: var(--tsf-warning-fg);
    --bs-alert-bg: var(--tsf-warning-bg);
    --bs-alert-border-color: var(--tsf-warning-border);
    --bs-alert-link-color: var(--tsf-warning-fg);
}

.alert-info,
.alert-secondary {
    --bs-alert-color: var(--tsf-text-secondary);
    --bs-alert-bg: var(--tsf-surface);
    --bs-alert-border-color: var(--tsf-border);
    --bs-alert-link-color: var(--tsf-accent);
}

/* A small flat panel for a block of read-only/quoted content inside a card
   (backup codes, saved notes) — replaces the old Replit `bg-light`/`bg-dark`
   utility pairing, which is a fixed grey regardless of theme and rendered as
   a bright white box in dark mode. */
.tsf-panel-muted {
    background-color: var(--tsf-surface);
    border-radius: var(--tsf-radius-md);
}

/* ===========================================================================
   Session-detail timeline (admin/session_detail.html's "Workflow Steps &
   Responses" list) — moved here from that template's own page-local
   `<style>` block (Codex review, LOW: a page-local style is inline CSP
   surface Diego's own security posture doesn't need, and it also meant this
   one component's tokens lived outside the sitewide system everything else
   pulls from).
   =========================================================================== */
.timeline {
    position: relative;
    padding-left: 3rem;
}

.timeline-item {
    position: relative;
    padding-bottom: 1.5rem;
}

.timeline-item:not(:last-child)::before {
    content: '';
    position: absolute;
    left: -1.5rem;
    top: 1.5rem;
    bottom: -1.5rem;
    width: 2px;
    background-color: var(--tsf-border);
}

.timeline-marker {
    position: absolute;
    left: -2rem;
    top: 0;
    width: 1rem;
    height: 1rem;
    background-color: var(--tsf-canvas);
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
}

.timeline-content {
    background-color: var(--tsf-surface);
    padding: 1rem;
    border-radius: var(--tsf-radius-md);
    border-left: 3px solid var(--tsf-accent);
}

/* ===========================================================================
   Modals — radius-lg, border not shadow, right-aligned footer actions (the
   templates already right-align footer buttons via markup order).
   =========================================================================== */
.modal-content {
    border-radius: var(--tsf-radius-lg);
    border: 1px solid var(--tsf-border);
    box-shadow: none;
}

.modal-header,
.modal-footer {
    border-color: var(--tsf-border);
}

/* ===========================================================================
   Dropdown menu — the one place in the app that floats with no backdrop of
   its own, so it keeps the one allowed shadow (--tsf-overlay-shadow, defined
   once in the token block above) instead of a border pretending to be
   enough separation from the page behind it.
   =========================================================================== */
.dropdown-menu {
    border-radius: var(--tsf-radius-md);
    box-shadow: var(--tsf-overlay-shadow);
}

.dropdown-item:hover,
.dropdown-item:focus {
    background-color: var(--tsf-surface-hover);
    color: var(--tsf-text);
}

/* M-5 (2026-09-25 release-gate audit, browser QA): the main content column
   needs its own stacking context (position: relative, no z-index of its own)
   so a fixed/absolutely-positioned descendant can't paint above a later
   sibling like the navbar dropdown in a way a stacking context would
   otherwise have prevented - carried forward verbatim from the pre-Unit-S
   stylesheet; tests/test_security_unit.py's
   test_browser_qa_regression_source_markers_present pins it, including that
   it stays z-index-free. */
main.container {
    position: relative;
}

/* ===========================================================================
   Workflow step form — keep the left-border active-state indicator (the
   retroactive brief's signature move); recolor to the accent token only.
   =========================================================================== */
.workflow-step {
    border-left: 3px solid var(--tsf-border);
    padding-left: var(--tsf-space-4);
    margin-left: var(--tsf-space-2);
}

.workflow-step.active {
    border-left-color: var(--tsf-accent);
}

/* Notes panel / path-code block: a plain textarea and a monospace code
   block respectively, not cards with extra chrome. */
.notes-area {
    min-height: 8rem;
    resize: vertical;
}

.summary-box,
code {
    background-color: var(--tsf-surface);
    border: 1px solid var(--tsf-border);
    border-radius: var(--tsf-radius-sm);
}

.summary-box {
    padding: var(--tsf-space-4);
    font-size: var(--tsf-text-base);
    line-height: 1.6;
}

code {
    padding: .125rem .375rem;
    color: var(--tsf-text);
}

.summary-notes {
    white-space: pre-wrap;
}

/* ===========================================================================
   Workflow editor's server-rendered diagram (app/diagram.py): the SVG is
   injected as raw markup and already emits `fill`/`stroke="currentColor"`
   throughout (no hardcoded hex anywhere in that generator), so scoping the
   inherited CSS `color` property here is enough for it to theme correctly
   AND repaint automatically the instant `data-bs-theme` changes - no JS
   redraw needed on toggle.
   =========================================================================== */
#diagramPanel svg {
    color: var(--tsf-text);
}

.diagram-panel-min-height {
    min-height: 200px;
}

.editor-sticky-panel {
    top: 1rem;
}

/* ===========================================================================
   Small layout/utility fixes carried forward as their own classes (moved out
   of inline style="" attributes for CSP style-src cleanliness, browser QA
   2026-09-25).
   =========================================================================== */
.d-inline-form {
    display: inline;
}

.mfa-qr-code {
    max-width: 200px;
}

/* R2 browser QA (2026-09-26): the old blanket `.table td { max-width: 200px;
   overflow: hidden }` clipped a wide action cell outright (no ellipsis is
   possible on a <button>/<form> child, only on plain text) - removed
   entirely per the UI-BRIEF removal list rather than kept as a default with
   a per-cell opt-out. A table cell that needs truncation opts in here
   instead (none currently do; this class is now available for the one that
   eventually needs it). */
.table-cell-truncate {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 200px;
}

.table .btn {
    white-space: nowrap;
    margin: 0 .125rem;
}

/* ===========================================================================
   R1 (WCAG 2.2 AA, "Bypass Blocks" / "Focus Visible") — unchanged behaviour,
   repointed to the token set.
   =========================================================================== */

/* Off-screen until focused, then pinned to the top-left corner above
   everything else - the standard "skip to content" pattern. Positioned
   absolutely (not display:none) so it can actually RECEIVE keyboard focus;
   a display:none element is unreachable by Tab. */
.skip-link {
    position: absolute;
    top: -3rem;
    left: .5rem;
    z-index: 2000;
    padding: .5rem 1rem;
    /* Solid fill behind white text - same accent-emphasis reasoning as the
       button/badge fixes above, found while auditing every remaining
       `background: var(--tsf-accent)` site for the same bug pattern. */
    background: var(--tsf-accent-emphasis);
    color: var(--tsf-accent-on);
    border-radius: 0 0 var(--tsf-radius-md) var(--tsf-radius-md);
    text-decoration: none;
    font-weight: 600;
    transition: top .15s ease;
}

.skip-link:focus {
    top: 0;
}

/* A visible focus ring on every interactive element, in both themes -
   Bootstrap 5's own focus ring is subtle enough on this palette to be easy
   to lose track of when tabbing through a dense admin table. outline
   (not box-shadow) so it is never clipped by an ancestor's overflow:hidden. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible {
    outline: 2px solid var(--tsf-accent);
    outline-offset: 2px;
}

/* main-content is a skip-link/focus target, not a visual element - it must
   never show its own focus ring (there is nothing to ring; the ring
   belongs on whatever the user lands on next). */
#main-content:focus-visible {
    outline: none;
}

/* Responsive adjustments */
@media (max-width: 768px) {
    .container-fluid {
        padding-left: 1rem;
        padding-right: 1rem;
    }

    .navbar-brand {
        font-size: var(--tsf-text-base);
    }
}

/* ===========================================================================
   Print (Codex review, MEDIUM): `/summary` and the MFA backup-codes page are
   both meant to leave the screen - a technician attaching a summary to a
   physical ticket folder, backup codes kept somewhere offline. Neither page
   had ANY print handling before this: dark-theme colors (light text on a
   near-black background, when `data-bs-theme="dark"` is the active choice)
   and every nav/action control would have printed exactly as shown on
   screen. Forcing the --tsf-* and --bs-* TOKENS to a black-on-white pair here,
   rather than writing print-specific overrides per component, means every
   component that already reads those tokens (cards, alerts, badges, the
   summary/backup-code text) goes black-on-white for free, the same
   token-bridge approach the rest of this file uses. Nav/footer/banners and
   every page's own action buttons are hidden via Bootstrap's own
   `.d-print-none` utility, applied directly in the templates that have
   them, rather than a parallel set of print-only selectors here.
   =========================================================================== */
@media print {
    :root,
    [data-bs-theme="dark"] {
        --tsf-canvas: #ffffff;
        --tsf-surface: #ffffff;
        --tsf-surface-hover: #ffffff;
        --tsf-surface-active: #ffffff;
        --tsf-border: #000000;
        --tsf-border-strong: #000000;
        --tsf-border-subtle: #00000080;
        --tsf-text: #000000;
        --tsf-text-secondary: #000000;
        --tsf-text-tertiary: #000000;
        --tsf-inverse-surface: #ffffff;
        --tsf-accent: #000000;
        --tsf-accent-rgb: 0, 0, 0;
        --tsf-accent-emphasis: #000000;
        --tsf-accent-on: #ffffff;
        --tsf-success-fg: #000000;
        --tsf-danger-fg: #000000;
        --tsf-warning-fg: #000000;
        --tsf-shadow: none;
        --tsf-overlay-shadow: none;
    }

    body {
        background: #ffffff;
        color: #000000;
    }

    a {
        color: #000000;
        text-decoration: underline;
    }

    .card,
    .alert,
    .modal-content {
        box-shadow: none !important;
        border: 1px solid #000000 !important;
    }

    /* Bootstrap's own `text-bg-*` badge backgrounds are baked per-variant
       hex, not driven by the token overrides above - force them flat too,
       so nothing assumes a colour printer or wastes coloured ink on a
       black-and-white one. */
    .badge {
        background: #ffffff !important;
        color: #000000 !important;
        border: 1px solid #000000;
    }

    /* Page breaks: keep each card, alert, and backup code on one page
       rather than splitting mid-block, and never leave a heading stranded
       at the bottom of a page with its content pushed to the next one. */
    .card,
    .alert,
    .backup-code {
        break-inside: avoid;
    }

    h1, h2, h3, h4, h5, h6 {
        break-after: avoid;
    }
}
