/*
  QRCodeGenerator — design tokens (docs/08-UI-SPEC.md §9.2 to §9.5).

  Every colour, space, radius, size, shadow and duration in the product is
  declared here and nowhere else. A component that writes a raw value has taken
  a decision this file is supposed to own, and a test fails the build over it.

  The cascade layers are declared here because this is the first stylesheet the
  page loads, and layer order is fixed by first appearance. Later layers win, so
  a component always beats base, and a utility always beats a component.

  Blazor's own scoped styles (`*.razor.css`, bundled as QrGen.Web.styles.css) are
  UNLAYERED and therefore beat all of these. That is the right way round — a
  component's own file is the most specific statement about it — but it does mean
  a scoped rule cannot be overridden by a utility class.
*/

@layer reset, tokens, base, primitives, components, utilities;

/* ── Typefaces ─────────────────────────────────────────────────────────────
   Self-hosted, subset to latin + latin-ext, variable on the weight axis: one
   file covers 100–900. Built by scripts/subset-web-fonts.py from the same TTFs
   the renderer draws captions with, so the interface and the artefacts it
   produces are the same release of the same family.
*/

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

@font-face {
    font-family: 'JetBrains Mono';
    src: url('../fonts/JetBrainsMono.woff2') format('woff2');
    font-weight: 100 800;
    font-style: normal;
    font-display: swap;
}

