/* ============================================================================
   sossenbruder-football — design-system foundation
   The Verge-inspired dark editorial theme. Full spec lives in the design system
   doc (../sossenbruder-wm/docs/design.md); this file wires up the tokens, fonts,
   and canvas every page builds on. The league is a round-robin table — no groups,
   no bracket — so the WC-specific bracket / countdown / join-board CSS is dropped.
   ============================================================================ */

/* --- Self-hosted fonts (latin subset, woff2) -------------------------------
   Free substitutes for the Verge's commercial faces (design spec §Fonts):
   Anton → display, Space Grotesk → UI/body, Space Mono → uppercase labels.
   Self-hosted, not hotlinked from Google's CDN.                              */

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

/* Space Grotesk is a variable font (weight axis 300–700) — one file covers the
   full 300/400/500/700 range the design spec uses, so it's a single @font-face. */
@font-face {
    font-family: 'Space Grotesk';
    font-style: normal;
    font-weight: 300 700;
    font-display: swap;
    src: url('../fonts/space-grotesk.woff2') format('woff2');
}

@font-face {
    font-family: 'Space Mono';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('../fonts/space-mono-400.woff2') format('woff2');
}

@font-face {
    font-family: 'Space Mono';
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url('../fonts/space-mono-700.woff2') format('woff2');
}

/* --- Design tokens --------------------------------------------------------- */

:root {
    /* Surfaces */
    --canvas: #131313;
    --surface-slate: #2d2d2d;
    --image-frame: #313131;

    /* Hazard accents */
    --mint: #3cffd0;
    --mint-border: #309875;
    --ultraviolet: #5200ff;
    --purple-rule: #3d00bf;

    /* Tile accent — the "goal difference / partial" points pill (design spec §2) */
    --tile-yellow: #ffd84d;

    /* Live-match state — the one deliberate red in the palette, reserved exclusively for a running
       game (LIVE tag/ring/accent). See the design spec §Inputs & forms. */
    --live: #e5484d;

    /* Standings zone rails — the European-league convention mapped onto this palette. A football
       table would normally paint the drop zone red, but --live is reserved for a running match, so
       the bottom two take a burnt orange that can't be mistaken for a live game. Consumed only by
       Standing.razor.css; declared here because a scoped sheet can't define :root. */
    --zone-cl: var(--mint);
    --zone-europe: #4a8fe7;
    --zone-playoff: var(--tile-yellow);
    --zone-drop: #e0603f;

    /* Interaction */
    --link-hover: #3860be;
    --focus-cyan: #1eaedb;

    /* Text */
    --text: #ffffff;
    --text-secondary: #949494;
    --text-muted: #e9e9e9;
    --text-inverted: #131313;

    /* Type families (fallback stacks per the design spec) */
    --font-display: 'Anton', Impact, sans-serif;
    --font-ui: 'Space Grotesk', Helvetica, Arial, sans-serif;
    --font-mono: 'Space Mono', 'Courier New', monospace;

    /* Border-radius scale — the seven length values (the 8th, 50% for
       avatars/icon buttons, is applied inline since it isn't a length token) */
    --radius-input: 2px;
    --radius-inline-img: 3px;
    --radius-nested: 4px;
    --radius-tile: 20px;
    --radius-feature: 24px;
    --radius-promo: 30px;
    --radius-cta: 40px;
}

/* --- Reset + canvas -------------------------------------------------------- */

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

html,
body {
    margin: 0;
    min-height: 100vh;
    background-color: var(--canvas);
    color: var(--text);
    font-family: var(--font-ui);
    font-size: 16px;
    line-height: 1.6;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
}

::selection {
    background: var(--mint);
    color: var(--text-inverted);
}

/* Headlines are Space Grotesk by default — Anton is opt-in via .hero only,
   never the automatic heading face (design spec: "Anton is the hero, never UI"). */
h1, h2, h3, h4, h5, h6 {
    font-family: var(--font-ui);
    font-weight: 700;
    line-height: 1;
    margin: 0;
}

p {
    margin: 0;
}

a {
    color: var(--text);
    text-decoration: none;
    transition: color 150ms ease;
}

a:hover {
    color: var(--link-hover);
}

:focus-visible {
    outline: 2px solid var(--focus-cyan);
    outline-offset: 2px;
}

/* FocusOnNavigate moves focus to the page <h1> for screen-reader announcement
   on every navigation. The heading isn't interactive, so suppress the visible
   ring — focus-cyan is reserved for actual controls (design spec §Color). */
