/* ============================================================================
   Matchue — the only stylesheet.
   Link it from every page: <link rel="stylesheet" href="styles.css">

   Three groups of color, and knowing which one you're reaching for is most of
   using this correctly:

   1. THEME ROLES  --bg --panel --line --ink --ink-dim
                   Derived at runtime from the reigning pair by theme.js.
                   Everything outside the gallery paints from these.
   2. HOUSE        --house-*
                   Fixed neutral. The default when no pair reigns, AND the
                   permanent chrome of the gallery, which never themes.
   3. BRAND        --brand-a --brand-b
                   Fixed Ember. The mark, favicon and app icons. Never
                   interface color.

   Plus --pair-a / --pair-b: the published pair a card is displaying, shown
   verbatim and never corrected.

   STRUCTURE — space, type, radius, motion, borders — is fixed and authored
   here, so a repaint reads as a color event and nothing else.

   There is still no accent and no semantic red/green. Matchue's *interface*
   has no fixed color; the logo does, because a logo that changes with the
   weather isn't a logo.
   ========================================================================= */

:root {
  /* --- HOUSE (fixed) ------------------------------------------------------
     The neutral the site wears when no pair has enough votes to reign — and
     the permanent chrome of the gallery, which never themes at all (see
     below). Deliberately colorless paper and near-black so a real winner
     reads as an event rather than a swap between two brands.

     Not hand-picked: exactly what deriveTheme("#f2f1ed", "#1c1b18") returns,
     so the pre-script paint matches the post-script paint and there is no
     flash of a wrong color. Regenerate rather than eyeball if these change. */
  --house-bg: #f2f1ed;
  --house-panel: #e2e1dd;
  --house-line: #b6b5b1;
  --house-ink: #1c1b18;
  --house-ink-dim: #6d6c69;

  /* --- BRAND (fixed) ------------------------------------------------------
     Ember. The mark, the favicon and the app icons are drawn in these two and
     nothing else. Not theme roles, never derived from a pair: the logo has to
     stay recognizable whatever the crowd votes for, and an installed app icon
     is cached by the OS regardless. Never use them as interface color. */
  --brand-a: #1c3145;   /* deep blue — the mark's left half                 */
  --brand-b: #f0a86a;   /* gold — the mark's right half                     */

  /* --- THEME ROLES (runtime) ----------------------------------------------
     Overwritten by theme.js when a pair reigns; fall back to house until
     then. Everything outside the gallery paints from these. */
  --bg: var(--house-bg);            /* the ground — pair color A, verbatim  */
  --panel: var(--house-panel);      /* raised surfaces: panels, the crown   */
  --line: var(--house-line);        /* borders and rules                    */
  --ink: var(--house-ink);          /* body and UI text — B, corrected      */
  --ink-dim: var(--house-ink-dim);  /* labels and secondary — floored at AA */
  --pair-a: #f2f1ed;    /* A exactly as published — swatches only           */
  --pair-b: #1c1b18;    /* B exactly as published — swatches only           */

  /* --- type -------------------------------------------------------------- */
  --font-display: 'Fraunces', Georgia, serif;
  --font-ui: 'Space Grotesk', 'Helvetica Neue', Arial, sans-serif;

  /* Space Grotesk is proportional, so figures do not align by default. Hex
     codes, vote counts and ratios sit in columns and must line up, hence the
     tabular-nums utility applied to those roles below. */
  --numeric: tabular-nums;

  --text-3xl: 34px;   /* page title                                         */
  --text-2xl: 22px;   /* section heading                                    */
  --text-xl: 19px;    /* swatch sample, preview heading                     */
  --text-lg: 16px;    /* panel title                                        */
  --text-md: 15px;    /* pair name                                          */
  --text-base: 13px;  /* inputs, body                                       */
  --text-sm: 12px;    /* secondary                                          */
  --text-xs: 11px;    /* small caps labels                                  */
  --text-2xs: 9px;    /* badge                                              */

  --leading-tight: 1.3;
  --leading-normal: 1.6;
  --leading-loose: 1.8;

  --track-tight: -0.03em;  /* display at 58px and up                        */
  --track-snug: -0.01em;   /* display below 58px                            */
  --track-label: 0.08em;   /* small caps labels                             */
  --track-caps: 0.12em;    /* the subtitle rail                             */
  --track-wide: 0.22em;    /* the tagline, and only the tagline             */

  /* --- space (4px base) --------------------------------------------------- */
  --space-1: 4px;
  --space-2: 6px;
  --space-3: 8px;
  --space-4: 10px;
  --space-5: 12px;
  --space-6: 14px;
  --space-7: 16px;
  --space-8: 20px;
  --space-9: 24px;
  --space-10: 34px;
  --space-11: 44px;
  --space-12: 80px;

  /* --- radius ------------------------------------------------------------- */
  --radius-xs: 4px;      /* swatch dots                                      */
  --radius-sm: 8px;      /* buttons, inputs                                  */
  --radius-md: 10px;     /* chips, vote controls, tabs                       */
  --radius-lg: 16px;     /* cards, panels                                    */
  --radius-pill: 999px;  /* badges, toast                                    */

  /* --- borders ------------------------------------------------------------ */
  --border: 1px;             /* structural                                   */
  --border-interactive: 1.5px; /* anything clickable                         */
  --border-emphasis: 2px;    /* the reigning pair                            */

  /* --- motion ------------------------------------------------------------- */
  --motion-theme: 0.5s ease;  /* the repaint — slow enough to read as an event */
  --motion-ui: 0.15s ease;    /* hover, press, focus                         */

  /* --- layout ------------------------------------------------------------- */
  --measure: 1080px;
}

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

