/* ============================================================================
   Spark.Web — Design Tokens
   ----------------------------------------------------------------------------
   Single source of truth for the visual system. Component-level styles live in
   per-component .razor.css files (Blazor CSS isolation) and reference these
   global tokens via var(--sk-*). Add new colors/spacing here first.
   ============================================================================ */
:root {
    /* ── Surface tiers (dark-mode first, high-contrast minimalist) ─────────── */
    --sk-bg-0:           #0a0a0c;   /* deepest — app background */
    --sk-bg-1:           #0f0f12;   /* chrome (header, sidebar) */
    --sk-bg-2:           #15151a;   /* cards */
    --sk-bg-3:           #1c1c22;   /* card hover / nested surface */
    --sk-bg-4:           #232329;   /* active selection / pressed */
    --sk-bg-elevated:    #1a1a20;   /* popovers, dropdowns */

    /* ── Borders (translucent + opaque, Linear-style) ──────────────────────── */
    --sk-border:         rgba(255, 255, 255, 0.06);
    --sk-border-strong:  rgba(255, 255, 255, 0.10);
    --sk-border-accent:  rgba(79, 140, 255, 0.32);
    --sk-divider:        #232329;

    /* ── Text ──────────────────────────────────────────────────────────────── */
    --sk-text:           #ececf1;
    --sk-text-soft:      #c0c0c8;
    --sk-text-muted:     #8a8a95;
    --sk-text-faint:     #7a7a85;   /* lifted for WCAG AA contrast on the dark ground */
    --sk-text-inverse:   #0a0a0c;

    /* ── Accents (reserved STRICTLY for execution state) ───────────────────── */
    --sk-accent:         #4f8cff;       /* primary interactive */
    --sk-accent-soft:    rgba(79, 140, 255, 0.12);
    --sk-running:        #4f8cff;       /* electric blue — actively executing */
    --sk-running-glow:   rgba(79, 140, 255, 0.45);
    --sk-success:        #5ad6a8;       /* mint — passed/complete */
    --sk-success-glow:   rgba(90, 214, 168, 0.35);
    --sk-error:          #ff5d6c;       /* neon coral — failed */
    --sk-error-glow:     rgba(255, 93, 108, 0.40);
    --sk-warning:        #f5b942;       /* amber — retries/warnings */
    --sk-warning-glow:   rgba(245, 185, 66, 0.35);
    --sk-queued:         #6e6e7a;       /* dim — queued / not yet started */
    --sk-skipped:        #4a4a52;       /* very dim — skipped */
    --sk-brand:          #f0c040;       /* Spark brand gold */

    /* ── Spacing (4px base) ────────────────────────────────────────────────── */
    --sk-space-1:  4px;
    --sk-space-2:  8px;
    --sk-space-3:  12px;
    --sk-space-4:  16px;
    --sk-space-5:  20px;
    --sk-space-6:  24px;
    --sk-space-8:  32px;
    --sk-space-10: 40px;
    --sk-space-12: 48px;

    /* ── Radii ─────────────────────────────────────────────────────────────── */
    --sk-radius-sm:   4px;
    --sk-radius:      6px;
    --sk-radius-md:   8px;
    --sk-radius-lg:  12px;
    --sk-radius-xl:  16px;
    --sk-radius-pill: 999px;

    /* ── Shadows ───────────────────────────────────────────────────────────── */
    --sk-shadow-sm:  0 1px 2px rgba(0, 0, 0, 0.35);
    --sk-shadow:     0 4px 12px rgba(0, 0, 0, 0.40);
    --sk-shadow-lg:  0 10px 32px rgba(0, 0, 0, 0.50);

    /* ── Type — defaults inherit "medium" tier (see data-text-size below) ── */
    --sk-font-sans:  'Inter', 'Segoe UI', 'SF Pro Text', system-ui, sans-serif;
    --sk-font-mono:  'JetBrains Mono', 'Cascadia Code', 'Consolas', monospace;
    --sk-fs-xs:   12px;
    --sk-fs-sm:   13px;
    --sk-fs-md:   14px;
    --sk-fs-base: 15px;
    --sk-fs-lg:   17px;
    --sk-fs-xl:   22px;
    --sk-fs-2xl:  30px;

    /* ── Motion ────────────────────────────────────────────────────────────── */
    --sk-ease:        cubic-bezier(0.4, 0, 0.2, 1);
    --sk-ease-out:    cubic-bezier(0.0, 0, 0.2, 1);
    --sk-ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
    --sk-dur-fast:    0.12s;
    --sk-dur:         0.22s;
    --sk-dur-slow:    0.4s;
}