h1:focus,
h1:focus-visible {
    outline: none;
}

/* --- Blazor error bar (kept minimal; the framework toggles its display) ----- */
#blazor-error-ui {
    background: var(--ultraviolet);
    bottom: 0;
    box-sizing: border-box;
    color: var(--text);
    display: none;
    left: 0;
    padding: 0.6rem 1.25rem 0.7rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: 1000;
}

#blazor-error-ui .dismiss {
    cursor: pointer;
    position: absolute;
    right: 0.75rem;
    top: 0.5rem;
}

#blazor-error-ui .reload {
    color: var(--text);
    text-decoration: underline;
}

/* --- Design-system components ----------------------------------------------
   The shared editorial primitives from the design spec §3–4: kickers, tags,
   points pills, buttons, the StoryStream timestamp. Global because they're the
   system's vocabulary, reused across the participant pages (the fixtures feed,
   the results board, the leaderboard). Page-specific layout stays in scoped
   component CSS; these are the pieces those pages compose.                    */

/* Thin-weight capitalized "fashion-whisper" eyebrow (design spec §3). */
.eyebrow {
    font-family: var(--font-ui);
    font-size: 1.19rem;
    font-weight: 300;
    letter-spacing: 1.9px;
    line-height: 1.2;
    color: var(--text);
    margin: 0 0 10px;
}

/* UPPERCASE mono kicker above a headline — mint by default (design spec §3). */
.kicker {
    font-family: var(--font-mono);
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 1.8px;
    text-transform: uppercase;
    color: var(--mint);
}

/* Anton content hero (≥60px — "Anton below 60px is a bug"). */
.hero {
    font-family: var(--font-display);
    font-weight: 400;
    font-size: clamp(3.5rem, 8vw, 6.69rem);
    line-height: 0.95;
    letter-spacing: 1.07px;
    color: var(--text);
}

/* Space Grotesk module/section headline (design spec §3 "Large headline"). */
.section-head {
    font-family: var(--font-ui);
    font-size: 2.13rem;
    font-weight: 700;
    line-height: 1;
    margin: 0 0 20px;
}

.lede {
    margin-top: 24px;
    color: var(--text-muted);
    font-size: 16px;
    line-height: 1.6;
    max-width: 60ch;
}

/* StoryStream timestamp — mono uppercase, on the rail (design spec §3). */
.timestamp {
    font-family: var(--font-mono);
    font-size: 11px;
    font-weight: 400;
    letter-spacing: 1.1px;
    text-transform: uppercase;
    color: var(--text-secondary);
}

/* Pill tags (design spec §4 "Pill tag"): saturated accent, mono uppercase. */
.tag {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-family: var(--font-mono);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 1.5px;
    text-transform: uppercase;
    border-radius: var(--radius-tile);
    padding: 4px 12px;
}