/* ============================================================ base =========*/

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

html, body {
  margin: 0;
  min-height: 100%;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-ui);
  font-size: var(--text-base);
  transition: background var(--motion-theme), color var(--motion-theme);
}

/* One focus treatment, everywhere. The site previously killed outlines on
   inputs and replaced them with a border-color change, which is invisible to
   anyone tabbing through — this is the fix, and it must not be overridden
   per component. */
:focus-visible {
  outline: var(--border-emphasis) solid var(--ink);
  outline-offset: 2px;
  border-radius: var(--radius-xs);
}
:focus:not(:focus-visible) { outline: none; }

::selection { background: var(--ink); color: var(--bg); }

.wrap { max-width: var(--measure); margin: 0 auto; padding: var(--space-10) var(--space-8) var(--space-12); }

/* ============================================================ type =========*/

.display, h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: 600;
  letter-spacing: var(--track-snug);
  margin: 0;
}
h1 { font-size: var(--text-3xl); line-height: var(--leading-tight); }
h2 { font-size: var(--text-2xl); }
h3 { font-size: var(--text-xl); }

/* The small-caps label. Matchue's most-used text object by far — every hint,
   every meta line, every tab. */
.label {
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: var(--track-label);
  color: var(--ink-dim);
}
.label-lg { font-size: var(--text-sm); letter-spacing: var(--track-caps); }
.tagline { font-size: var(--text-sm); text-transform: uppercase; letter-spacing: var(--track-wide); color: var(--ink-dim); }

.hint { font-size: var(--text-xs); line-height: var(--leading-normal); color: var(--ink-dim); }
.meta { font-size: var(--text-sm); color: var(--ink-dim); line-height: var(--leading-normal); }

/* ============================================================ brand ========*/

/* The mark: one rounded square split down the middle, deep blue then gold.
   Corner radius is always 28% of its size — that ratio is the mark, so scale
   it with `--mark-size` rather than restyling the corners.

   It paints --brand-a / --brand-b, NOT the theme roles, so it holds still
   while everything around it repaints. The one fixed point in a site that has
   no fixed colors. */
.mark {
  --mark-size: 32px;
  width: var(--mark-size);
  height: var(--mark-size);
  border-radius: calc(var(--mark-size) * 0.28);
  overflow: hidden;
  display: flex;
  flex-shrink: 0;
}
.mark > :first-child { flex: 1; background: var(--brand-a); }
.mark > :last-child { flex: 1; background: var(--brand-b); }
.mark svg { display: block; width: 100%; height: 100%; }

/* THE RING IS NOT OPTIONAL. The mark's two colors are fixed but the ground
   behind it is whatever a crowd voted for, so sooner or later one half lands
   on a near-identical surface and dissolves into a half-shape. Not
   hypothetical: the gold half sits at 1.77:1 against the house paper, and the
   blue half at 1.00:1 against Ember's own blue should that pair ever win.
   There is no ground on which both halves are safe — which is why the ring is
   built into .mark rather than offered as a modifier you have to remember.

   An --ink ring fixes both edges at once: it outlines whichever half has gone
   invisible, and where it merges into a half that already contrasts with the
   page it costs nothing. On an --ink surface the relationship inverts, so add
   .on-ink to the container and the ring flips to --bg. Use .mark-plain only
   where the surface matches neither half — rare, and worth a comment. */
.mark { box-shadow: 0 0 0 1.5px var(--ring, var(--ink)); }
.on-ink { --ring: var(--bg); }
.mark-plain { box-shadow: none; }