/* Simple/Power progressive disclosure — anything tagged .power-only is hidden in Simple mode and
   revealed in Power mode, app-wide. This rule previously lived only inside Home, so .power-only did
   nothing on other pages; centralised here so the toggle works everywhere (mirrors .nav-advanced). */
html[data-ui-mode='simple'] .power-only { display: none; }

/* ============================================================================
   Text-size scale — S / M / L
   ----------------------------------------------------------------------------
   Industry-aligned tiers (Notion, Slack, WCAG body baseline). Switch via the
   data-text-size attribute on <html>:
       <html data-text-size="small">    — compact dashboards (14px base)
       <html data-text-size="medium">   — default, comfortable (15px base)
       <html data-text-size="large">    — accessibility / casual (17px base)
   The :root block above already defines the "medium" defaults; the rules below
   override them for the "small" and "large" tiers. Components that read
   --sk-fs-* tokens (or rem units against the html base) scale automatically.
   ============================================================================ */
html[data-text-size="small"] {
    --sk-fs-xs:   11px;
    --sk-fs-sm:   12px;
    --sk-fs-md:   13px;
    --sk-fs-base: 14px;
    --sk-fs-lg:   16px;
    --sk-fs-xl:   20px;
    --sk-fs-2xl:  28px;
}
html[data-text-size="large"] {
    --sk-fs-xs:   13px;
    --sk-fs-sm:   14px;
    --sk-fs-md:   16px;
    --sk-fs-base: 17px;
    --sk-fs-lg:   19px;
    --sk-fs-xl:   24px;
    --sk-fs-2xl:  34px;
}

/* ============================================================================
   Base
   ============================================================================ */
html, body {
    margin: 0;
    padding: 0;
    font-family: var(--sk-font-sans);
    font-size: var(--sk-fs-base);
    background: var(--sk-bg-0);
    color: var(--sk-text);
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
}

h1, h2, h3, h4, h5, h6 { font-weight: 600; letter-spacing: -0.01em; color: var(--sk-text); }
h2 { font-size: var(--sk-fs-xl); }
h5 { font-size: var(--sk-fs-md); }