.tag-mint { background: var(--mint); color: #000; }
.tag-slate { background: var(--surface-slate); color: var(--text-muted); }
.tag-ghost { background: transparent; color: var(--text-secondary); border: 1px solid var(--image-frame); }
/* A running game: LIVE (red, pulsing) / HT half-time (yellow). */
.tag-live { background: var(--live); color: #fff; }
.tag-ht { background: var(--tile-yellow); color: #000; }
.tag-live::before {
    content: "";
    display: inline-block;
    width: 6px; height: 6px;
    margin-right: 2px;
    border-radius: 50%;
    background: #fff;
    animation: live-pulse 1.4s ease-in-out infinite;
}
@keyframes live-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .25; } }
@media (prefers-reduced-motion: reduce) { .tag-live::before { animation: none; } }

/* Result-source badge on a settled result: amber while it's the raw feed value (auto),
   mint once an admin has confirmed it. */
.result-badge {
    display: inline-block;
    margin-left: 8px;
    font-family: var(--font-mono);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 1px;
    text-transform: none;  /* keep the authored lowercase; don't inherit the kicker's uppercase */
    border-radius: var(--radius-tile);
    padding: 2px 7px;
    vertical-align: middle;
}

.result-badge.auto { background: var(--tile-yellow); color: #000; }
.result-badge.confirmed { background: var(--mint); color: #000; }

/* Points pill — a graded prediction's outcome. Shared by the settled fixture tile,
   the results board, and the leaderboard's per-source breakdown. */
.pts {
    display: inline-flex;
    align-items: center;
    font-family: var(--font-mono);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 1.3px;
    text-transform: uppercase;
    border-radius: var(--radius-tile);
    padding: 5px 12px;
    white-space: nowrap;
}

.pts.win { background: var(--mint); color: #000; }
.pts.part { background: var(--tile-yellow); color: #000; }
.pts.miss { background: var(--ultraviolet); color: #fff; }
/* A call that's neither won nor lost yet — a phase still awaiting its reveal gate. */
.pts.pending { background: transparent; border: 1px solid var(--image-frame); color: var(--text-secondary); }

/* Back link — a small mono uppercase line at the top of a sub-page, shared by the match detail and
   the standings page. The arrow is a CSS pseudo-element so the label stays a plain localised string. */
.back {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-bottom: 24px;
    font-family: var(--font-mono);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 1.5px;
    text-transform: uppercase;
    color: var(--text-secondary);
    text-decoration: none;
}

.back::before {
    content: '';
    display: inline-block;
    width: 14px;
    height: 14px;
    flex-shrink: 0;
    background: currentColor;
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M19 12H5'/%3E%3Cpath d='M12 19l-7-7 7-7'/%3E%3C/svg%3E") center/contain no-repeat;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M19 12H5'/%3E%3Cpath d='M12 19l-7-7 7-7'/%3E%3C/svg%3E") center/contain no-repeat;
}

.back:hover {
    color: var(--mint);
}

/* Buttons (design spec §4). Mint primary, slate secondary. */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    border: none;
    font-family: var(--font-mono);
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 1.5px;
    text-transform: uppercase;
    transition: background 180ms ease, color 150ms ease, box-shadow 180ms ease;
}

.btn-primary {
    background: var(--mint);
    color: #000;
    border-radius: var(--radius-feature);
    padding: 12px 26px;
}

.btn-primary:hover:not(:disabled) {
    background: rgba(255, 255, 255, 0.2);
    color: #000;
    box-shadow: 0 0 0 1px #c2c2c2;
}

/* Disabled (e.g. an invalid form held back by client-side validation) — greyed, not clickable. */
.btn:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}

.btn-secondary {
    background: var(--surface-slate);
    color: var(--text-muted);
    border-radius: var(--radius-feature);
    padding: 12px 26px;
    font-family: var(--font-ui);
    font-size: 14px;
    letter-spacing: 0.3px;
    text-transform: none;
}

.btn-secondary:hover {
    background: rgba(255, 255, 255, 0.2);
    color: #000;
}

/* --- Saved-then-edit controls ------------------------------------------------
   A prediction that's already been saved reads back as a value, not a live form:
   the save button gives way to a small pencil, and editing offers a quiet way
   back out. Shared by the match page and the table-ranking editor, so these live
   here rather than in either page's scoped sheet. */

/* A full 44px touch target; callers negative-margin it when it would otherwise
   stretch the row it sits in. */
.edit-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 44px;
    height: 44px;
    padding: 0;
    background: transparent;
    border: 1px solid var(--image-frame);
    border-radius: var(--radius-input);
    color: var(--text-secondary);
    cursor: pointer;
    transition: color 0.15s ease, border-color 0.15s ease;
}

.edit-btn:hover {
    color: var(--mint);
    border-color: var(--mint-border);
}

.edit-icon {
    width: 18px;
    height: 18px;
}

/* Text button, so leaving edit mode never competes with the mint save CTA. */
.cancel-btn {
    display: block;
    width: 100%;
    min-height: 44px;
    margin-top: 4px;
    background: transparent;
    border: 0;
    font-family: var(--font-mono);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 1.5px;
    text-transform: uppercase;
    color: var(--text-secondary);
    cursor: pointer;
}

.cancel-btn:hover {
    color: var(--text);
}

/* ============================================================================
   Shared page layout — promoted from the former component-scoped Home.razor.css.
   Blazor CSS isolation had confined these to Home only, so the pages built on
   them (Games / Overview / Standing / Score / Game / UserHistory / PredictPhase /
   Admin*) rendered unstyled. They belong to the whole system's vocabulary, so
   they live here now. Rules moved verbatim; the leader accent is retargeted to
   the position cell (which carries no class), and the Home-only notes / head-row
   / board-error / standings scaffolding that no page references was dropped.
   Dark canvas, 1px hairlines (never shadows), pill-cornered tiles (design spec §5).
   ============================================================================ */

/* --- Identity hero ---------------------------------------------------------- */
.identity {
    padding: 8px 0 8px;
    margin-bottom: 56px;
}

.identity .kicker {
    margin: 4px 0 14px;
}

/* A feed / results / predictions section — paced with generous whitespace above. */
.feed {
    margin-top: 56px;
}

/* --- StoryStream fixtures timeline ------------------------------------------ */
.stream {
    list-style: none;
    margin: 0;
    padding: 0;
    /* Base: a plain vertical stack with gap — used generically as a nav/card/link/CTA container
       (overview tiles, league/season pickers, back links). */
    display: flex;
    flex-direction: column;
    gap: 12px;
}

/* Only the fixtures timeline (which hangs .stream-item entries off a spine) gets the dashed rail
   (design spec §Cards → StoryStream rail): a 1px purple rule with a mint node per entry. The
   generic uses above have no .stream-item, so they stay rail-less. */
.stream:has(.stream-item) {
    gap: 0;
    margin-left: 8px;
}

.stream-item {
    display: grid;
    grid-template-columns: 120px 1fr;
    gap: 16px;
    align-items: start;
    padding: 0 0 16px 20px;
    border-left: 1px dashed var(--purple-rule);
}

.stream-item:last-child {
    padding-bottom: 0;
}

/* Timestamp sits on the rail. A small mint node marks each entry's point on the spine. */
.rail {
    position: relative;
    padding-top: 20px;
}

.rail::before {
    content: "";
    position: absolute;
    left: -25px;
    top: 24px;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--mint);
}

.tile {
    background: var(--canvas);
    border: 1px solid var(--image-frame);
    border-radius: var(--radius-tile);
    padding: 18px 22px;
}

/* A running game breaks the rhythm with the one deliberate red — a 1px live ring, no fill wash. */
.tile-live {
    border-color: var(--live);
}

.tile-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 14px;
}

.tile-head:last-child {
    margin-bottom: 0;
}

.matchup {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: 14px;
    margin-bottom: 14px;
}

.team {
    display: flex;
    align-items: center;
    gap: 12px;
    min-width: 0;
}

.team-away {
    justify-content: flex-end;
}

.team-name {
    font-family: var(--font-ui);
    font-weight: 700;
    font-size: 1.25rem;
    line-height: 1.1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.team-name-short {
    display: none;
    font-family: var(--font-mono);
    font-weight: 700;
    font-size: 1rem;
    letter-spacing: 0.5px;
    cursor: default;
}

.score {
    font-family: var(--font-mono);
    font-weight: 700;
    font-size: 1rem;
    letter-spacing: 1px;
    color: var(--text-secondary);
    white-space: nowrap;
}

.score-live {
    color: var(--live);
    font-size: 1.25rem;
}

.vs {
    font-size: 11px;
    letter-spacing: 1.5px;
    text-transform: uppercase;
}

/* --- Standings / results table ---------------------------------------------- */
/* Horizontal scroll on narrow viewports so the table never breaks the page layout. */
.table-scroll {
    overflow-x: auto;
    border: 1px solid var(--image-frame);
    border-radius: var(--radius-tile);
}

.table {
    width: 100%;
    min-width: 620px;
    border-collapse: collapse;
    background: var(--surface-slate);
}

.table th,
.table td {
    padding: 12px 10px;
    text-align: center;
    border-bottom: 1px solid var(--image-frame);
    white-space: nowrap;
}

.table tbody tr:last-child td {
    border-bottom: none;
}

/* Mono uppercase column labels (design spec §3). */
.table th {
    font-family: var(--font-mono);
    font-weight: 700;
    font-size: 11px;
    letter-spacing: 1.3px;
    text-transform: uppercase;
    color: var(--text-secondary);
}

.table td {
    font-family: var(--font-mono);
    font-size: 13px;
    color: var(--text-muted);
}

.col-club {
    text-align: left;
}

.col-pts {
    color: var(--text);
    font-weight: 700;
}

.club {
    display: inline-flex;
    align-items: center;
    gap: 10px;
}

.club-name {
    font-family: var(--font-ui);
    font-weight: 500;
    font-size: 14px;
    letter-spacing: 0.2px;
    color: var(--text);
    text-transform: none;
}

/* The leader takes a mint accent — the top of the table as the single high-attention row.
   The position number is the first cell (no dedicated class), so it carries the mint. */
.table tbody tr.leader td {
    background: rgba(60, 255, 208, 0.08);
}

.table tbody tr.leader td:first-child {
    color: var(--mint);
    font-weight: 700;
}

/* Single-column stream layout — no rail dots, short team names in tiles. */
.stream-item {
    grid-template-columns: 1fr;
    gap: 8px;
}

.rail {
    padding-top: 0;
}

.rail::before {
    display: none;
}

.tile .team-name {
    display: none;
}

.tile .team-name-short {
    display: block;
}

/* ============================================================================
   Inputs & forms — the onboarding / profile / predict / admin pages compose bare
   native controls on the dark canvas, so give them the theme's field treatment
   (design spec §Inputs & forms): dark slate surface, 1px hairline, mint focus
   accent, mono for numeric entry. Elements are styled directly so the existing
   markup gets themed for free — no new class names the pages don't already use.
   ============================================================================ */

/* Field surface. `input:not([type])` catches Blazor's <InputText> and the bare
   AdminImport search box, both of which render <input> without an explicit type. */
input[type="text"],
input[type="search"],
input[type="number"],
input:not([type]),
select,
textarea {
    font-family: var(--font-ui);
    font-size: 14px;
    color: var(--text);
    background: var(--surface-slate);
    border: 1px solid var(--image-frame);
    border-radius: var(--radius-input);
    padding: 8px 12px;
    line-height: 1.4;
    transition: border-color 150ms ease, box-shadow 150ms ease;
}

/* Text fields breathe to their container; cap so they don't sprawl on wide pages. */
input[type="text"],
input[type="search"],
input:not([type]),
textarea {
    width: 100%;
    max-width: 360px;
}

/* Numeric entry (scorelines, ranking positions) is mono and compact — a couple of digits. */
input[type="number"] {
    font-family: var(--font-mono);
    width: 4.5rem;
    padding: 6px 8px;
    text-align: center;
}

textarea {
    min-height: 96px;
    resize: vertical;
}

/* Mint focus accent — the field lights up on focus, replacing the default control ring. */
input[type="text"]:focus,
input[type="search"]:focus,
input[type="number"]:focus,
input:not([type]):focus,
select:focus,
textarea:focus {
    outline: none;
    border-color: var(--mint);
    box-shadow: 0 0 0 2px rgba(60, 255, 208, 0.25);
}

input:disabled,
select:disabled,
textarea:disabled {
    opacity: 0.45;
    cursor: not-allowed;
}

/* Radio inputs (the Stay/Drop relegation bet) keep native rendering, just the mint tick. */
input[type="radio"] {
    accent-color: var(--mint);
    margin-right: 8px;
}

/* Labels get the mono uppercase treatment where the markup opts in via .timestamp;
   this only handles layout — stack the label above its field with a little breathing room. */
label {
    display: block;
    margin-bottom: 6px;
}

/* Vertical rhythm between stacked label+field groups (both plain divs and EditForm forms). */
.identity > div,
.identity form > div {
    margin-bottom: 18px;
}

/* The action row under a guard panel or a form (Initial / Profile / Predict / Admin). */
.guard-actions {
    margin-top: 28px;
}

/* The permission-wall / awaiting-approval guard section — a quiet hero column. */
.guard {
    padding: 8px 0 8px;
    margin-bottom: 56px;
}

/* Visually-hidden but screen-reader-available (a11y — accessible names / off-screen headings). */
.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* The router-level error screen (App.razor's ErrorBoundary). Standalone by design — it renders
   outside MainLayout so it can't depend on anything that might itself be failing — so it carries its
   own page padding instead of inheriting .content's. */
.error-screen {
    max-width: 1300px;
    margin: 0 auto;
    padding: 48px 24px;
}

/* --- Boot spinner (index.html, before Blazor replaces #app) ---------------- */
.loading-boot {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
}

.loading-boot-ring {
    width: 34px;
    height: 34px;
    border: 2px solid var(--image-frame);
    border-top-color: var(--mint);
    border-radius: 50%;
    animation: loading-boot-spin 720ms linear infinite;
}

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

@media (prefers-reduced-motion: reduce) {
    .loading-boot-ring {
        animation: loading-boot-pulse 1.4s ease-in-out infinite;
    }

    @keyframes loading-boot-pulse {
        0%, 100% { opacity: 0.35; }
        50% { opacity: 1; }
    }
}
