/*
 * SPDX-FileCopyrightText: 2026-present TeamCalendar
 * SPDX-License-Identifier: LicenseRef-Proprietary
 *
 * The site-wide stylesheet: tokens, reset, and the header, rail, main and
 * footer regions. What renders inside the main region lives in picker.css or
 * pages.css, so the static pages can link this one without the picker.
 */

:root {
    color-scheme: light dark;

    --ink: #16181d;
    --ink-soft: #5b6270;
    --page: #f7f7f5;
    --panel: #ffffff;
    --rule: #e2e2dd;
    /* One neutral accent, so it never competes with the league marks' colours. */
    --accent: #1d5fd8;
    /* The selected rail row. A token so dark mode can override it: a translucent
       black is invisible on a dark page. */
    --selected: #e8eaf0;

    --measure: 62rem;
    --step: 1rem;
    --radius: 0.5rem;

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

    /* A fixed height rather than a measured one, so the rail can use
       `calc(100vh - header)` without a script. Update it if the header's contents change. */
    --header-height: 3.5rem;

    /* Collapsed is about 56px: a mark plus padding, the same as the header's height. */
    --rail-width: 13rem;
    --rail-collapsed-width: 3.5rem;
}

@media (prefers-color-scheme: dark) {
    :root {
        --ink: #eceef2;
        --ink-soft: #9aa1af;
        --page: #101216;
        --panel: #191c22;
        --rule: #2b2f38;
        --accent: #7aa5ff;
        --selected: #262b34;
    }
}

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

/* Flex, not grid: under grid each region's containing block is its own row,
   which stops the sticky header and rail from moving. */
body {
    margin: 0;
    min-height: 100dvh;
    display: flex;
    flex-direction: column;
    background: var(--page);
    color: var(--ink);
    font-family: var(--sans);
    font-size: 1rem;
    line-height: 1.5;
    -webkit-text-size-adjust: 100%;
}

/* ── Region 1: the header ─────────────────────────────────────────────────── */

/* Sticky, so the rail's "viewport minus header" height stays true at every
   scroll position. */
.site-header {
    position: sticky;
    top: 0;
    z-index: 2;
    display: flex;
    flex: none;
    align-items: baseline;
    gap: 0.75rem;
    height: var(--header-height);
    padding-inline: var(--step);
    border-bottom: 1px solid var(--rule);
    background: var(--panel);
}

.wordmark {
    margin: 0;
    font-size: 1.25rem;
    font-weight: 650;
    letter-spacing: -0.03em;
}

/* The one line saying what the product does. It goes first on narrow screens to make room for the hamburger. */
.tagline {
    margin: 0;
    color: var(--ink-soft);
    font-size: 0.875rem;
}

@media (max-width: 40rem), (max-height: 30rem) {
    .tagline {
        display: none;
    }
}

/* The hamburger, pushed right. `align-self` because the header aligns on the
   text baseline and a button has none. */
.header-menu {
    display: flex;
    align-self: center;
    margin-inline-start: auto;
}

.menu-toggle {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    margin: 0;
    padding: 0;
    border: 0;
    border-radius: var(--radius);
    background: none;
    color: var(--ink);
    font: inherit;
    cursor: pointer;
}

.menu-toggle:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
}

.menu-glyph {
    font-size: 1.25rem;
    line-height: 1;
}

/* The menu panel as a desktop dropdown under the header. It is positioned
   against the sticky header, so it moves with it; the phone rule below
   re-anchors it to the viewport. */
.site-menu {
    position: absolute;
    top: var(--header-height);
    inset-inline-end: 0;
    z-index: 3;
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    width: min(22rem, 92vw);
    max-height: calc(100dvh - var(--header-height) - var(--step));
    overflow-y: auto;
    padding: var(--step);
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    background: var(--panel);
}

/* Needed because the author `display` above overrides the user agent's
   `[hidden]` rule; without it the panel is always open. */
.site-menu[hidden] {
    display: none;
}

.menu-heading {
    margin: 0 0 0.5rem;
    font-size: 0.8125rem;
    font-weight: 650;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--ink-soft);
}

.menu-entry {
    margin: 0;
}

.menu-link {
    padding: 0.375rem 0;
    color: var(--ink);
    text-decoration: none;
}

.menu-link:hover,
.menu-link:focus-visible {
    text-decoration: underline;
}

.menu-preference {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin: 0 0 0.5rem;
}

.menu-label {
    flex: none;
    color: var(--ink-soft);
    font-size: 0.875rem;
}

/* The zone list is long, so the control may shrink instead of widening the panel. */
.menu-zone {
    flex: 1;
    min-width: 0;
    font: inherit;
}

.menu-check {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.875rem;
    cursor: pointer;
}