.lockup { display: flex; align-items: center; gap: var(--space-5); }
.lockup .wordmark {
  font-family: var(--font-display);
  font-weight: 600;
  letter-spacing: var(--track-snug);
  line-height: 1;
  color: var(--ink);
}
.lockup-stacked { flex-direction: column; gap: var(--space-7); text-align: center; }
/* Display sizes tighten: -0.03em from 58px up. */
.lockup-lg .wordmark { font-size: 82px; letter-spacing: var(--track-tight); }
.lockup-md .wordmark { font-size: 58px; letter-spacing: var(--track-tight); }
.lockup-sm .wordmark { font-size: var(--text-3xl); }

/* ==================================================== the gallery ==========
   THE CARDS DO NOT THEME. Everything else on the page repaints with the
   winning pair; the gallery deliberately does not, and paints --house-*
   throughout.

   The reason is perceptual, not stylistic. A swatch is judged against what
   surrounds it — the same gold reads warm on a blue ground and muddy on a
   cream one. If the card chrome wore the reigning pair, every published pair
   in the gallery would be tinted by whichever pair happened to be winning,
   and people would be voting on a comparison the site had rigged. Neutral
   chrome is the white wall a gallery hangs work on.

   So: .card and everything inside it uses --house-bg / --house-panel /
   --house-line / --house-ink / --house-ink-dim. The only colors in a card
   that move are --pair-a / --pair-b, which are the published pair itself.
   ========================================================================= */

/* ========================================================== surfaces =======*/

.panel {
  background: var(--panel);
  border: var(--border) solid var(--line);
  border-radius: var(--radius-lg);
  padding: var(--space-9);
  transition: background var(--motion-theme), border-color var(--motion-theme);
}
.panel-title { font-family: var(--font-display); font-size: var(--text-lg); font-weight: 600; margin: 0 0 var(--space-1); }

.card {
  /* This is the whole mechanism, and it is deliberately four lines rather than
     forty. Custom properties inherit, so rebinding the theme roles here means
     every descendant — card body, hex chips, vote buttons, ratio, badge — goes
     neutral without any of their own rules changing, and any component added
     to a card later inherits neutrality for free. --pair-a / --pair-b are
     pointedly NOT rebound: those are the published pair, the one thing in here
     that must show its real color. */
  --bg: var(--house-bg);
  --panel: var(--house-panel);
  --line: var(--house-line);
  --ink: var(--house-ink);
  --ink-dim: var(--house-ink-dim);

  /* Explicitly re-resolve `color` here. Rebinding --ink above is not enough:
     `color` is an inherited property, so body resolves var(--ink) at body — in
     the themed value — and descendants inherit that computed color, not the
     variable. Without this line a card's text comes out in the winning pair's
     ink on a neutral card. Any inherited property that reads a theme role has
     the same trap. */
  color: var(--ink);

  background: var(--panel);
  border: var(--border) solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  /* No theme transition: the card has nothing to transition to. */
}
.card.is-reigning { border-color: var(--ink); border-width: var(--border-emphasis); }
.card-body { padding: var(--space-6); display: flex; flex-direction: column; gap: var(--space-4); }
.card-name { font-family: var(--font-display); font-size: var(--text-md); font-weight: 600; line-height: var(--leading-tight); word-break: break-word; }
.card-unnamed { font-size: var(--text-xs); text-transform: uppercase; letter-spacing: var(--track-label); color: var(--ink-dim); }

.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(258px, 1fr)); gap: var(--space-7); }

/* ========================================================== swatches =======*/

/* Always paints --pair-a / --pair-b, never --bg / --ink. A published pair is
   the product; showing a legibility-corrected version of it would be a lie. */
.swatch { height: 108px; position: relative; display: flex; }
.swatch > div { flex: 1; }
.swatch-a { background: var(--pair-a); }
.swatch-b { background: var(--pair-b); }
.swatch-sample {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  font-family: var(--font-display); font-size: var(--text-xl);
  letter-spacing: var(--track-snug);
}

.badge {
  position: absolute; top: var(--space-4); right: var(--space-4);
  font-size: var(--text-2xs); text-transform: uppercase; letter-spacing: 0.1em;
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-pill);
}

/* ========================================================== controls =======*/

.btn {
  flex-shrink: 0;
  font-family: var(--font-ui);
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  background: var(--ink);
  color: var(--bg);
  border: none;
  border-radius: var(--radius-sm);
  padding: var(--space-4) var(--space-5);
  cursor: pointer;
  transition: opacity var(--motion-ui), background var(--motion-theme), color var(--motion-theme);
}
.btn:hover { opacity: 0.85; }
.btn:active { opacity: 0.7; }
.btn:disabled { opacity: 0.4; cursor: not-allowed; }

