/* ===== layers.css ===== */
/*
 * Cascade layer order -- declared exactly once, before anything else.
 *
 * This single line is what lets skin win over structure on the properties it
 * owns without either file needing specificity hacks, and it is why a theme
 * can never be fought by a component rule.
 *
 * It is also the enforcement mechanism for the floors: themes are authored
 * into `tokens`, floors live in `base`, and `base` comes later -- so a theme
 * physically cannot lower a tap target or shrink body text below the minimum.
 */
@layer reset, tokens, base, structure, skin, utility, overrides;

/* ===== reset.css ===== */
/*
 * Minimal reset. No colour, no font, no spacing decisions -- those are the
 * theme's job. This layer only removes browser defaults that would fight
 * token-driven layout.
 */
@layer reset {
  *,
  *::before,
  *::after {
    box-sizing: border-box;
  }

  * {
    margin: 0;
  }

  html {
    -webkit-text-size-adjust: 100%;
    text-size-adjust: 100%;
  }

  body {
    min-height: 100vh;
  }

  img,
  picture,
  video,
  canvas,
  svg {
    display: block;
    max-width: 100%;
  }

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

  p,
  h1,
  h2,
  h3,
  h4,
  h5,
  h6 {
    overflow-wrap: break-word;
  }

  ul[role="list"],
  ol[role="list"] {
    list-style: none;
    padding: 0;
  }

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

/* ===== tokens/contract.css ===== */
/*
 * THE TOKEN CONTRACT
 *
 * Every token the app is allowed to use, declared once with a safe fallback.
 * This file IS the spec: a new theme is written by overriding names from this
 * list, and nothing here needs reading the component source to discover.
 *
 * Components consume tier 2 and tier 3 only. Tier 1 primitives are private to
 * each theme file and are never referenced outside it.
 *
 * Fallbacks are deliberately drab. If a theme forgets a token the page stays
 * legible and the omission is visible rather than catastrophic.
 */
@layer tokens {
  :root {
    /* ---------------------------------------------------------------
     * TIER 2 -- semantic roles
     * --------------------------------------------------------------- */

    /* Surfaces */
    --surface-page: white;
    --surface-raised: white;
    --surface-sunken: white;
    --surface-overlay: white;

    /* Text */
    --text-primary: black;
    --text-secondary: black;
    --text-muted: black;
    --text-inverse: white;
    --text-link: black;

    /* Lines */
    --line-hairline: gray;
    --line-strong: black;
    --line-focus: black;

    /* Accent. `-contrast` is the foreground that is legible ON the accent. */
    --accent: black;
    --accent-hover: black;
    --accent-contrast: white;

    /* Feedback. Same pattern: solid for text/borders, -bg for fills,
     * -contrast for the foreground that sits on -bg. */
    --ok: black;
    --ok-bg: white;
    --ok-contrast: black;
    --warn: black;
    --warn-bg: white;
    --warn-contrast: black;
    --danger: black;
    --danger-bg: white;
    --danger-contrast: black;
    --info: black;
    --info-bg: white;
    --info-contrast: black;

    /* ---------------------------------------------------------------
     * TIER 3 -- domain semantics
     *
     * This tier exists so themes restyle *meaning* coherently. Without it
     * every theme author has to rediscover which grey means "floated", and
     * themes drift apart. The list is closed: a new token requires a new
     * domain concept, not a new opinion.
     * --------------------------------------------------------------- */

    /* Agreement state -- brief 4.3. Visual weight tracks state. */
    --state-floated-fg: black;
    --state-floated-bg: white;
    --state-floated-line: gray;
    --state-agreed-fg: black;
    --state-agreed-bg: white;
    --state-agreed-line: gray;
    --state-locked-fg: black;
    --state-locked-bg: white;
    --state-locked-line: black;

    /* Vote values -- yes / fine either way / rather not */
    --vote-yes: black;
    --vote-either: gray;
    --vote-rather-not: black;

    /* Day types -- drives the trip's spine being visible at a glance */
    --day-prep-accent: black;
    --day-prep-bg: white;
    --day-install-accent: black;
    --day-install-bg: white;
    --day-show-accent: black;
    --day-show-bg: white;
    --day-deinstall-accent: black;
    --day-deinstall-bg: white;
    --day-free-accent: black;
    --day-free-bg: white;
    --day-travel-accent: black;
    --day-travel-bg: white;
    --day-protected-accent: black;
    --day-protected-bg: white;

    /* Authors -- attribution now, two-camera toggle in November */
    /* `-contrast` is the foreground legible ON that author's accent, needed
     * wherever an author colour is used as a fill rather than as text. */
    --author-1-accent: black;
    --author-1-bg: white;
    --author-1-contrast: white;
    --author-2-accent: black;
    --author-2-bg: white;
    --author-2-contrast: white;
    --author-3-accent: black;
    --author-3-bg: white;
    --author-3-contrast: white;

    /* Album triage states */
    --album-unreviewed: gray;
    --album-in-scroll: black;
    --album-vault: gray;
    --album-cut: gray;

    /* Aging -- nothing rots (non-negotiable 4) */
    --aging-fresh: gray;
    --aging-stale: black;
    --aging-rotting: black;

    /* ---------------------------------------------------------------
     * TYPE
     *
     * Three roles, mapping onto what this app actually does. --font-data is
     * the almanac bar, dual clocks, countdowns and timestamps; decision 8's
     * "monospace in the data displays, not body text" is expressed here as a
     * theme setting rather than a rule scattered through markup.
     * --------------------------------------------------------------- */
    --font-display: system-ui, sans-serif;
    --font-body: system-ui, sans-serif;
    --font-data: ui-monospace, monospace;

    --scale--1: 0.8125rem;
    --scale-0: 1rem;
    --scale-1: 1.125rem;
    --scale-2: 1.375rem;
    --scale-3: 1.75rem;
    --scale-4: 2.25rem;
    --scale-5: 3rem;

    --weight-normal: 400;
    --weight-medium: 500;
    --weight-semibold: 600;
    --weight-bold: 700;

    --tracking-tight: -0.015em;
    --tracking-normal: 0;
    --tracking-wide: 0.12em;

    --leading-tight: 1.15;
    --leading-normal: 1.55;
    --leading-loose: 1.75;

    /* ---------------------------------------------------------------
     * SPACE -- one scale, used for every gap and pad. Layout does the
     * spacing via flex/grid `gap`, never per-element margins.
     * --------------------------------------------------------------- */
    --space-0: 0;
    --space-1: 0.25rem;
    --space-2: 0.5rem;
    --space-3: 0.75rem;
    --space-4: 1rem;
    --space-5: 1.5rem;
    --space-6: 2rem;
    --space-7: 3rem;
    --space-8: 4rem;
    --space-9: 6rem;

    /* SHAPE */
    --radius-0: 0;
    --radius-sm: 2px;
    --radius-md: 4px;
    --radius-lg: 8px;
    --radius-pill: 999px;
    --border-width: 1px;

    /* ELEVATION */
    --elev-0: none;
    --elev-1: none;
    --elev-2: none;

    /* MOTION. A theme may set every duration to 0ms and become a
     * reduced-motion theme for free. */
    --dur-fast: 120ms;
    --dur-base: 220ms;
    --dur-slow: 400ms;
    --ease-standard: cubic-bezier(0.2, 0, 0.38, 1);
    --ease-enter: cubic-bezier(0, 0, 0.38, 1);
    --ease-exit: cubic-bezier(0.2, 0, 1, 1);

    /* DENSITY -- multiplier feeding control padding and heights */
    --density: 1;
  }
}

/* ===== tokens/floors.css ===== */
/*
 * NON-OVERRIDABLE FLOORS
 *
 * These are declared in the `base` layer, not `tokens`. Because layer order is
 * `... tokens, base, ...`, a theme authored into `tokens` loses to this file
 * for the same property -- so the floors are enforced by the architecture
 * rather than by anyone remembering them.
 *
 * Literal values are correct and expected here. This is the one file besides
 * the theme palettes where the literal-ban gate permits raw numbers, because
 * a floor that referred to a token would not be a floor.
 */
@layer base {
  :root {
    /* Phone-first is a locked non-negotiable, not a stylistic preference.
     * 44px is the minimum comfortable tap target. */
    --tap-min: 44px;

    /* Body text never resolves below this. The Korea side reads English on
     * phones; legibility outranks any theme's typographic ambition. */
    --body-min: 16px;
  }
}

/* ===== tokens/themes/almanac.css ===== */
/*
 * THEME ONE -- "almanac"
 *
 * Decision 8: warm/neutral base, industrial accents, legibility first. The
 * monospace register is carried by --font-data only, so it reaches the almanac
 * bar, dual clocks, countdowns and timestamps without touching body text or
 * navigation.
 *
 * Structure of this file, which every future theme should copy:
 *   1. Light primitives + THE MAPPING (written once)
 *   2. Dark primitives only -- the mapping is inherited, not repeated
 *   3. Same dark primitives for data-mode="auto" under prefers-color-scheme
 *
 * Only primitives are duplicated for dark mode. The semantic and domain
 * mapping exists in exactly one place, so adding a token is a one-line change.
 */
@layer tokens {
  [data-theme="almanac"] {
    /* ---- TIER 1: light primitives (private) ---- */
    --p-paper: #eef0ea;
    --p-paper-raised: #f7f8f4;
    --p-paper-sunken: #e4e7de;
    --p-paper-overlay: #fbfcf9;
    --p-ink: #1b211c;
    --p-ink-mid: #454c46;
    --p-ink-soft: #5a6159;
    --p-rule: #d3d7cc;
    --p-rule-strong: #9ba394;

    --p-pine: #1f5f4e;
    --p-pine-deep: #17493c;
    --p-pine-tint: #e2efea;
    --p-leaf: #2e6b3a;
    --p-leaf-deep: #1d4a26;
    --p-leaf-tint: #e3efe4;
    --p-ochre: #7c6414;
    --p-ochre-deep: #5a4a0f;
    --p-ochre-tint: #f5edd4;
    --p-rust: #a33a22;
    --p-rust-deep: #7a2b19;
    --p-rust-tint: #f7e3dd;
    --p-slate: #1f5066;
    --p-slate-deep: #17414f;
    --p-slate-tint: #dfeaf0;
    --p-plum: #8a2f5c;
    --p-plum-tint: #f6e3ee;
    --p-iris: #5a4a6b;
    --p-iris-tint: #ece7f1;
    --p-quiet-tint: #e9ebe4;
    --p-quiet-line: #b6bcad;
    --p-agreed-line: #86ae8d;
    /* Vault and cut are de-emphasised, but they still render as text -- so they
     * are darkened to clear AA rather than left as pale greys. */
    --p-vault: #60675e;
    --p-cut: #656c62;
    /* Foreground for anything filled with an accent colour. One primitive so
     * accent and all three author fills stay in step. */
    --p-on-accent: #f7f8f4;

    /* ---- THE MAPPING -- written once, inherited by every mode ---- */

    /* Tier 2: surfaces */
    --surface-page: var(--p-paper);
    --surface-raised: var(--p-paper-raised);
    --surface-sunken: var(--p-paper-sunken);
    --surface-overlay: var(--p-paper-overlay);

    /* Tier 2: text */
    --text-primary: var(--p-ink);
    --text-secondary: var(--p-ink-mid);
    --text-muted: var(--p-ink-soft);
    --text-inverse: var(--p-paper-raised);
    --text-link: var(--p-pine);

    /* Tier 2: lines */
    --line-hairline: var(--p-rule);
    --line-strong: var(--p-rule-strong);
    --line-focus: var(--p-pine);

    /* Tier 2: accent */
    --accent: var(--p-pine);
    --accent-hover: var(--p-pine-deep);
    --accent-contrast: var(--p-on-accent);

    /* Tier 2: feedback */
    --ok: var(--p-leaf);
    --ok-bg: var(--p-leaf-tint);
    --ok-contrast: var(--p-leaf-deep);
    --warn: var(--p-ochre);
    --warn-bg: var(--p-ochre-tint);
    --warn-contrast: var(--p-ochre-deep);
    --danger: var(--p-rust);
    --danger-bg: var(--p-rust-tint);
    --danger-contrast: var(--p-rust-deep);
    --info: var(--p-slate);
    --info-bg: var(--p-slate-tint);
    --info-contrast: var(--p-slate-deep);

    /* Tier 3: agreement state. Weight climbs Floated -> Agreed -> Locked,
     * so a glance at the calendar shows what is real. */
    --state-floated-fg: var(--p-ink-soft);
    --state-floated-bg: var(--p-quiet-tint);
    --state-floated-line: var(--p-quiet-line);
    --state-agreed-fg: var(--p-leaf-deep);
    --state-agreed-bg: var(--p-leaf-tint);
    --state-agreed-line: var(--p-agreed-line);
    --state-locked-fg: var(--p-ink);
    --state-locked-bg: var(--p-paper-raised);
    --state-locked-line: var(--p-ink);

    /* Tier 3: vote values */
    --vote-yes: var(--p-leaf);
    --vote-either: var(--p-ink-soft);
    --vote-rather-not: var(--p-rust);

    /* Tier 3: day types */
    --day-prep-accent: var(--p-ochre);
    --day-prep-bg: var(--p-ochre-tint);
    --day-install-accent: var(--p-slate);
    --day-install-bg: var(--p-slate-tint);
    --day-show-accent: var(--p-plum);
    --day-show-bg: var(--p-plum-tint);
    --day-deinstall-accent: var(--p-iris);
    --day-deinstall-bg: var(--p-iris-tint);
    --day-free-accent: var(--p-leaf);
    --day-free-bg: var(--p-leaf-tint);
    --day-travel-accent: var(--p-pine);
    --day-travel-bg: var(--p-pine-tint);
    --day-protected-accent: var(--p-rust);
    --day-protected-bg: var(--p-rust-tint);

    /* Tier 3: authors */
    --author-1-accent: var(--p-pine);
    --author-1-bg: var(--p-pine-tint);
    --author-1-contrast: var(--p-on-accent);
    --author-2-accent: var(--p-slate);
    --author-2-bg: var(--p-slate-tint);
    --author-2-contrast: var(--p-on-accent);
    --author-3-accent: var(--p-plum);
    --author-3-bg: var(--p-plum-tint);
    --author-3-contrast: var(--p-on-accent);

    /* Tier 3: album triage */
    --album-unreviewed: var(--p-ink-soft);
    --album-in-scroll: var(--p-pine);
    --album-vault: var(--p-vault);
    --album-cut: var(--p-cut);

    /* Tier 3: aging */
    --aging-fresh: var(--p-ink-soft);
    --aging-stale: var(--p-ochre);
    --aging-rotting: var(--p-rust);

    /* ---- Type: three roles ---- */
    --font-display: "Segoe UI Variable Display", -apple-system,
      BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
    --font-body: "Segoe UI Variable Text", -apple-system, BlinkMacSystemFont,
      "Segoe UI", system-ui, Roboto, sans-serif;
    --font-data: ui-monospace, "SF Mono", SFMono-Regular, "Cascadia Mono",
      "Segoe UI Mono", Menlo, Consolas, monospace;

    /* ---- Shape: squared off, industrial ---- */
    --radius-0: 0;
    --radius-sm: 1px;
    --radius-md: 2px;
    --radius-lg: 3px;
  }

  /* ---- Dark: primitives only. The mapping above is inherited. ---- */
  [data-theme="almanac"][data-mode="dark"] {
    --p-paper: #121513;
    --p-paper-raised: #1b1f1c;
    --p-paper-sunken: #0c0e0c;
    --p-paper-overlay: #242a24;
    --p-ink: #e4e7df;
    --p-ink-mid: #b4bcb0;
    --p-ink-soft: #9aa397;
    --p-rule: #2e342e;
    --p-rule-strong: #4a524a;

    --p-pine: #6fbfa3;
    --p-pine-deep: #8ed0b8;
    --p-pine-tint: #14251f;
    --p-leaf: #7cc08a;
    --p-leaf-deep: #c8e6cf;
    --p-leaf-tint: #1b2a1e;
    --p-ochre: #c4a748;
    --p-ochre-deep: #e8d89a;
    --p-ochre-tint: #2a2413;
    --p-rust: #e0805f;
    --p-rust-deep: #f2c4b2;
    --p-rust-tint: #2a1a15;
    --p-slate: #7fb3cc;
    --p-slate-deep: #c3dde9;
    --p-slate-tint: #16242c;
    --p-plum: #d190b0;
    --p-plum-tint: #2a1620;
    --p-iris: #a596b8;
    --p-iris-tint: #1f1a26;
    --p-quiet-tint: #1a1e1a;
    --p-quiet-line: #3a423a;
    --p-agreed-line: #4a7a55;
    --p-vault: #8b9388;
    --p-cut: #7f877c;

    /* Dark inverts which end of the ramp is "inverse" text. */
    --p-on-accent: #0e1310;
    --text-inverse: #121513;
  }

  /* ---- Auto: same dark primitives, applied only when the OS asks ---- */
  @media (prefers-color-scheme: dark) {
    [data-theme="almanac"][data-mode="auto"] {
      --p-paper: #121513;
      --p-paper-raised: #1b1f1c;
      --p-paper-sunken: #0c0e0c;
      --p-paper-overlay: #242a24;
      --p-ink: #e4e7df;
      --p-ink-mid: #b4bcb0;
      --p-ink-soft: #9aa397;
      --p-rule: #2e342e;
      --p-rule-strong: #4a524a;

      --p-pine: #6fbfa3;
      --p-pine-deep: #8ed0b8;
      --p-pine-tint: #14251f;
      --p-leaf: #7cc08a;
      --p-leaf-deep: #c8e6cf;
      --p-leaf-tint: #1b2a1e;
      --p-ochre: #c4a748;
      --p-ochre-deep: #e8d89a;
      --p-ochre-tint: #2a2413;
      --p-rust: #e0805f;
      --p-rust-deep: #f2c4b2;
      --p-rust-tint: #2a1a15;
      --p-slate: #7fb3cc;
      --p-slate-deep: #c3dde9;
      --p-slate-tint: #16242c;
      --p-plum: #d190b0;
      --p-plum-tint: #2a1620;
      --p-iris: #a596b8;
      --p-iris-tint: #1f1a26;
      --p-quiet-tint: #1a1e1a;
      --p-quiet-line: #3a423a;
      --p-agreed-line: #4a7a55;
      --p-vault: #8b9388;
      --p-cut: #7f877c;

      --p-on-accent: #0e1310;
      --text-inverse: #121513;
    }
  }
}

/* ===== tokens/themes/_smoke.css ===== */
/*
 * SMOKE THEME -- test fixture, never shipped.
 *
 * Deliberately garish and deliberately nothing like theme one: different hues,
 * absurd radii, a display face nothing else uses, inflated spacing. Its whole
 * job is to make a hardcoded value or a missing token obvious on sight.
 *
 * If a component looks unchanged under this theme, that component is cheating
 * -- it is painting itself instead of consuming tokens.
 *
 * Contrast still passes AA. A test theme that failed the gate would make the
 * gate useless for finding real problems.
 */
@layer tokens {
  [data-theme="smoke"] {
    --surface-page: #1a0033;
    --surface-raised: #2d0a52;
    --surface-sunken: #0d0019;
    --surface-overlay: #3d1466;

    --text-primary: #f5ff00;
    --text-secondary: #ff9ee8;
    --text-muted: #b8a3ff;
    --text-inverse: #1a0033;
    --text-link: #00ffd5;

    --line-hairline: #7a3fbf;
    --line-strong: #f5ff00;
    --line-focus: #00ffd5;

    --accent: #ff007f;
    --accent-hover: #ff4da6;
    --accent-contrast: #1a0033;

    --ok: #00ffd5;
    --ok-bg: #003d33;
    --ok-contrast: #7cffe8;
    --warn: #ffb300;
    --warn-bg: #3d2a00;
    --warn-contrast: #ffd980;
    --danger: #ff3d5a;
    --danger-bg: #3d0011;
    --danger-contrast: #ff9eae;
    --info: #00b3ff;
    --info-bg: #002b3d;
    --info-contrast: #80d9ff;

    --state-floated-fg: #b8a3ff;
    --state-floated-bg: #1f0a3d;
    --state-floated-line: #5a2f8f;
    --state-agreed-fg: #7cffe8;
    --state-agreed-bg: #003d33;
    --state-agreed-line: #00ffd5;
    --state-locked-fg: #f5ff00;
    --state-locked-bg: #2d0a52;
    --state-locked-line: #f5ff00;

    --vote-yes: #00ffd5;
    --vote-either: #b8a3ff;
    --vote-rather-not: #ff3d5a;

    --day-prep-accent: #ffb300;
    --day-prep-bg: #3d2a00;
    --day-install-accent: #00b3ff;
    --day-install-bg: #002b3d;
    --day-show-accent: #ff007f;
    --day-show-bg: #3d0022;
    --day-deinstall-accent: #b8a3ff;
    --day-deinstall-bg: #1f0a3d;
    --day-free-accent: #00ffd5;
    --day-free-bg: #003d33;
    --day-travel-accent: #f5ff00;
    --day-travel-bg: #333d00;
    --day-protected-accent: #ff3d5a;
    --day-protected-bg: #3d0011;

    --author-1-accent: #00ffd5;
    --author-1-bg: #003d33;
    --author-1-contrast: #1a0033;
    --author-2-accent: #ff007f;
    --author-2-bg: #3d0022;
    --author-2-contrast: #1a0033;
    --author-3-accent: #ffb300;
    --author-3-bg: #3d2a00;
    --author-3-contrast: #1a0033;

    --album-unreviewed: #b8a3ff;
    --album-in-scroll: #00ffd5;
    --album-vault: #ff9ee8;
    --album-cut: #a06fe0;

    --aging-fresh: #b8a3ff;
    --aging-stale: #ffb300;
    --aging-rotting: #ff3d5a;

    /* Nothing like theme one -- serif display, wide tracking, fat radii. */
    --font-display: Georgia, "Times New Roman", serif;
    --font-body: "Comic Sans MS", "Brush Script MT", cursive, sans-serif;
    --font-data: "Courier New", Courier, monospace;

    --tracking-tight: 0.04em;
    --tracking-wide: 0.3em;
    --leading-normal: 2;

    --radius-sm: 8px;
    --radius-md: 16px;
    --radius-lg: 28px;
    --border-width: 4px;

    --space-4: 2rem;
    --space-5: 3.5rem;

    --dur-base: 900ms;
    --density: 1.6;
  }
}

/* ===== base.css ===== */
/*
 * Element defaults, entirely from tokens. Nothing here knows a colour name or
 * a font family -- change the theme and this file's output changes with it.
 *
 * Note the two floor guards: max() against --body-min and --tap-min. Because
 * this layer sits after `tokens`, a theme cannot undercut either one.
 */
@layer base {
  html {
    color-scheme: light dark;
  }

  body {
    background: var(--surface-page);
    color: var(--text-primary);
    font-family: var(--font-body);
    font-size: max(var(--scale-0), var(--body-min));
    font-weight: var(--weight-normal);
    line-height: var(--leading-normal);
    letter-spacing: var(--tracking-normal);
  }

  h1,
  h2,
  h3,
  h4 {
    font-family: var(--font-display);
    font-weight: var(--weight-semibold);
    line-height: var(--leading-tight);
    letter-spacing: var(--tracking-tight);
    text-wrap: balance;
  }

  h1 {
    font-size: var(--scale-4);
  }
  h2 {
    font-size: var(--scale-3);
  }
  h3 {
    font-size: var(--scale-2);
  }
  h4 {
    font-size: var(--scale-1);
  }

  p {
    max-width: 65ch;
  }

  small {
    font-size: var(--scale--1);
    color: var(--text-muted);
  }

  a {
    color: var(--text-link);
    text-decoration-thickness: 1px;
    text-underline-offset: 2px;
  }

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

  /* Data displays carry the industrial register -- decision 8. Any element
   * showing a time, a count or a measurement takes this class. */
  .data {
    font-family: var(--font-data);
    font-variant-numeric: tabular-nums;
    letter-spacing: var(--tracking-normal);
  }

  /* Uppercase labels get tracking, per the same register. */
  .label {
    font-family: var(--font-data);
    font-size: var(--scale--1);
    letter-spacing: var(--tracking-wide);
    text-transform: uppercase;
    color: var(--text-muted);
  }

  hr {
    border: 0;
    border-top: var(--border-width) solid var(--line-hairline);
  }

  code,
  pre {
    font-family: var(--font-data);
  }

  /* Every interactive control clears the tap floor. */
  button,
  a[role="button"],
  input[type="submit"],
  summary {
    min-height: max(calc(var(--space-6) * var(--density)), var(--tap-min));
    min-width: var(--tap-min);
    cursor: pointer;
  }

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

  /* Wide content scrolls inside its own container so the page body never
   * scrolls sideways. */
  .scroller {
    overflow-x: auto;
  }
}

/* ===== structure/gate.css ===== */
/*
 * STRUCTURE -- the login gate. Layout only.
 *
 * Phone-first: the form is the whole viewport on a small screen, the input is a
 * full-width target, and nothing depends on hover.
 */
@layer structure {
  .gate {
    min-height: 100svh;
    max-width: 26rem;
    margin-inline: auto;
    padding: var(--space-6) var(--space-4);
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: var(--space-6);
  }

  .gate__head {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
  }

  .gate__form {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
  }

  .gate__field {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
  }

  .gate__field input {
    width: 100%;
    min-height: max(calc(var(--space-6) * var(--density)), var(--tap-min));
    padding: var(--space-2) var(--space-3);
  }

  .gate__form button {
    width: 100%;
    padding: var(--space-3) var(--space-4);
  }

  .gate__choices {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
  }
}

/* ===== structure/probe.css ===== */
/*
 * STRUCTURE -- theme probe.
 *
 * Layout only. No colour, no font family, no radius, no shadow. Spacing comes
 * from the space scale via `gap`, never per-element margins.
 *
 * The probe is the visual contract: it renders every tier-3 token so a new
 * theme can be judged at a glance, and so the smoke theme has something to
 * visibly wreck.
 */
@layer structure {
  .probe {
    max-width: 60rem;
    margin-inline: auto;
    padding: var(--space-5) var(--space-4) var(--space-8);
    display: flex;
    flex-direction: column;
    gap: var(--space-7);
  }

  .probe__head {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
  }

  .probe__group {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
  }

  .probe__grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
    gap: var(--space-3);
  }

  .probe__swatch {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    padding: var(--space-3);
    min-height: calc(var(--space-8) * var(--density));
  }

  .probe__row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
  }

  .probe__chip {
    display: inline-flex;
    align-items: center;
    padding: var(--space-1) var(--space-3);
    min-height: var(--tap-min);
  }

  .probe__scale {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
  }

  .probe__scale > * {
    display: block;
  }
}