@layer tokens {
    :root {
        color-scheme: light dark;

        /* ── Colour ─────────────────────────────────────────────────────────
           OKLCH, not hex: perceptually uniform, so one lightness ramp works in
           both themes and contrast stays predictable. Every token is a single
           light-dark() declaration — there is no second palette to keep in step.

           The accent lightness values are not guesses. QrGen.Web.IntegrationTests
           reads this file, converts these literals to sRGB and asserts the
           contrast ratios §9.7 demands, in both themes.
        */
        --accent-h: 258;
        --accent-base: light-dark(oklch(0.53 0.19 var(--accent-h)),
                                  oklch(0.76 0.15 var(--accent-h)));
        --accent: oklch(from var(--accent-base) l c h);

        /* Text drawn ON the accent: near-white over the dark light-theme accent,
           near-black over the light dark-theme one. */
        --accent-contrast: light-dark(oklch(0.99 0.002 var(--accent-h)),
                                      oklch(0.18 0.010 var(--accent-h)));

        --bg:         light-dark(oklch(0.99 0.002 258), oklch(0.17 0.008 258));
        --surface:    light-dark(oklch(1.00 0     0  ), oklch(0.21 0.010 258));
        --surface-2:  light-dark(oklch(0.975 0.003 258), oklch(0.25 0.012 258));
        /* Two borders, because they answer to different rules. `--border` is a
           separator — decoration, exempt from contrast. `--border-strong` is the
           boundary of a control a user has to find, which WCAG 1.4.11 holds to
           3:1 against the surface behind it; these two lightness values are the
           extremes that still clear it, and the test asserts they do. */
        --border:     light-dark(oklch(0.91 0.005 258), oklch(0.30 0.014 258));
        --border-strong: light-dark(oklch(0.648 0.010 258), oklch(0.53 0.018 258));
        --text:       light-dark(oklch(0.22 0.010 258), oklch(0.96 0.004 258));
        --text-muted: light-dark(oklch(0.48 0.012 258), oklch(0.74 0.010 258));

        --ok:   light-dark(oklch(0.52 0.14 155), oklch(0.75 0.16 155));
        --warn: light-dark(oklch(0.55 0.14  75), oklch(0.82 0.15  75));
        --err:  light-dark(oklch(0.52 0.20  25), oklch(0.72 0.18  25));

        /* Tints are mixed, not declared: one accent, no parallel ramp to maintain. */
        --accent-tint: color-mix(in oklch, var(--accent) 12%, transparent);
        --accent-tint-strong: color-mix(in oklch, var(--accent) 22%, transparent);

        /* ── Spacing — 4 px base, nothing off-scale ─────────────────────── */
        --space-1: 0.25rem;   /*  4px */
        --space-2: 0.5rem;    /*  8px */
        --space-3: 0.75rem;   /* 12px */
        --space-4: 1rem;      /* 16px */
        --space-5: 1.25rem;   /* 20px */
        --space-6: 1.5rem;    /* 24px */
        --space-8: 2rem;      /* 32px */
        --space-10: 2.5rem;   /* 40px */
        --space-12: 3rem;     /* 48px */
        --space-16: 4rem;     /* 64px */

        /* ── Radius ─────────────────────────────────────────────────────────
           Consistent radius is one of the strongest "this was designed"
           signals; mixed radii is the strongest signal of the opposite.
        */
        --r-sm: 6px;
        --r-md: 10px;
        --r-lg: 16px;
        --r-xl: 24px;
        --r-full: 999px;

        /* ── Elevation ──────────────────────────────────────────────────────
           Layered, low-opacity, tinted with the accent hue — never pure black,
           which reads as dirt on a tinted surface. In dark mode a raised
           surface is expressed with lightness (--surface-2), not a bigger
           shadow: a shadow on a dark ground is invisible.
        */
        --shadow-color: oklch(0.25 0.05 var(--accent-h));
        --shadow-sm: 0 1px 2px  color-mix(in oklch, var(--shadow-color) 6%, transparent);
        --shadow-md: 0 2px 4px  color-mix(in oklch, var(--shadow-color) 5%, transparent),
                     0 8px 16px color-mix(in oklch, var(--shadow-color) 6%, transparent);
        --shadow-lg: 0 4px 8px  color-mix(in oklch, var(--shadow-color) 5%, transparent),
                     0 16px 32px color-mix(in oklch, var(--shadow-color) 8%, transparent);

        /* ── Typography ─────────────────────────────────────────────────────
           Fluid with clamp(): every size grows between a 320 px phone and a
           1440 px desktop with no breakpoint anywhere. Ratio ≈ 1.2 through the
           UI sizes and ≈ 1.25 through the display ones. Body never drops below
           15 px, and nothing a user must read below 13 px.
        */
        --font-sans: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
        --font-mono: 'JetBrains Mono', ui-monospace, 'SF Mono', 'Cascadia Mono', monospace;

        /* cv05 gives the lower-case l a tail, ss03 the curved-tail g, tnum the
           tabular figures every table, counter and quota indicator needs. */
        --font-features: 'cv05' 1, 'ss03' 1, 'tnum' 1;

        --text-xs:   clamp(0.75rem,   0.734rem + 0.08vw, 0.8125rem);
        --text-sm:   clamp(0.8125rem, 0.797rem + 0.08vw, 0.875rem);
        --text-base: clamp(0.9375rem, 0.922rem + 0.08vw, 1rem);
        --text-md:   clamp(1.0625rem, 1.047rem + 0.08vw, 1.125rem);
        --text-lg:   clamp(1.1875rem, 1.141rem + 0.23vw, 1.375rem);
        --text-xl:   clamp(1.4375rem, 1.359rem + 0.39vw, 1.75rem);
        --text-2xl:  clamp(1.75rem,   1.625rem + 0.63vw, 2.25rem);
        --text-3xl:  clamp(2.125rem,  1.922rem + 1.02vw, 2.9375rem);

        --leading-display: 1.15;
        --leading-body: 1.5;
        --leading-tight: 1.3;

        --tracking-display: -0.02em;
        --tracking-body: 0;
        --tracking-eyebrow: 0.08em;

        --weight-regular: 400;
        --weight-medium: 500;
        --weight-semibold: 600;
        --weight-bold: 700;

        /* Measure: ~68 characters, the point past which the eye loses the line. */
        --measure: 68ch;

        /* ── Density ────────────────────────────────────────────────────────
           One switch, no duplicated components: `comfortable` on the public
           page, `compact` in the admin tables. Components read the derived
           values, never the flag.
        */
        --density-space: var(--space-4);
        --density-row: 2.75rem;
        --control-h: 2.5rem;      /* 40px — the minimum touch target */

        /* ── Motion ─────────────────────────────────────────────────────────
           Restrained, fast, purposeful. Anything that draws attention to
           itself is wrong.
        */
        --dur-instant: 120ms;   /* hover, focus, colour */
        --dur-panel: 200ms;     /* panel, popover, disclosure */
        --dur-route: 250ms;     /* view transitions */
        --dur-preview: 180ms;   /* the preview crossfade */
        --ease-out: ease-out;
        --ease-panel: cubic-bezier(0.32, 0.72, 0, 1);
        --ease-crossfade: ease-in-out;

        /* ── Focus ──────────────────────────────────────────────────────────
           A 2 px accent ring at a 2 px offset, :focus-visible only, never
           removed (§9.6).
        */
        --focus-ring-width: 2px;
        --focus-ring-offset: 2px;
        --focus-ring-color: var(--accent);

        /* ── Layering ───────────────────────────────────────────────────────
           A short, named scale: a z-index written as a number in a component is
           a number somebody else has to guess against.
        */
        --z-base: 0;
        --z-sticky: 10;
        --z-header: 20;
        --z-overlay: 30;
        --z-modal: 40;
        --z-toast: 50;
    }

    /*
      The theme override. `color-scheme: light dark` above follows the operating
      system; this is how a caller pins one, and it is the only mechanism —
      `light-dark()` resolves against the used colour scheme, so pinning the
      scheme repaints every token at once with no second palette and no class
      per component.

      Task 6.1 sets the attribute on <html> from a cookie, which is what makes
      the choice survive a server render. It works on any element, which is what
      lets one page show both themes side by side.
    */
    [data-theme='light'] { color-scheme: light; }
    [data-theme='dark'] { color-scheme: dark; }

    /* The one density override. Set on a container, not on the component. */
    [data-density='compact'] {
        --density-space: var(--space-2);
        --density-row: 2.25rem;
    }
}