.btn-ghost {
  background: transparent;
  color: var(--ink);
  border: var(--border-interactive) solid var(--line);
  transition: border-color var(--motion-ui), background var(--motion-theme), color var(--motion-theme);
}
.btn-ghost:hover { opacity: 1; border-color: var(--ink); }
.btn-ghost.is-on { border-color: var(--ink); }
.btn-lg { padding: var(--space-5) 22px; font-size: var(--text-sm); }
.btn-sm { padding: 7px var(--space-4); font-size: 10px; }

/* Segmented choice — the A/B slot tabs and the top/new sort. */
.seg { display: flex; gap: var(--space-3); }
.seg-opt {
  flex: 1;
  display: flex; align-items: center; gap: var(--space-3);
  font-family: var(--font-ui); font-size: var(--text-xs);
  text-transform: uppercase; letter-spacing: 0.06em;
  background: transparent; color: var(--ink-dim);
  border: var(--border-interactive) solid var(--line);
  border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4);
  cursor: pointer;
  transition: border-color var(--motion-ui), color var(--motion-ui);
}
.seg-opt:hover { color: var(--ink); }
.seg-opt.is-active { border-color: var(--ink); color: var(--ink); }
.seg-dot { width: 16px; height: 16px; border-radius: 5px; border: var(--border) solid var(--line); flex-shrink: 0; }

/* Vote controls. */
.vote-row { display: flex; align-items: center; gap: var(--space-3); }
.vote-btn {
  display: flex; align-items: center; gap: var(--space-2);
  font-family: var(--font-ui); font-size: var(--text-sm);
  background: transparent; color: var(--ink);
  border: var(--border-interactive) solid var(--line);
  border-radius: var(--radius-md);
  padding: 7px var(--space-4);
  cursor: pointer;
  transition: border-color var(--motion-ui), background var(--motion-ui), color var(--motion-ui);
}
.vote-btn:hover { border-color: var(--ink); }
.vote-btn.is-on { background: var(--ink); color: var(--bg); border-color: var(--ink); }
.vote-btn svg { width: 14px; height: 14px; }
.ratio { margin-left: auto; font-size: var(--text-xs); color: var(--ink-dim); text-align: right;
         line-height: 1.4; font-variant-numeric: var(--numeric); }

/* The copyable hex chip. */
.hex-chip {
  flex: 1;
  display: flex; align-items: center; gap: 7px;
  border: var(--border) solid var(--line);
  border-radius: var(--radius-md);
  padding: 7px var(--space-3);
  background: transparent; color: var(--ink);
  cursor: pointer;
  font-family: var(--font-ui); font-size: var(--text-sm);
  transition: border-color var(--motion-ui);
}
.hex-chip:hover { border-color: var(--ink); }
.hex-chip .dot { width: 15px; height: 15px; border-radius: var(--radius-xs); border: var(--border) solid var(--line); flex-shrink: 0; }
.hex-chip .lbl { flex: 1; text-align: left; font-variant-numeric: var(--numeric); }
.hexes { display: flex; gap: var(--space-3); }

/* ============================================================ forms ========*/

.field { display: flex; flex-direction: column; gap: var(--space-2); }
.field > label { font-size: var(--text-xs); text-transform: uppercase; letter-spacing: 0.06em; color: var(--ink-dim); }

.input {
  width: 100%; min-width: 0;
  font-family: var(--font-ui); font-size: var(--text-base);
  background: var(--bg);
  border: var(--border) solid var(--line);
  border-radius: var(--radius-sm);
  padding: var(--space-4) 11px;
  color: var(--ink);
  transition: border-color var(--motion-ui), background var(--motion-theme), color var(--motion-theme);
}
.input:hover { border-color: var(--ink-dim); }
/* No `outline: none` here — the global :focus-visible ring is the focus state. */
.input-num { width: 56px; flex-shrink: 0; text-align: center; background: var(--panel);
             padding: var(--space-2); font-variant-numeric: var(--numeric); }
.input-hex { font-size: 14px; padding: 9px var(--space-4); font-variant-numeric: var(--numeric); }