/* ===== skin/gate.css ===== */
/*
 * SKIN -- the login gate. Tokens only.
 */
@layer skin {
  .gate__field input {
    background: var(--surface-raised);
    color: var(--text-primary);
    border: var(--border-width) solid var(--line-hairline);
    border-radius: var(--radius-md);
    font-family: var(--font-data);
    letter-spacing: var(--tracking-wide);
  }

  .gate__field input:focus-visible {
    border-color: var(--line-focus);
  }

  .gate__form button {
    background: var(--accent);
    color: var(--accent-contrast);
    border: var(--border-width) solid var(--accent);
    border-radius: var(--radius-md);
    font-family: var(--font-data);
    font-size: var(--scale--1);
    font-weight: var(--weight-semibold);
    letter-spacing: var(--tracking-wide);
    text-transform: uppercase;
    transition: background var(--dur-fast) var(--ease-standard);
  }

  .gate__form button:hover {
    background: var(--accent-hover);
    border-color: var(--accent-hover);
  }

  /* The picker buttons carry each person's author colour, so the choice reads
   * as identity rather than as a generic form control. */
  .gate__choices button[data-author="1"] {
    background: var(--author-1-accent);
    border-color: var(--author-1-accent);
    color: var(--author-1-contrast);
  }

  .gate__choices button[data-author="2"] {
    background: var(--author-2-accent);
    border-color: var(--author-2-accent);
    color: var(--author-2-contrast);
  }

  .gate__choices button[data-author="3"] {
    background: var(--author-3-accent);
    border-color: var(--author-3-accent);
    color: var(--author-3-contrast);
  }

  .gate__error {
    color: var(--danger-contrast);
    background: var(--danger-bg);
    border-left: calc(var(--border-width) * 2) solid var(--danger);
    padding: var(--space-2) var(--space-3);
    font-size: var(--scale--1);
  }
}

