/* Named patterns: stat table scroll region, leaders strip, player identity,
   style guide scaffolding. No cards: the page is one sheet of paper, ruled.
   Family hues take the rules themselves — the double rule under a family
   table's header, the section rule above a family heading, a tile's label
   (see docs/agents/styling.md and CONTEXT.md, "Family hue"). */

@layer components {
    /* Orientation copy: the deck under a reference page's headline. */
    .page-intro {
        color: var(--text-muted);
        max-inline-size: var(--measure-prose);
    }

    .prose p {
        max-inline-size: var(--measure-prose);
    }

    /* The StatTable scroll region: plain, on the paper itself. */
    .table-scroll {
        background:
            linear-gradient(to left, var(--bg), transparent var(--space-xl)) 100% 0 / var(--space-xl) 100% no-repeat local,
            radial-gradient(farthest-side at 100% 50%, var(--scroll-shadow), transparent) 100% 0 / var(--space-sm) 100% no-repeat scroll;
        margin-block: var(--space-sm);
        max-inline-size: 100%;
        overflow-x: auto;
    }

    .table-scroll table {
        margin-block: 0;
    }

    /* Sticky identity column: opaque paper, seam border. The header cell gets
       the same paper — editorial headers have no fill to inherit. */
    .table-scroll th:first-child,
    .table-scroll td:first-child {
        background: var(--surface);
        border-inline-end: 1px solid var(--border-strong);
        inset-inline-start: 0;
        position: sticky;
        z-index: 1;
    }

    /* The two alignment deviations from base.css, marked on whichever cells of a
       column base.css gets wrong — usually the header and every cell under it.
       `.text` is a column of prose in a stat table — an opponent, a venue, a status,
       a position — which reads from the start like the identity column does.
       `.numeric` is the inverse, for the rare table whose first column is a figure
       rather than a name: there only the header needs it, since its cells already
       end where the column does. */
    :is(th, td).text {
        text-align: start;
    }

    :is(th, td).numeric {
        text-align: end;
    }

    /* Family coding: the double rule under a family table's header is set in
       the family's ink. */
    table.batting thead th {
        border-block-end-color: var(--batting-accent);
    }

    table.pitching thead th {
        border-block-end-color: var(--pitching-accent);
    }

    /* The break in a schedule where one phase of the season ends and the next begins
       (ADR-0012, #216). It has to read as a rule drawn across the schedule rather than as
       another game, so it takes the firm rule the scorecard closes on and the raised paper
       the page uses for a notice, with the label at the start under the dates it divides.
       Muted and a step up from agate: it is context for the rows below, not a figure among
       them.

       It out-specifies the sticky identity column above deliberately. The cell spans the
       whole table, so being pinned to the leading edge is a no-op — but the seam border
       that rule draws would land at the far end of the break as a stray vertical rule. */
    .table-scroll tr.phase-divider td {
        background: var(--surface-alt);
        border-block-start: 2px solid var(--border-strong);
        border-inline-end: none;
        color: var(--text-muted);
        font-size: var(--text-sm);
        position: static;
        text-align: start;
    }

    /* Family coding on headings: the section rule takes the family ink. h2
       already carries a rule from base; h1 (the leaderboard headline) gets
       one here. */
    h1.batting,
    h1.pitching {
        border-block-start: 3px solid var(--batting-accent);
        padding-block-start: var(--space-xs);
    }

    h1.pitching {
        border-block-start-color: var(--pitching-accent);
    }

    h2.batting {
        border-block-start-color: var(--batting-accent);
    }

    h2.pitching {
        border-block-start-color: var(--pitching-accent);
    }

    /* The home page's league-leaders strip: ruled boxes, headline numerals. */
    .leaders-strip ul {
        display: grid;
        gap: var(--space-xs);
        grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
        list-style: none;
        margin: 0;
        padding: 0;
    }

    .leaders-strip li {
        background: var(--surface);
        border: 1px solid var(--border-strong);
        display: flex;
        flex-direction: column;
        gap: var(--space-3xs);
        padding: var(--space-xs) var(--space-sm);
    }

    .leaders-strip li a:first-child {
        color: var(--text-muted);
        font-size: var(--text-sm);
        font-variant-caps: all-small-caps;
        font-weight: 600;
        letter-spacing: 0.05em;
        text-decoration: none;
    }

    .leaders-strip li.batting a:first-child {
        color: var(--batting-accent);
    }

    .leaders-strip li.pitching a:first-child {
        color: var(--pitching-accent);
    }

    /* The team she plays for: a side note under her name, quiet enough that the
       tile still reads label, name, figure (#70). */
    .leaders-strip li small {
        color: var(--text-muted);
        font-size: var(--text-sm);
    }

    /* The value is a headline: display face, print numerals. */
    .leaders-strip li span {
        font-family: var(--font-display);
        font-size: var(--text-h2);
        font-weight: 700;
    }

    /* The player page's identity block. */
    .player-identity {
        display: flex;
        flex-wrap: wrap;
        align-items: flex-start;
        gap: var(--space-md);
    }

    .player-identity > div {
        flex: 1 1 20rem;
    }

    .player-identity h1 {
        margin-block-start: var(--space-sm);
    }

    .portrait {
        border: 1px solid var(--border-strong);
        block-size: 9rem;
        inline-size: 9rem;
        object-fit: cover;
    }

    .portrait-empty {
        align-items: center;
        background: var(--surface-alt);
        color: var(--text-muted);
        display: flex;
        font-family: var(--font-display);
        font-size: var(--text-h1);
        justify-content: center;
    }

    /* A club's mark, hotlinked from the league's CDN (#74). One size for every
       placement, so a page fetches one file per club however often it appears; the
       chip is what a missing or withdrawn logo falls back to, at the same height so a
       row keeps its rhythm when one goes. */
    .crest {
        background: var(--crest-bg);
        block-size: 1.75rem;
        border-radius: var(--space-2xs);
        flex: none;
        inline-size: 1.75rem;
        object-fit: contain;
        /* The marks are drawn to their own edges; a hair of plate around them keeps a
           square logo from reading as a cropped one. */
        padding: var(--space-3xs);
    }

    /* The chip is square where the mark is, and grows sideways rather than clipping a
       three-letter abbreviation into a two-letter box. */
    .crest-empty {
        align-items: center;
        color: var(--crest-text);
        display: flex;
        font-family: var(--font-display);
        font-size: var(--text-sm);
        inline-size: auto;
        justify-content: center;
        line-height: 1;
        min-inline-size: 1.75rem;
        padding-inline: var(--space-2xs);
    }

    /* A heading a club's mark leads: the h2's section rule still spans the page,
       because the flex container is the heading itself and not a wrapper round it. */
    .team-heading {
        align-items: center;
        display: flex;
        gap: var(--space-xs);
    }

    /* The mark beside a team's name inside a table cell. Inline-flex, so the cell
       stays a cell — a flex `th` detaches from the table's column widths. */
    .team-cell {
        align-items: center;
        display: inline-flex;
        gap: var(--space-xs);
    }

    /* Agate rows are shorter than the standing chip, so the standings mark comes down
       to the row's own height and the chip's letters to the row's own size. */
    .team-cell .crest {
        block-size: 1.25rem;
        inline-size: 1.25rem;
    }

    .team-cell .crest-empty {
        font-size: var(--text-stat);
        inline-size: auto;
        min-inline-size: 1.25rem;
    }

    .socials {
        display: flex;
        flex-wrap: wrap;
        gap: var(--space-xs) var(--space-md);
        font-size: var(--text-sm);
        list-style: none;
        margin-block: var(--space-xs);
        padding: 0;
    }

    .player-bio {
        max-inline-size: var(--measure-prose);
    }

    .source-credit {
        color: var(--text-muted);
        font-size: var(--text-sm);
        max-inline-size: var(--measure-prose);
    }

    /* The page-level disclosure that some figures here are the league's own (ADR-0011).
       The site's only disclosure now (#171), and it is met before every number on the page
       rather than under one table, because it is the contract the site offers in place of
       having proved them. So it takes body size on a raised surface, with the accent rule
       down its leading edge that is the one interactive colour this site spends — not the
       credit-line weight the per-table notes it replaced were set in. Prose measure like
       every other running sentence, and no negative pull: nothing above it is a scrolling
       table. */
    .page-notice {
        background: var(--surface-alt);
        border-inline-start: var(--space-3xs) solid var(--accent);
        margin-block: var(--space-md);
        max-inline-size: var(--measure-prose);
        padding: var(--space-xs) var(--space-sm);
    }

    /* The score, above the box score that explains it. This is the one number on the
       page a reader may have come for, so the two totals face off at headline size —
       display face, print numerals — with each club's mark above its total and its
       name beneath, the shape a fixture card gives a result. The band of raised
       surface is what ends the header: everything on it is the game's headline,
       everything after it is the record. Centered, the one thing on a left-aligned
       page that is — the deviation is what makes it read as the headline rather
       than another context line. */
    .scoreboard {
        background: var(--surface-alt);
        margin-block: var(--space-md);
        padding: var(--space-md);
        text-align: center;
    }

    .scoreboard ul {
        display: grid;
        grid-template-columns: 1fr 1fr;
        list-style: none;
        margin: 0 auto;
        padding: 0;
        width: fit-content;
    }

    /* Mark, total, name down one centered column a side; the hairline is the seam
       between the two clubs, not a box around either. */
    .scoreboard li {
        display: grid;
        gap: var(--space-xs);
        justify-items: center;
        padding-inline: var(--space-lg);
    }

    .scoreboard li + li {
        border-inline-start: 1px solid var(--border);
    }

    /* The club's mark on the disc the league itself draws behind it: the plate takes
       the club's published primary (api-recon caveat 35) from a custom property the
       markup sets per side as data — never a stylesheet value — and every published
       primary is dark, which is what lets the two white-ink marks read in both
       palettes. --crest-bg stays in charge for a club with no colour on record. */
    .scoreboard .crest {
        background: var(--team-plate, var(--crest-bg));
        block-size: 3rem;
        padding: var(--space-xs);
    }

    .scoreboard img.crest {
        border-radius: 50%;
        inline-size: 3rem;
    }

    .scoreboard .crest-empty {
        min-inline-size: 3rem;
    }

    .scoreboard strong {
        font-family: var(--font-display);
        font-size: var(--text-h1);
        font-variant-numeric: tabular-nums;
        line-height: 1;
    }

    /* Where the game got to, under the score — meta about the result, at the weight
       of the game's other context lines. */
    .scoreboard p {
        color: var(--text-muted);
        font-size: var(--text-sm);
        margin: var(--space-xs) 0 0;
    }

    /* The game's context gathered onto one quiet line under the scoreboard — the
       start, the stadium, its capacity — where a stack of one-fact paragraphs above
       the score zigzagged the eye on its way to the tables. */
    .gamehead-meta {
        color: var(--text-muted);
        font-size: var(--text-sm);
        margin: var(--space-xs) 0 0;
    }

    /* A game's context lines: the stadium the league names, and — before it is played —
       where to buy a ticket or watch. Both are meta about the game rather than part of
       it, so they read at the weight of the status line above them, not the data below. */
    .game-venue,
    .game-tickets {
        color: var(--text-muted);
        font-size: var(--text-sm);
        margin-block-start: calc(var(--space-xs) * -1);
    }

    .game-tickets {
        display: flex;
        flex-wrap: wrap;
        gap: var(--space-sm);
    }

    /* The pitch codes trailing a play's narrative. They are the evidence under the
       sentence rather than part of it, so they read at the credit line's weight and
       are told apart from the prose by a gap the markup cannot supply — Razor drops
       the whitespace between two elements. The codes themselves are spaced so a
       four-pitch at-bat reads as four pitches and not one word. */
    .pitch-sequence {
        color: var(--text-muted);
        font-size: var(--text-sm);
        margin-inline-start: var(--space-xs);
    }

    .pitch-sequence abbr + abbr {
        margin-inline-start: var(--space-2xs);
    }

    /* Style guide scaffolding. */
    .swatch-grid,
    .space-list {
        list-style: none;
        margin: 0;
        padding: 0;
    }

    .swatch-grid {
        display: flex;
        flex-wrap: wrap;
        gap: var(--space-sm);
    }

    .swatch-grid li {
        display: flex;
        align-items: center;
        gap: var(--space-xs);
    }

    .swatch {
        display: inline-block;
        inline-size: var(--space-lg);
        block-size: var(--space-lg);
        border: 1px solid var(--border-strong);
        background: var(--swatch);
    }

    .space-list li {
        display: flex;
        align-items: center;
        gap: var(--space-xs);
        margin-block: var(--space-2xs);
    }

    .space-bar {
        display: inline-block;
        inline-size: var(--bar-size);
        block-size: var(--space-xs);
        background: var(--accent);
    }

    /* Blazor's error boundary — inline data: URI, no external request. The icon's own
       size is named rather than left implicit: the template's inset was a raw 3.7rem
       tuned by eye to it, off the spacing scale and impossible to re-derive. The text
       clears the icon by a scale step on either side of it (issue #32). */
    .blazor-error-boundary {
        --icon-size: 1.8rem;
        background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat var(--space-md) / var(--icon-size), var(--danger-bg);
        color: var(--danger-text);
        padding: var(--space-md);
        padding-inline-start: calc(var(--space-md) * 2 + var(--icon-size));
    }

    .blazor-error-boundary::after {
        content: "An error has occurred.";
    }
}