.range {
  width: 100%; -webkit-appearance: none; appearance: none;
  height: 14px; border-radius: 7px; outline: none;
  background: linear-gradient(90deg, #000, #fff);
}
/* The thumb reads against arbitrary gradient stops, so it needs its own
   contrast: a --panel fill ringed in --ink rather than a hardcoded white. */
.range::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 22px; height: 22px; border-radius: 50%;
  background: var(--panel); border: 3px solid var(--ink);
  cursor: pointer; margin-top: -4px;
}
.range::-moz-range-thumb {
  width: 22px; height: 22px; border-radius: 50%;
  background: var(--panel); border: 3px solid var(--ink);
  cursor: pointer;
}
.range-hue {
  background: linear-gradient(90deg,
    hsl(0,80%,55%), hsl(60,80%,55%), hsl(120,80%,55%),
    hsl(180,80%,55%), hsl(240,80%,55%), hsl(300,80%,55%), hsl(360,80%,55%));
}

/* ========================================================== feedback =======*/

/* The reigning-pair banner. */
.crown {
  display: flex; align-items: center; gap: var(--space-6); flex-wrap: wrap;
  background: var(--panel);
  border: var(--border) solid var(--line);
  border-radius: var(--radius-lg);
  padding: var(--space-6) 18px;
  transition: background var(--motion-theme), border-color var(--motion-theme);
}
.crown-chips { display: flex; gap: var(--space-2); }
.crown-chip { width: 30px; height: 30px; border-radius: var(--radius-sm); border: var(--border) solid var(--line); }
.crown-text { font-size: var(--text-sm); line-height: var(--leading-normal); color: var(--ink-dim); flex: 1; min-width: 200px; }
.crown-text b { color: var(--ink); font-weight: 600; }

.toast {
  position: fixed; bottom: var(--space-9); left: 50%;
  transform: translateX(-50%) translateY(20px);
  background: var(--ink); color: var(--bg);
  font-size: var(--text-sm);
  padding: 11px 18px;
  border-radius: var(--radius-pill);
  opacity: 0;
  transition: opacity 0.2s, transform 0.2s;
  pointer-events: none;
  z-index: 50;
}
.toast.is-visible { opacity: 1; transform: translateX(-50%) translateY(0); }

.empty { padding: 40px var(--space-8); text-align: center; color: var(--ink-dim); font-size: var(--text-base); line-height: 1.7; }

/* ==================================================== head-to-head =========
   The arena — two pairs, pick one or neither. The site's primary interaction:
   ranking is Elo over these comparisons, and `neither` is the absolute-quality
   signal that decides whether anything deserves to reign at all.

   Like the gallery, the arena does NOT theme. The two pairs are being judged
   against each other, so the chrome stays house-neutral and neither option is
   flattered by the winning pair's colors — the same white-wall logic, and the
   same four-line rebind. Only the option swatches move, each painting its own
   --pair-a / --pair-b (set per option, since the two options are two pairs). */
.arena {
  --bg: var(--house-bg);
  --panel: var(--house-panel);
  --line: var(--house-line);
  --ink: var(--house-ink);
  --ink-dim: var(--house-ink-dim);
  color: var(--ink);
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
}
.arena-prompt {
  font-family: var(--font-display);
  font-size: var(--text-lg);
  font-weight: 600;
  text-align: center;
  margin: 0;
}
.arena-pairs { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-6); }

/* A whole option is one button. Weight, not color, carries "clickable" — 1.5px
   like every other interactive border, moving to full ink on hover. */
.arena-option {
  display: flex;
  flex-direction: column;
  font: inherit;
  text-align: left;
  color: var(--ink);
  background: var(--panel);
  border: var(--border-interactive) solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
  cursor: pointer;
  padding: 0;
  transition: border-color var(--motion-ui);
}
.arena-option:hover { border-color: var(--ink); }
.arena-swatch { height: 150px; position: relative; display: flex; }
.arena-swatch > div { flex: 1; }
.arena-swatch-sample {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  font-family: var(--font-display); font-size: var(--text-2xl); letter-spacing: var(--track-snug);
}
.arena-cap { padding: var(--space-4) var(--space-5); display: flex; flex-direction: column; gap: var(--space-1); }
.arena-name { font-family: var(--font-display); font-size: var(--text-md); font-weight: 600; line-height: var(--leading-tight); word-break: break-word; }
.arena-unnamed { font-size: var(--text-xs); text-transform: uppercase; letter-spacing: var(--track-label); color: var(--ink-dim); }
.arena-hex { font-size: var(--text-xs); color: var(--ink-dim); font-variant-numeric: var(--numeric); }

/* "Neither" is deliberately quieter than the two options: it's the escape hatch
   that says none of these deserves to win, not a third contender. Centered,
   ghost, its own row. */
.arena-actions { display: flex; justify-content: center; }
.arena-neither { min-width: 180px; }