/* ===== skin/probe.css ===== */
/*
 * SKIN -- theme probe.
 *
 * Colour, type and shape only, and every value is a token. Because `skin` sits
 * after `structure` in the layer order, these rules win on the properties they
 * own without any specificity games.
 *
 * State is selected on data attributes, not colour-carrying class names, so the
 * skin reads meaning rather than appearance.
 */
@layer skin {
  .probe__group > h2 {
    padding-bottom: var(--space-2);
    border-bottom: var(--border-width) solid var(--line-hairline);
  }

  .probe__swatch {
    border: var(--border-width) solid var(--line-hairline);
    border-radius: var(--radius-md);
    background: var(--surface-raised);
  }

  .probe__chip {
    border: var(--border-width) solid currentColor;
    border-radius: var(--radius-pill);
    font-family: var(--font-data);
    font-size: var(--scale--1);
    letter-spacing: var(--tracking-wide);
    text-transform: uppercase;
  }

  /* Agreement state -- the whole point of tier 3. Weight climbs with state. */
  .probe__chip[data-state="floated"] {
    color: var(--state-floated-fg);
    background: var(--state-floated-bg);
    border-color: var(--state-floated-line);
    border-style: dashed;
    font-weight: var(--weight-normal);
  }

  .probe__chip[data-state="agreed"] {
    color: var(--state-agreed-fg);
    background: var(--state-agreed-bg);
    border-color: var(--state-agreed-line);
    font-weight: var(--weight-medium);
  }

  .probe__chip[data-state="locked"] {
    color: var(--state-locked-fg);
    background: var(--state-locked-bg);
    border-color: var(--state-locked-line);
    border-width: calc(var(--border-width) * 2);
    font-weight: var(--weight-semibold);
  }

  .probe__chip[data-vote="yes"] {
    color: var(--vote-yes);
  }
  .probe__chip[data-vote="either"] {
    color: var(--vote-either);
  }
  .probe__chip[data-vote="rather-not"] {
    color: var(--vote-rather-not);
  }

  .probe__chip[data-aging="fresh"] {
    color: var(--aging-fresh);
  }
  .probe__chip[data-aging="stale"] {
    color: var(--aging-stale);
  }
  .probe__chip[data-aging="rotting"] {
    color: var(--aging-rotting);
  }

  /* Day types drive the trip's spine being visible at a glance. */
  .probe__swatch[data-day="prep"] {
    background: var(--day-prep-bg);
    border-color: var(--day-prep-accent);
  }
  .probe__swatch[data-day="install"] {
    background: var(--day-install-bg);
    border-color: var(--day-install-accent);
  }
  .probe__swatch[data-day="show"] {
    background: var(--day-show-bg);
    border-color: var(--day-show-accent);
  }
  .probe__swatch[data-day="deinstall"] {
    background: var(--day-deinstall-bg);
    border-color: var(--day-deinstall-accent);
  }
  .probe__swatch[data-day="free"] {
    background: var(--day-free-bg);
    border-color: var(--day-free-accent);
  }
  .probe__swatch[data-day="travel"] {
    background: var(--day-travel-bg);
    border-color: var(--day-travel-accent);
  }
  .probe__swatch[data-day="protected"] {
    background: var(--day-protected-bg);
    border-color: var(--day-protected-accent);
  }

  .probe__swatch[data-author="1"] {
    background: var(--author-1-bg);
    border-color: var(--author-1-accent);
  }
  .probe__swatch[data-author="2"] {
    background: var(--author-2-bg);
    border-color: var(--author-2-accent);
  }
  .probe__swatch[data-author="3"] {
    background: var(--author-3-bg);
    border-color: var(--author-3-accent);
  }

  .probe__swatch[data-feedback="ok"] {
    background: var(--ok-bg);
    color: var(--ok-contrast);
    border-color: var(--ok);
  }
  .probe__swatch[data-feedback="warn"] {
    background: var(--warn-bg);
    color: var(--warn-contrast);
    border-color: var(--warn);
  }
  .probe__swatch[data-feedback="danger"] {
    background: var(--danger-bg);
    color: var(--danger-contrast);
    border-color: var(--danger);
  }
  .probe__swatch[data-feedback="info"] {
    background: var(--info-bg);
    color: var(--info-contrast);
    border-color: var(--info);
  }

  .probe__swatch[data-accent="accent"] {
    background: var(--accent);
    color: var(--accent-contrast);
    border-color: var(--accent);
  }
}