.menu-action {
    margin: 0;
    padding: 0;
    border: 0;
    background: none;
    color: var(--accent);
    font: inherit;
    text-align: start;
    cursor: pointer;
}

.menu-status {
    margin: 0;
    color: var(--ink-soft);
    font-size: 0.8125rem;
}

/* The same panel as a right-side flyout on phones. Only positioning changes;
   the menu is never hidden on small screens. */
@media (max-width: 40rem), (max-height: 30rem) {
    .site-menu {
        position: fixed;
        inset-block: var(--header-height) 0;
        inset-inline-end: 0;
        width: min(20rem, 85vw);
        max-height: none;
        border-radius: 0;
        border-inline-start: 1px solid var(--rule);
    }
}

/* ── Regions 2 and 3: the rail and the main region ────────────────────────── */

/* Holds the rail and main region, so the rail can be sized against the viewport. */
.shell {
    flex: 1;
    display: grid;
    /* The track follows the rail's own width, so the collapsed state can live on
       the rail. `minmax(0, 1fr)` stops wide content in main from pushing the column
       past the viewport. */
    grid-template-columns: auto minmax(0, 1fr);
    align-items: start;
}

/* The frame does not scroll; `.rail-list` inside it does. */
.rail {
    position: sticky;
    top: var(--header-height);
    height: calc(100dvh - var(--header-height));
    width: var(--rail-width);
    display: flex;
    flex-direction: column;
    overflow: hidden;
    border-inline-end: 1px solid var(--rule);
}

/* Collapsed state as an attribute value. Unset means "not decided yet", which
   must differ from "expanded" so the phone default below doesn't override a
   visitor who expanded the rail. Both rules below outrank that default. */
.rail[data-rail-state="expanded"] {
    width: var(--rail-width);
}

.rail[data-rail-state="collapsed"] {
    width: var(--rail-collapsed-width);
}

.rail-list {
    flex: 1;
    overflow-y: auto;
    list-style: none;
    margin: 0;
    padding: 0.5rem;
}

.rail-row {
    display: flex;
    align-items: center;
}

/* The row is a real `/{league}` link that the click handler intercepts. The
   padding is here so the whole row is the hit target. */
.rail-link {
    flex: 1;
    display: flex;
    min-width: 0;
    align-items: center;
    gap: 0.625rem;
    padding: 0.5rem 0.625rem;
    /* The site's radius, matching every other filled block. */
    border-radius: var(--radius);
    color: inherit;
    text-decoration: none;
}

/* `min-width: 0` lets a long name ellipsize instead of widening the row. */
.rail-text {
    display: flex;
    min-width: 0;
    flex-direction: column;
    gap: 0.0625rem;
}

/* The visitor's own pick count, in the accent. Empty for leagues with no picks,
   and an empty span takes no line. */
.rail-meta {
    overflow: hidden;
    color: var(--accent);
    font-size: 0.75rem;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.rail-meta:empty {
    display: none;
}

/* The selected row, keyed off `aria-current`. Fill plus weight, so it doesn't
   rely on colour alone. The weight is set here and inherited rather than set on
   `.rail-name`, because `styles.mjs#declarations` would find that rule first. */
.rail-link[aria-current="page"] {
    background: var(--selected);
    font-weight: 600;
}

.rail-link:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
}

.rail-mark {
    flex: none;
    width: 28px;
    height: 28px;
    object-fit: contain;
}

/* One line, clipped: the frame must not scroll, so a long name costs width,
   never height. */
.rail-name {
    overflow: hidden;
    color: var(--ink);
    font-size: 0.875rem;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.rail-status {
    margin: 0;
    padding: 0.5rem;
    overflow: hidden;
    color: var(--ink-soft);
    font-size: 0.875rem;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Outside the list so it stays put while the list scrolls. `flex: none` after
   the `flex: 1` list pins it to the bottom. */
.rail-toggle {
    flex: none;
    display: flex;
    align-items: center;
    justify-content: center;
    height: 2.25rem;
    margin: 0;
    padding: 0;
    border: 0;
    border-top: 1px solid var(--rule);
    background: none;
    color: var(--ink-soft);
    font: inherit;
    cursor: pointer;
}

.rail-toggle:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
}

.rail-toggle-mark {
    font-size: 1.25rem;
    line-height: 1;
}

.site-main {
    /* A grid item's minimum is also its content, so it needs the same guard. */
    min-width: 0;
    max-width: var(--measure);
    padding: calc(var(--step) * 2) var(--step);
}

/* ── The step bar and the three phone steps ─────────────────────────────── */

/* Hidden above the phone breakpoint, where all three regions are on screen
   at once. `display: none` so it takes no space and isn't announced. */
.step-bar {
    display: none;
    grid-column: 1 / -1;
}

.steps {
    display: flex;
    list-style: none;
    margin: 0;
    padding: 0;
    border-bottom: 1px solid var(--rule);
    background: var(--panel);
}

.step {
    flex: 1;
}

.step-link {
    width: 100%;
    /* 44px minimum touch target. */
    min-height: 2.75rem;
    padding: 0.5rem;
    border: 0;
    background: none;
    color: var(--ink-soft);
    font: inherit;
    font-size: 0.875rem;
    cursor: pointer;
}

/* The current step, marked by weight and a rule so it doesn't rely on colour. */
.step-link[aria-current="step"] {
    color: var(--ink);
    font-weight: 600;
    box-shadow: inset 0 -2px 0 0 var(--accent);
}

.step-link:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
}