a, .btn-link {
    color: var(--sk-accent);
    text-decoration: none;
    transition: color var(--sk-dur-fast) var(--sk-ease);
}
a:hover { color: #7ea9ff; text-decoration: none; }

code, pre { font-family: var(--sk-font-mono); }

/* Bootstrap defaults override (kept narrow to avoid surprising other pages) */
.content { padding-top: var(--sk-space-5); }

.btn:focus, .btn:active:focus, .form-control:focus, .form-check-input:focus {
    box-shadow: 0 0 0 2px var(--sk-accent-soft);
    outline: none;
}
.btn-primary { background: var(--sk-accent); border-color: var(--sk-accent); color: var(--sk-text-inverse); }
.btn-primary:hover { background: #6ba3ff; border-color: #6ba3ff; color: var(--sk-text-inverse); }

/* Form validation (existing behavior preserved with token colors) */
.valid.modified:not([type=checkbox]) { outline: 1px solid var(--sk-success); }
.invalid { outline: 1px solid var(--sk-error); }
.validation-message { color: var(--sk-error); }

/* ============================================================================
   Reusable utilities (global — referenced from many .razor.css files)
   ============================================================================ */

/* Cards — universal unit of UI */
.sk-card {
    background: var(--sk-bg-2);
    border: 1px solid var(--sk-border);
    border-radius: var(--sk-radius-md);
    padding: var(--sk-space-4);
    transition: border-color var(--sk-dur) var(--sk-ease),
                background var(--sk-dur) var(--sk-ease),
                transform var(--sk-dur) var(--sk-ease);
}
.sk-card:hover {
    border-color: var(--sk-border-strong);
    background: var(--sk-bg-3);
}
.sk-card.is-interactive { cursor: pointer; }
.sk-card.is-interactive:hover { transform: translateY(-1px); }

/* Status pills */
.sk-pill {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 2px 8px;
    border-radius: var(--sk-radius-pill);
    font-size: var(--sk-fs-xs); font-weight: 500;
    border: 1px solid var(--sk-border);
    background: var(--sk-bg-3);
    color: var(--sk-text-soft);
    line-height: 1.4;
}
.sk-pill .sk-dot {
    width: 6px; height: 6px;
    border-radius: 50%;
    background: currentColor;
    box-shadow: 0 0 0 0 currentColor;
}
.sk-pill.is-running { color: var(--sk-running); border-color: var(--sk-border-accent); background: var(--sk-accent-soft); }
.sk-pill.is-running .sk-dot { animation: sk-pulse 1.4s var(--sk-ease) infinite; }
.sk-pill.is-success { color: var(--sk-success); border-color: rgba(90, 214, 168, 0.32); background: rgba(90, 214, 168, 0.08); }
.sk-pill.is-error   { color: var(--sk-error);   border-color: rgba(255, 93, 108, 0.32); background: rgba(255, 93, 108, 0.08); }
.sk-pill.is-warning { color: var(--sk-warning); border-color: rgba(245, 185, 66, 0.32); background: rgba(245, 185, 66, 0.08); }
.sk-pill.is-idle    { color: var(--sk-text-muted); }
.sk-pill.is-offline { color: var(--sk-text-faint); }

@keyframes sk-pulse {
    0%   { box-shadow: 0 0 0 0 currentColor; opacity: 1; }
    70%  { box-shadow: 0 0 0 6px transparent; opacity: 0.6; }
    100% { box-shadow: 0 0 0 0 transparent; opacity: 1; }
}

/* Buttons */
.sk-btn {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 6px 12px;
    background: var(--sk-bg-3);
    color: var(--sk-text);
    border: 1px solid var(--sk-border);
    border-radius: var(--sk-radius);
    font-size: var(--sk-fs-sm); font-weight: 500;
    cursor: pointer;
    transition: all var(--sk-dur-fast) var(--sk-ease);
    font-family: inherit;
    text-decoration: none;
}
.sk-btn:hover { background: var(--sk-bg-4); border-color: var(--sk-border-strong); color: var(--sk-text); }
.sk-btn:active { transform: translateY(1px); }
.sk-btn.is-primary { background: var(--sk-accent); color: var(--sk-text-inverse); border-color: transparent; }
.sk-btn.is-primary:hover { background: #6ba3ff; color: var(--sk-text-inverse); }
.sk-btn.is-ghost   { background: transparent; }
.sk-btn.is-danger  { background: var(--sk-error); color: white; border-color: transparent; }
.sk-btn.is-xs { padding: 2px 8px; font-size: var(--sk-fs-xs); }
.sk-btn:disabled { opacity: 0.5; cursor: not-allowed; }

/* Section labels */
.sk-section-label {
    font-size: var(--sk-fs-xs);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--sk-text-faint);
    font-weight: 600;
}

/* Dot-grid utility */
.sk-dotgrid {
    background-color: var(--sk-bg-0);
    background-image: radial-gradient(circle at center, rgba(255, 255, 255, 0.06) 1px, transparent 1px);
    background-size: 22px 22px;
}

/* ============================================================================
   Scrollbars
   ============================================================================ */
::-webkit-scrollbar       { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
    background: var(--sk-bg-4);
    border-radius: 5px;
    border: 2px solid var(--sk-bg-0);
}
::-webkit-scrollbar-thumb:hover { background: #2e2e36; }

/* ============================================================================
   Blazor error boundary (dark)
   ============================================================================ */
.blazor-error-boundary {
    background: var(--sk-error);
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
    border-radius: var(--sk-radius);
}
.blazor-error-boundary::after { content: "An error has occurred."; }

.darker-border-checkbox.form-check-input { border-color: var(--sk-border-strong); }

.form-floating > .form-control-plaintext::placeholder,
.form-floating > .form-control::placeholder {
    color: var(--sk-text-muted);
    text-align: end;
}
.form-floating > .form-control-plaintext:focus::placeholder,
.form-floating > .form-control:focus::placeholder {
    text-align: start;
}

/* ============================================================================
   Responsive helpers (mobile / tablet)
   ----------------------------------------------------------------------------
   Used across pages — keep narrow and well-named. Page-specific responsive
   tweaks live in component-scoped CSS.
   ============================================================================ */

/* Horizontal-scroll wrapper for wide tables on small screens. Wrap any <table>
   that doesn't fit on a phone in <div class="sk-table-scroll">…</div>. */
.sk-table-scroll {
    width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}
.sk-table-scroll > table { min-width: 560px; }

/* Prevent images & SVGs from busting layouts on phones. */
img, svg { max-width: 100%; }

/* Bootstrap tables — at least scroll horizontally rather than overflow off-screen */
.table-responsive { -webkit-overflow-scrolling: touch; }

/* Long monospace strings (job IDs, ticket keys) should break before they overflow */
code, .mono { word-break: break-word; overflow-wrap: anywhere; }

@media (max-width: 640.98px) {
    /* Tighter outer padding on the content area was set in MainLayout; this just
       keeps headings from dominating phones. */
    h1 { font-size: 1.5rem; }
    h2 { font-size: 1.25rem; }
    h3 { font-size: 1.1rem; }
    .content { padding-top: var(--sk-space-3); }

    /* Make Bootstrap modals usable on phones — full-width, and (crucially) fit the
       viewport with a scrollable body instead of overflowing off-screen. 100dvh tracks
       the *visible* height as mobile browser chrome shows/hides. */
    .modal-dialog { margin: 0.5rem; max-width: calc(100% - 1rem); }
    .modal-dialog-centered { min-height: calc(100dvh - 1rem); }
    .modal-content { border-radius: var(--sk-radius-md); max-height: calc(100dvh - 1rem); }
    .modal-body { overflow-y: auto; -webkit-overflow-scrolling: touch; }
    /* Keep header/footer actions reachable while the body scrolls */
    .modal-header, .modal-footer { flex-shrink: 0; }
    .modal-footer { position: sticky; bottom: 0; background: inherit; }

    /* Form controls — full-width on phones so they don't sit awkwardly */
    .form-control, .form-select { width: 100%; }

    /* Buttons in button-rows wrap cleanly */
    .btn-toolbar, .btn-group { flex-wrap: wrap; gap: var(--sk-space-2); }

    /* Bootstrap tables on phones — scroll horizontally instead of overflowing the
       page. We don't wrap every table in .table-responsive, so apply the same
       behaviour at the table level. Column widths won't be uniform across rows
       (display: block side-effect), which is the accepted trade-off for keeping
       all columns reachable. */
    .table {
        display: block;
        max-width: 100%;
        overflow-x: auto;
        white-space: nowrap;
        -webkit-overflow-scrolling: touch;
    }

    /* Comfortable tap targets on phones (Apple HIG ~44px). Scoped to phones so dense
       desktop toolbars/tables keep their compact sizing. inline-flex keeps icon+label
       centered when a min-height is taller than the content. */
    .btn, .sk-btn {
        min-height: 40px;
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }
    .form-control, .form-select { min-height: 44px; }
    .btn-close { width: 1.1rem; height: 1.1rem; background-size: 1.1rem; padding: 0.65rem; }
    .form-check-input { width: 1.15rem; height: 1.15rem; margin-top: 0.15rem; }
    .dropdown-item { min-height: 40px; display: flex; align-items: center; }

    /* "Everything is tiny" was largely the detail text (pills, labels, meta, table
       small text). Nudge the two smallest type steps up a touch on phones — per S/M/L
       tier, so the Text-size picker still works and dense/large users keep their choice. */
    html[data-text-size="small"]  { --sk-fs-xs: 12px;   --sk-fs-sm: 13px; }
    html[data-text-size="medium"] { --sk-fs-xs: 13px;   --sk-fs-sm: 14px; }
    html[data-text-size="large"]  { --sk-fs-xs: 13.5px; --sk-fs-sm: 14.5px; }
}

/* iOS Safari prefers 16px+ inputs (otherwise it zooms on focus). Keep it small
   on desktop, bump to 16px on phones. */
@media (max-width: 640.98px) and (hover: none) {
    input[type="text"], input[type="search"], input[type="email"],
    input[type="number"], input[type="password"], input[type="url"],
    textarea, select, .form-control, .form-select { font-size: 16px; }
}

/* ============================================================================
   Page-nav bar — global Back button + breadcrumb trail
   ----------------------------------------------------------------------------
   Sits between the top row and page content (see MainLayout). The Back button is
   the primary "back" affordance in the installed PWA, whose standalone window
   has no browser chrome. Uses --sk-* tokens so it tracks the theme. A long trail
   scrolls horizontally rather than wrapping or pushing the layout wide.
   ============================================================================ */
.page-nav {
    display: flex;
    align-items: center;
    gap: var(--sk-space-3);
    min-height: 40px;
    padding-top: 6px;
    padding-bottom: 6px;
    border-bottom: 1px solid var(--sk-border);
    overflow-x: auto;
    scrollbar-width: none;               /* Firefox — hide the horizontal scrollbar */
}
.page-nav::-webkit-scrollbar { display: none; }

/* Back button — ghost style; dims and drops interaction when there's no history. */
.page-nav-back {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    flex-shrink: 0;
    padding: 3px 10px 3px 8px;
    font-family: inherit;
    font-size: var(--sk-fs-sm);
    font-weight: 600;
    color: var(--sk-text-soft);
    background: transparent;
    border: 1px solid var(--sk-border-strong);
    border-radius: var(--sk-radius);
    cursor: pointer;
    transition: background .12s var(--sk-ease), color .12s var(--sk-ease), border-color .12s var(--sk-ease);
}
.page-nav-back:hover:not(:disabled) {
    background: var(--sk-bg-3);
    color: var(--sk-text);
}
.page-nav-back:disabled { opacity: 0.4; cursor: default; }
.page-nav-back .bi { font-size: 1rem; line-height: 1; }

/* Breadcrumb trail */
.page-nav-crumbs { min-width: 0; }
.page-nav-crumbs ol {
    display: flex;
    align-items: center;
    flex-wrap: nowrap;
    margin: 0;
    padding: 0;
    list-style: none;
    white-space: nowrap;
    font-size: var(--sk-fs-sm);
}
.page-nav-crumbs li { display: inline-flex; align-items: center; }
.page-nav-crumbs li + li::before {
    content: "\203A";                    /* › */
    margin: 0 8px;
    color: var(--sk-text-faint);
}
.page-nav-crumbs a { color: var(--sk-text-muted); text-decoration: none; }
.page-nav-crumbs a:hover { color: var(--sk-accent); }
.page-nav-crumbs .current { color: var(--sk-text); font-weight: 600; }

/* Narrow screens — collapse Back to an icon to save room, and don't let the bar
   inflate the tap-target min-height it inherits from the global phone button rule. */
@media (max-width: 640.98px) {
    .page-nav-back-label { display: none; }
    .page-nav-back { padding: 3px 8px; min-height: 32px; }
}

/* ============================================================================
   Shared guided-UI bridge  (_content/TechBenchEco.Blazor.Common/guided.css)
   ----------------------------------------------------------------------------
   GuidedLanding / GuidedForm / GuidedRunPanel are shared across four apps and
   resolve their colours through   var(--guided-X, var(--shell-X, var(--color-X,
   <literal>)))  — QaToolKit's tokens, then WebGuard's, then a charcoal literal.

   Spark.Web uses NEITHER of those systems, so before this block it satisfied no
   rung and the guided components rendered the dark literals verbatim: a charcoal
   island on the light / y2k / kenney / xp themes.

   Mapping the --guided-* rung is the contract's own escape hatch. Because these
   point at --sk-* rather than at hex, every one of the 10 themes restyles the
   guided surfaces for free — including the theme-specific accents.
   ============================================================================ */
:root {
    --guided-bg:        var(--sk-bg-0);
    --guided-surface:   var(--sk-bg-2);          /* "cards" tier — matches .sk-card */
    --guided-raised:    var(--sk-bg-3);
    --guided-input-bg:  var(--sk-bg-3);        /* the gate's password field */
    --guided-border:    var(--sk-border);
    --guided-border-hi: var(--sk-border-strong);
    --guided-text:      var(--sk-text);
    --guided-muted:     var(--sk-text-muted);
    --guided-dim:       var(--sk-text-faint);
    --guided-accent:    var(--sk-accent);
    --guided-accent-hi: var(--sk-accent);
    --guided-ok:        var(--sk-success);
    --guided-warn:      var(--sk-warning);
    --guided-danger:    var(--sk-error);
}

/* Run-report role filter ───────────────────────────────────────────────────
   Every WebGuard finding carries a dev / QA / management explanation, and the
   run panel rendered all three at once — two thirds of the words in front of
   any given reader written for somebody else. Picking a role on the report
   hides the other two audiences' lines; Management also drops the technical
   evidence (.tech: selectors and raw output).

   Mirrors the same rules in the standalone HTML report
   (HtmlReportExporter) and the desktop app (components.css), so a run reads
   the same way whichever surface you open it on. */
[data-report-role="dev"] .aud-qa,
[data-report-role="dev"] .aud-mgmt,
[data-report-role="qa"] .aud-dev,
[data-report-role="qa"] .aud-mgmt,
[data-report-role="mgmt"] .aud-dev,
[data-report-role="mgmt"] .aud-qa,
[data-report-role="mgmt"] .tech { display: none; }

/* A why-block whose only surviving line was filtered out would render as an
   empty bordered box. */
[data-report-role="dev"] .whyblock:not(:has(.aud-dev)),
[data-report-role="qa"] .whyblock:not(:has(.aud-qa)),
[data-report-role="mgmt"] .whyblock:not(:has(.aud-mgmt)) { display: none; }

/* Run-report status + severity badges ──────────────────────────────────────
   The run panel used Bootstrap's filled contextual badges: bg-danger,
   bg-warning text-dark, bg-info text-dark, bg-secondary. A failing run is
   thirty of those on one screen — a wall of solid red, amber and cyan blocks
   in which nothing is emphasised because everything is. It's the same problem
   the standalone HTML report fixed by going to tinted text, and this is the
   Spark.Web half of it.

   Shape: a hairline pill whose TEXT is always body ink, with a small coloured
   dot carrying the status. Two reasons over tinted text:

   1. Ten themes. --sk-warning is #9a6700 on `light` (fine as text) but #ff9f1c
      on `frutiger` and #f9f871 on `y2k` — as text on those surfaces it lands
      near 2:1. As a 7px dot beside readable ink it is a mark, not body text,
      and 3:1 is the bar it has to clear.
   2. Colour is never the only channel: the word is right there, so the badge
      still reads on a mono printout and under any colour-vision deficiency. */
.report-badge {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 1px 9px 1px 7px;
    border: 1px solid var(--sk-border);
    border-radius: 999px;
    background: transparent;
    color: var(--sk-text);
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    line-height: 1.6;
    white-space: nowrap;
    text-transform: none;
}
.report-badge::before {
    content: "";
    width: 7px;
    height: 7px;
    border-radius: 50%;
    flex: none;
    background: var(--report-dot, var(--sk-text-faint));
    /* A hairline ring so the dot has an edge even where a theme's own semantic colour sits close to
       its card — measured, the dot lands under 3:1 against the surface on kenney, xp, win9x and y2k.
       It never has to carry meaning alone (the status word is right beside it), but a mark you can't
       quite see is just a smudge. */
    box-shadow: 0 0 0 1px var(--sk-border-strong, var(--sk-border));
}
.report-badge[data-tone="fail"]  { --report-dot: var(--sk-error); }
.report-badge[data-tone="warn"]  { --report-dot: var(--sk-warning); }
.report-badge[data-tone="pass"]  { --report-dot: var(--sk-success); }
.report-badge[data-tone="info"]  { --report-dot: var(--sk-brand); }
.report-badge[data-tone="muted"] { --report-dot: var(--sk-text-faint); }

/* Blocking findings get a left edge on the row instead of a tinted row
   background: the row tint fought with the badge and the red error text for
   the same attention, and three emphases on one row is none. */
.report-row-blocking { border-left: 3px solid var(--sk-error); }

/* Any column of figures is read by comparing digits down the column. */
.report-num { font-variant-numeric: tabular-nums; }

/* ── Sign-in (SparkWebReUX F8) ────────────────────────────────────────────────
   The only screen an unauthenticated colleague ever sees. It used to sit off the
   design system: a hardcoded #1a1a2e→#0f3460 gradient used nowhere else in the
   app, and Bootstrap's .text-muted — a grey chosen for light backgrounds —
   reading at roughly 3:1 on that dark ground.

   It now uses the app's own --sk-* tokens, so it looks like the product it is
   the front door to, and the supporting copy takes --sk-text-muted, which is
   the tier that was raised for AA contrast on this ground. */
.sk-signin {
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
    background:
        radial-gradient(80rem 40rem at 50% -10%, rgba(79, 140, 255, 0.10), transparent 60%),
        var(--sk-bg-0);
}

.sk-signin-card {
    text-align: center;
    max-width: 30rem;
}

.sk-signin-mark {
    font-size: 3.25rem;
    color: var(--sk-accent);
    line-height: 1;
}

.sk-signin-title {
    margin: 1rem 0 0;
    font-size: 2.25rem;
    font-weight: 700;
    letter-spacing: -0.02em;
    color: var(--sk-text);
}

/* The sentence F8 asked for. It names the work, not the software category —
   "start and watch QA runs" is something a reader recognises; "portal" is not. */
.sk-signin-what {
    margin: 0.75rem 0 1.75rem;
    font-size: 1rem;
    line-height: 1.6;
    color: var(--sk-text-muted);
}

.sk-signin-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.75rem 1.75rem;
    border-radius: 8px;
    background: var(--sk-accent);
    color: var(--color-on-accent, #fff);
    font-weight: 600;
    text-decoration: none;
    transition: filter 0.15s ease;
}

.sk-signin-btn:hover { filter: brightness(1.08); color: #fff; }

/* No :focus-visible rule here on purpose — bench-foundation.css ships one
   globally, which is the whole point of loading it. */

.sk-signin-note {
    margin: 1rem 0 0;
    font-size: 0.8125rem;
    color: var(--sk-text-faint);
}

/* ── Runs list: the failure reason and Re-run on the row (SparkWebReUX F1 / V1)
   Triage used to cost three clicks — a red dot on the row, the reason one
   navigation away. Scanning ten failures meant ten round trips. */
.rh-why {
    margin-top: 3px;
    font-size: 0.8125rem;
    line-height: 1.4;
    color: var(--sk-danger, #ff6b6b);

    /* One line, clamped. A stack trace here would push every other row off the
       screen, which is the opposite of what putting the reason on the row is
       for. The full text is the title attribute, and the report has all of it. */
    display: -webkit-box;
    -webkit-line-clamp: 1;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.rh-row-rerun {
    margin-left: 6px;
    /* Hidden until the row is hovered or focused-within, so a list of forty runs
       is not forty competing buttons — but focus-within keeps it reachable by
       keyboard, which display:none on hover-only would not. */
    opacity: 0;
    transition: opacity 0.12s ease;
}

.rh-row:hover .rh-row-rerun,
.rh-row:focus-within .rh-row-rerun { opacity: 1; }

/* Touch and reduced-motion users get no hover signal, so never hide it there. */
@media (hover: none), (prefers-reduced-motion: reduce) {
    .rh-row-rerun { opacity: 1; transition: none; }
}

/* The pointer that replaced the Overview's duplicate failure list (SparkWebReUX
   V3). The automation card keeps the per-project counts; the failures live in
   the attention list, which is the ranked one. */
.ov-fails-moved {
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px solid var(--sk-border);
    font-size: 0.8125rem;
    color: var(--sk-text-muted);
}

.ov-fails-moved strong { color: var(--sk-text); }

/* Roll-up signal pills, scaled by severity (SparkWebReUX V3). All five used to
   render at one size and weight, so the single red signal did not stand out
   from the four beside it — the hierarchy was flattest exactly where it needed
   to be steepest. A down signal is the reason you are reading the roll-up. */
.ov-signals .sk-pill { font-weight: 500; }
.ov-signals .sk-pill.is-error {

    font-weight: 700;
    font-size: 0.8125rem;
    /* A ring rather than a bigger box, so the row's baseline does not jump. */
    box-shadow: 0 0 0 1px var(--sk-danger, #ff6b6b);
}
.ov-signals .sk-pill.is-idle { opacity: 0.72; }

/* ── The top bar's search affordance (SparkWebReUX F6) ────────────────────────
   Replaces a link to the GitHub repository in the widest, most-seen slot in the
   app. It opens the SAME palette Ctrl+K opens — a visible affordance for it,
   not a second search. Looks like an input and behaves like a button, which is
   the standard pattern (Linear, GitHub, Slack) precisely because the thing that
   opens is a palette, not a text field. */
.sk-omnibox {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    min-width: 260px;
    max-width: 420px;
    padding: 6px 10px;
    border: 1px solid var(--sk-border);
    border-radius: 8px;
    background: var(--sk-bg-2);
    color: var(--sk-text-faint);
    font-size: 0.8125rem;
    text-align: left;
    cursor: pointer;
    transition: border-color 0.12s ease, color 0.12s ease;
}

.sk-omnibox:hover {
    border-color: var(--sk-border-strong, var(--sk-border));
    color: var(--sk-text-muted);
}

.sk-omnibox-hint { flex: 1; }

.sk-omnibox-kbd {
    font-size: 0.6875rem;
    padding: 1px 6px;
    border: 1px solid var(--sk-border);
    border-radius: 4px;
    color: var(--sk-text-faint);
    background: var(--sk-bg-3);
    white-space: nowrap;
}

/* Below ~900px the sidebar toggle and the user menu need the room more than a
   hint does. The control stays — Ctrl+K still works, and so does tapping it. */
@media (max-width: 900px) {
    .sk-omnibox { min-width: 0; }
    .sk-omnibox-hint, .sk-omnibox-kbd { display: none; }
}
