/*
  QRCodeGenerator — reset and element defaults (docs/08-UI-SPEC.md §9.3, §9.5, §9.6).

  Everything here reads tokens. If a value in this file is not a var(), it is
  either a keyword or a bug.
*/

/*
  Route changes cross-fade (docs/08-UI-SPEC.md §9.5). `navigation: auto` opts the
  document into the View Transitions API for same-origin CROSS-DOCUMENT
  navigations, which is what the shells' links are: Blazor's enhanced navigation
  patches the DOM in place and never performs one, so both shells carry
  `data-enhance-nav="false"` on their navigation. That trade is recorded in §9.5.

  It is outside the layers on purpose — `@view-transition` is a document-level
  at-rule and belongs beside the `@font-face` block rather than in a cascade layer
  it would never be overridden from.
*/
@view-transition {
    navigation: auto;
}

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

    :where(body, h1, h2, h3, h4, p, figure, blockquote, dl, dd, ul, ol) {
        margin: 0;
    }

    :where(ul[role='list'], ol[role='list']) {
        padding: 0;
        list-style: none;
    }

    :where(img, picture, svg, video, canvas) {
        display: block;
        max-inline-size: 100%;
    }

    :where(input, button, textarea, select) {
        font: inherit;
        color: inherit;
    }

    /* A textarea that cannot grow sideways cannot break a grid column. */
    :where(textarea) {
        resize: vertical;
    }
}

@layer base {
    html {
        /* Anchor scrolling clears a sticky header without a magic offset per page. */
        scroll-padding-block-start: var(--space-16);
        -webkit-text-size-adjust: 100%;
    }

    body {
        margin: 0;
        min-block-size: 100dvh;
        background-color: var(--bg);
        color: var(--text);

        font-family: var(--font-sans);
        font-size: var(--text-base);
        font-weight: var(--weight-regular);
        line-height: var(--leading-body);
        letter-spacing: var(--tracking-body);
        font-feature-settings: var(--font-features);
        font-optical-sizing: auto;
        -webkit-font-smoothing: antialiased;
        text-rendering: optimizeLegibility;
    }

    /* ── Headings ──────────────────────────────────────────────────────────
       Display sizes are tighter and slightly negative-tracked, which is what
       stops a large heading reading as a blown-up paragraph. `balance` keeps a
       two-line heading from leaving one word alone on the second line.
    */
    h1, h2, h3, h4 {
        font-weight: var(--weight-semibold);
        line-height: var(--leading-display);
        letter-spacing: var(--tracking-display);
        text-wrap: balance;
    }

    h1 { font-size: var(--text-3xl); }
    h2 { font-size: var(--text-2xl); }
    h3 { font-size: var(--text-xl); }
    h4 { font-size: var(--text-lg); line-height: var(--leading-tight); }

    p {
        text-wrap: pretty;
    }

    small {
        font-size: var(--text-sm);
    }

    strong, b {
        font-weight: var(--weight-semibold);
    }

    a {
        color: var(--accent);
        text-decoration-thickness: 1px;
        text-underline-offset: 0.15em;
        transition: color var(--dur-instant) var(--ease-out);
    }

    a:hover {
        color: color-mix(in oklch, var(--accent) 80%, var(--text));
    }

    code, kbd, samp, pre {
        font-family: var(--font-mono);
        font-size: 0.9em;
        font-feature-settings: normal;
    }

    /* Horizontal padding only: vertical padding on an inline element grows the
       box without growing the line, so it overlaps the line above. */
    :not(pre) > code {
        padding-inline: var(--space-1);
        border-radius: var(--r-sm);
        background: var(--surface-2);
    }

    hr {
        border: 0;
        border-block-start: 1px solid var(--border);
        margin-block: var(--space-6);
    }

    /* Numbers line up in columns. Every table in this product is figures. */
    table {
        border-collapse: collapse;
        font-variant-numeric: tabular-nums;
    }

    th {
        text-align: start;
        font-weight: var(--weight-medium);
        color: var(--text-muted);
    }

    /* ── Focus ─────────────────────────────────────────────────────────────
       One ring, everywhere, on :focus-visible only — so a mouse click does not
       draw it and a keyboard user never loses it. It is never removed (§9.6);
       a component may change its colour, not its existence.
    */
    :focus-visible {
        outline: var(--focus-ring-width) solid var(--focus-ring-color);
        outline-offset: var(--focus-ring-offset);
        border-radius: var(--r-sm);
    }

    /* The ring is the affordance, so the UA's own dotted outline is not needed. */
    :focus:not(:focus-visible) {
        outline: none;
    }

    /*
      A destination, not a control. `tabindex="-1"` means an element can only be
      focused by script — which is exactly what Blazor does to the page's <h1>
      after a navigation, so a screen reader announces where it landed. The
      announcement is the affordance; a ring around the page title reads as an
      editable field. Nothing a keyboard can reach is covered by this: a control
      a user can tab to does not carry -1.
    */
    [tabindex='-1']:focus,
    [tabindex='-1']:focus-visible {
        outline: none;
    }

    ::selection {
        background: var(--accent-tint-strong);
    }

    /*
      The route change itself: 250 ms on the token, the panel easing, and a plain
      cross-fade. Anything more elaborate — a slide, a scale — announces the
      navigation rather than covering it, which §9.5 calls wrong.
    */
    ::view-transition-old(root),
    ::view-transition-new(root) {
        animation-duration: var(--dur-route);
        animation-timing-function: var(--ease-panel);
    }

    /* ── Motion ────────────────────────────────────────────────────────────
       `reduce` REMOVES motion rather than shortening it (§9.5): the interface
       has to be complete and usable with every animation gone, which is why
       durations go to a hair rather than to zero — a zero-length transition
       fires no transitionend, and code that waits for one would hang.
    */
    @media (prefers-reduced-motion: reduce) {
        :root {
            --dur-instant: 1ms;
            --dur-panel: 1ms;
            --dur-route: 1ms;
            --dur-preview: 1ms;
        }

        *,
        *::before,
        *::after {
            animation-duration: 1ms !important;
            animation-iteration-count: 1 !important;
            transition-duration: 1ms !important;
            scroll-behavior: auto !important;
        }

        /* A transform that is part of the layout stays; one that is a flourish
           does not. Components declare the latter with this custom property. */
        [data-motion='decorative'] {
            transform: none !important;
        }

        /* The universal selector above does not match a pseudo-element, so the
           route cross-fade has to be named to be removed. Without this the one
           animation a reduced-motion visitor cannot switch off is the one that
           covers the whole page. */
        ::view-transition-old(root),
        ::view-transition-new(root) {
            animation: none !important;
        }
    }
}