/* ── One region at a time, on a phone ─────────────────────────────────────── */

/* On a phone only the region named by `body[data-step]` is shown.
   `picker.js#showStep` sets it; without script every region stays visible. */
@media (max-width: 40rem), (max-height: 30rem) {
    .step-bar {
        display: block;
    }

    /* One region at a time, so no columns. A grid with a hidden track still sizes
       the other track to its content. */
    .shell {
        display: block;
    }

    /* Step 1 is the rail, full width with names, scrolling with the page. The
       collapse control has no use here. */
    body[data-step="1"] .rail {
        position: static;
        width: 100%;
        height: auto;
        overflow: visible;
    }

    body[data-step="1"] .rail-list {
        overflow-y: visible;
    }

    body[data-step="1"] .rail-toggle {
        display: none;
    }

    /* One rule per step so tests can name the step they check. */
    /* Half the vertical padding, which matters on a rotated phone. */
    .site-main {
        padding-block: var(--step);
    }

    body[data-step="2"] .rail {
        display: none;
    }

    body[data-step="3"] .rail {
        display: none;
    }
}

/* ── Region 4: the footer ─────────────────────────────────────────────────── */

/* Four link columns, then social links and the copyright. Here rather than in
   pages.css because the layout and the static pages share it. */
.site-footer {
    flex: none;
    border-top: 1px solid var(--rule);
    padding: calc(var(--step) * 1.5) var(--step);
    color: var(--ink-soft);
    font-size: 0.875rem;
}

.site-footer p {
    margin: 0;
}

/* `auto-fit` reflows the columns without another breakpoint. */
.footer-columns {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
    gap: var(--step) calc(var(--step) * 2);
    max-width: var(--measure);
}

.footer-heading {
    margin: 0 0 0.5rem;
    color: var(--ink);
    font-size: 0.8125rem;
    font-weight: 650;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

.footer-links,
.footer-social {
    display: flex;
    flex-direction: column;
    gap: 0.375rem;
    list-style: none;
    margin: 0;
    padding: 0;
}

.footer-social {
    flex-direction: row;
    gap: var(--step);
}

/* The Supported Leagues column, filled by `footer.js` from `/api/leagues`. */
.footer-league {
    display: flex;
    align-items: center;
}

/* Each entry is a `/{league}` link. Flex is on the anchor so the mark and
   name are one target. */
.footer-league-link {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.footer-mark {
    flex: none;
    width: 20px;
    height: 20px;
    object-fit: contain;
}

/* Shown before the leagues load, if the request fails, and without JavaScript. */
.footer-status {
    color: var(--ink-soft);
}

.site-footer a {
    color: inherit;
    text-decoration: none;
}

.site-footer a:hover,
.site-footer a:focus-visible {
    color: var(--ink);
    text-decoration: underline;
}

.footer-base {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.5rem var(--step);
    max-width: var(--measure);
    margin-top: calc(var(--step) * 1.5);
    padding-top: var(--step);
    border-top: 1px solid var(--rule);
}

.footer-copyright {
    font-size: 0.8125rem;
}

/* ── The ad slot ──────────────────────────────────────────────────────────── */

/* The ad slot `ads.js` adds before the footer on pages without a capability.
   Here because both page families share it. */
.ad-slot {
    /* Takes the height of its content and never grows into the main region's space. */
    flex: none;
    width: 100%;
    max-width: var(--measure);
    margin: 0 auto;
    padding: var(--step);
}

/* Collapsed while empty, so it takes no space and announces no empty landmark. */
.ad-slot:empty {
    display: none;
}

/* ── Shared ───────────────────────────────────────────────────────────────── */

/* The wordmark is a link on the static pages and plain text on the picker;
   one rule keeps both in step. */
.wordmark-link {
    color: inherit;
    text-decoration: none;
}

/* Visible to screen readers only. Clip-based, since `display: none` would hide
   it from them too. The footer heading and contact page labels use it. */
.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
    }
}
