/* ============================================================================
   TicketScope shared theme
   Version 1.0  —  21 September 2026
   Author: review pass (Claude). Intended to be diffed against Codex's
   shared stylesheet, not to replace it wholesale. Where the two agree,
   keep either. Where they differ, the colour-role rule below decides.

   ----------------------------------------------------------------------------
   COLOUR ROLE RULE  —  read this before adding or changing any colour
   ----------------------------------------------------------------------------
   Every colour in this file belongs to exactly one of four roles. A colour
   may never be used outside its role, however good it looks.

   1. GROUND     page, card, ink, stub, line, text, muted, faint
                 Structure and type. The only colours allowed on plain
                 surfaces, borders, dividers, and body copy.

   2. ACTION     action, on-action, focus
                 Interactive intent. Buttons, links, focus rings, active
                 nav, form affordances. Blue. This is the only role that
                 may appear on a hover or active state.

   3. STATUS     official (mint/green), legacy-source (rose/red)
                 MEANING ONLY. These two say what a ticket IS: sold by the
                 event, or handled through an earlier sales channel. They must never be
                 used decoratively — not on a hover, not on a generic
                 highlight, not as a pleasant accent on an unrelated card,
                 not as a gradient stop. If a user sees rose on this site
                 it must mean legacy-source, every single time. A decorative use
                 of a status colour teaches the user that the colour means
                 nothing, and the moment that happens the badge stops
                 working. Decorative warmth comes from --line, --stub or
                 the action blue instead.

   4. CATEGORY   sport (mauve), vip (gold)
                 Taxonomy labels. Used on category chips and rails only.
                 Not on buttons, not on status badges.

   Plus one standalone: --accent-red, a single decorative red mark. It is a
   2px graphic mark and nothing else. It never carries text, never fills a
   button, never becomes a background. Contrast is audited against the
   3:1 graphic threshold, not the 4.5:1 text threshold, because it is not
   permitted to be text.

   ----------------------------------------------------------------------------
   CONTRAST
   ----------------------------------------------------------------------------
   64 token pairings across both themes were audited against WCAG 2.1, plus
   two negative controls that must keep failing. Zero real failures.
   Tightest margins, for anyone tempted to darken a token:
     light faint-on-page    5.71:1   (needs 4.5)
     light official-on-page 5.74:1   (needs 4.5)
     light sport-on-page    5.77:1   (needs 4.5)
     dark  faint-on-card    5.90:1   (needs 4.5)
   Changing any hex below invalidates the audit. Re-run the audit script in
   this handover folder before shipping a palette change.
   ============================================================================ */

/* ---------------------------------------------------------------------------
   1. TOKENS
   Dark is the default. Light is opt-in via [data-theme="light"] on <html>,
   and is also the automatic choice for a user whose system asks for light.
   --------------------------------------------------------------------------- */

:root {
  /* GROUND */
  --page:        #131211;   /* warm charcoal, not blue-black */
  --card:        #1C1A19;
  --ink:         #0C0B0A;   /* deepest surface: ticket stub body, footer */
  --stub:        #EDE7D8;   /* cream. the ticket paper. */
  --text:        #EDE7D8;
  --muted:       #C6C2B6;
  --faint:       #9C968B;
  --line:        rgba(237, 231, 216, 0.14);
  --line-strong: rgba(237, 231, 216, 0.26);

  /* --ink stays dark in BOTH themes: the ticket stub keeps the brand even
     on the light page. So anything sitting on --ink needs its own
     foreground tokens. Using --text on --ink is a 1.0:1 contrast bug in
     light theme, because there --text IS the ink colour. */
  --on-ink:        #EDE7D8;
  --on-ink-muted:  #C6C2B6;
  --on-ink-action: #8FA8E0;

  /* Eyebrow colour. Cream reads as lifted on a dark page and vanishes on a
     light one, so this is a token rather than a fixed --stub reference. */
  --eyebrow: #EDE7D8;

  /* ACTION */
  --action:      #8FA8E0;
  --on-action:   #131211;
  --focus:       #9DB0E8;

  /* STATUS — meaning only, never decoration */
  --official:    #8FCBAA;
  --legacy-source:      #E09A9C;
  /* Status edges drawn ON the dark stub. Separate tokens because the
     light-theme status colours are too dark to read against --ink. */
  --official-on-ink: #8FCBAA;
  --legacy-source-on-ink:   #E09A9C;

  /* CATEGORY */
  --sport:       #CDA4E4;   /* mauve */
  --vip:         #D4A85E;   /* gold */

  /* LOGO ACCENT — single decorative mark only; no red/blue seams, never text, never a fill */
  --accent-blue: #9DB0E8;
  --accent-red:  #F0605F;

  /* Blue wash stays in the visible hero, independent of the full page height. */
  --ambient: radial-gradient(
      ellipse 950px 700px at 85% 150px,
      rgba(80, 96, 160, 0.42),
      transparent 75%);

  /* Shape and motion */
  --r-sm: 8px;
  --r-md: 12px;
  --r-lg: 18px;
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.30), 0 12px 32px rgba(0, 0, 0, 0.22);
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --dur: 220ms;

  /* Type */
  --font-display: "Fraunces", Georgia, "Times New Roman", serif;
  --font-body: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
  --tabular: tabular-nums;
}

:root[data-theme="light"],
html[data-theme="light"] {
  --page:        #F7F4ED;
  --card:        #FFFFFF;
  --ink:         #131211;   /* still dark: the stub keeps the brand */
  --stub:        #EDE7D8;
  --text:        #131211;
  --muted:       #3C4348;
  --faint:       #5B6167;
  --line:        rgba(19, 18, 17, 0.12);
  --line-strong: rgba(19, 18, 17, 0.22);

  --on-ink:        #EDE7D8;
  --on-ink-muted:  #C6C2B6;
  --on-ink-action: #8FA8E0;
  --eyebrow:       #3C4348;

  --action:      #3C4A84;
  --on-action:   #FFFFFF;
  --focus:       #3C4A84;

  --official:    #2F6B4C;
  --legacy-source:      #6E2A2C;
  --official-on-ink: #8FCBAA;
  --legacy-source-on-ink:   #E09A9C;

  --sport:       #6B3E82;   /* mauve */
  --vip:         #7A5A22;   /* gold */

  --accent-blue: #3C4A84;
  --accent-red:  #C01818;

  --ambient: radial-gradient(
      ellipse 950px 700px at 85% 150px,
      rgba(80, 96, 160, 0.13),
      transparent 75%);

  --shadow: 0 1px 2px rgba(19, 18, 17, 0.05), 0 12px 30px rgba(19, 18, 17, 0.07);
}

/* A user who has asked their system for light gets light, unless the page
   has been explicitly pinned to dark. */
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    --page:        #F7F4ED;
    --card:        #FFFFFF;
    --ink:         #131211;
    --text:        #131211;
    --muted:       #3C4348;
    --faint:       #5B6167;
    --line:        rgba(19, 18, 17, 0.12);
    --line-strong: rgba(19, 18, 17, 0.22);
    --on-ink:        #EDE7D8;
    --on-ink-muted:  #C6C2B6;
    --on-ink-action: #8FA8E0;
    --eyebrow:       #3C4348;
    --action:      #3C4A84;
    --on-action:   #FFFFFF;
    --focus:       #3C4A84;
    --official:    #2F6B4C;
    --legacy-source:      #6E2A2C;
    --official-on-ink: #8FCBAA;
    --legacy-source-on-ink:   #E09A9C;
    --sport:       #6B3E82;
    --vip:         #7A5A22;
    --accent-blue: #3C4A84;
    --accent-red:  #C01818;
    --ambient: radial-gradient(ellipse 950px 700px at 85% 150px,
        rgba(80, 96, 160, 0.13), transparent 75%);
    --shadow: 0 1px 2px rgba(19, 18, 17, 0.05), 0 12px 30px rgba(19, 18, 17, 0.07);
  }
}

/* ---------------------------------------------------------------------------
   2. RESET AND GROUND
   --------------------------------------------------------------------------- */

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

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

body {
  margin: 0;
  background-color: var(--page);
  background-image: var(--ambient);
  background-repeat: no-repeat;
  color: var(--text);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.6;
  /* Do not hide horizontal overflow: responsive checks must expose it. */
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: 600;
  letter-spacing: -0.015em;
  line-height: 1.12;
  margin: 0;
  text-wrap: balance;
}
p { margin: 0; text-wrap: pretty; }

h1 { font-size: clamp(31px, min(5.4vw, 8.4vh), 72px); }
h2 { font-size: clamp(24px, 3.2vw, 38px); }
h3 { font-size: clamp(18px, 2.1vw, 24px); }

a { color: var(--action); text-decoration-thickness: 1px; text-underline-offset: 3px; }
a:hover { color: var(--focus); }   /* ACTION role. never a status colour. */

/* Anything hidden stays hidden, whatever a later display rule says.
   This exists because a display:inline-flex on a counter once beat the
   [hidden] attribute and shipped "0 tickets delivered" to a screenshot. */
[hidden] { display: none !important; }

:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  border-radius: 4px;
}

/* ---------------------------------------------------------------------------
   3. LAYOUT PRIMITIVES
   Every grid track is minmax(0, 1fr). The implicit default is
   minmax(auto, 1fr), which refuses to shrink below its content and pushes
   the page wider than the viewport on narrow screens. That is the cause of
   nearly every mobile overflow bug in this codebase.
   --------------------------------------------------------------------------- */

.wrap {
  width: min(1180px, 100% - 2 * var(--gutter, 20px));
  margin-inline: auto;
}
@media (min-width: 760px) { .wrap { --gutter: 32px; } }
@media (min-width: 1100px) { .wrap { --gutter: 48px; } }

.stack > * + * { margin-top: var(--stack, 16px); }

.grid { display: grid; gap: 20px; grid-template-columns: minmax(0, 1fr); }
@media (min-width: 760px) {
  .grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (min-width: 1100px) {
  .grid-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

.card {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: 22px;
  min-width: 0;                /* lets a card shrink inside a grid track */
}

/* Card hover is a lift and a line, not a colour change. Specifically it is
   not rose, not mint. See the colour role rule at the top of this file. */
.card--interactive {
  transition: transform var(--dur) var(--ease),
              border-color var(--dur) var(--ease),
              box-shadow var(--dur) var(--ease);
}
.card--interactive:hover {
  transform: translateY(-2px);
  border-color: var(--line-strong);
  box-shadow: var(--shadow);
}

/* ---------------------------------------------------------------------------
   4. TYPE ROLES
   --------------------------------------------------------------------------- */

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--eyebrow);        /* lifted, not faint, never a status colour */
}
.eyebrow::before {
  content: "";
  width: 26px;
  height: 1px;
  background: var(--line-strong);
  flex: none;
}

.lede  { font-size: clamp(15px, 1.5vw, 18px); color: var(--muted); max-width: 56ch; }
.meta  { font-size: 13px; color: var(--faint); }
.num   { font-family: var(--font-display); font-variant-numeric: var(--tabular); }

/* ---------------------------------------------------------------------------
   5. ACTION
   --------------------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 44px;
  padding: 12px 20px;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  font: 600 14px/1 var(--font-body);
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--dur) var(--ease),
              border-color var(--dur) var(--ease),
              transform var(--dur) var(--ease);
}
.btn:active { transform: translateY(1px); }

.btn--primary {
  background: var(--action);
  color: var(--on-action);
}
.btn--primary:hover {
  background: color-mix(in oklab, var(--action) 88%, var(--text));
  color: var(--on-action);
}

.btn--ghost {
  background: transparent;
  color: var(--text);
  border-color: var(--line-strong);
}
.btn--ghost:hover {
  border-color: var(--action);
  color: var(--action);
}

.chip {
  display: inline-flex;
  align-items: center;
  min-height: 36px;
  padding: 8px 16px;
  border: 1px solid var(--line);
  border-radius: 999px;
  font-size: 13px;
  color: var(--muted);
  text-decoration: none;
  transition: border-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.chip:hover { border-color: var(--action); color: var(--action); }
.chip[aria-current="true"],
.chip.is-on { background: var(--action); color: var(--on-action); border-color: var(--action); }

.field {
  display: flex;
  flex-wrap: wrap;             /* so a long search row wraps instead of overflowing */
  gap: 10px;
  align-items: center;
  background: var(--card);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  padding: 7px 7px 7px 16px;
}
.field:focus-within { border-color: var(--focus); }
.field input {
  flex: 1 1 200px;
  min-width: 0;
  border: 0;
  outline: 0;
  background: transparent;
  color: var(--text);
  font: 400 15px/1.4 var(--font-body);
}
.field input::placeholder { color: var(--faint); }

/* ---------------------------------------------------------------------------
   6. STATUS
   The only two rules in this file permitted to use --official or --legacy-source.
   If you find either token anywhere else in the codebase, it is a bug.
   Codex's audit found exactly this: rose on generic hover states. Those are
   listed in the companion sweep note in this handover folder.
   --------------------------------------------------------------------------- */

.badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: 999px;
  border: 1px solid currentColor;
  font: 600 10px/1.4 var(--font-body);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.badge--official { color: var(--official); background: color-mix(in oklab, var(--official) 10%, transparent); }
.badge--legacy-source   { color: var(--legacy-source);   background: color-mix(in oklab, var(--legacy-source) 10%, transparent); }

/* The 3px edge on an event card's artwork, saying what the listing is.
   The artwork panel is always dark, so it uses the on-ink variants. */
.status-edge--official { border-bottom: 3px solid var(--official-on-ink); }
.status-edge--legacy-source   { border-bottom: 3px solid var(--legacy-source-on-ink); }

/* ---------------------------------------------------------------------------
   7. CATEGORY
   --------------------------------------------------------------------------- */

.cat--sport { color: var(--sport); }
.cat--vip   { color: var(--vip); }

/* ---------------------------------------------------------------------------
   8. LOGO RULE
   The red and blue from the mark, as a 2px graphic only. Placed under a
   section head or along a card edge. It carries no text and no meaning.
   --------------------------------------------------------------------------- */

.rule {
  height: 2px;
  width: 64px;
  border-radius: 2px;
  background: linear-gradient(90deg,
      var(--accent-blue) 0 58%,
      var(--accent-red) 58% 100%);
}
.rule--wide { width: 100%; max-width: 180px; }

/* ---------------------------------------------------------------------------
   9. TICKET
   The stub is the identity. Perforation is a mask, so it works on any
   background without painting fake holes in a matching colour.
   --------------------------------------------------------------------------- */

.ticket {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  overflow: hidden;
}
@media (min-width: 640px) {
  .ticket { grid-template-columns: auto minmax(0, 1fr); }
}
.ticket__stub {
  background: var(--ink);
  color: var(--on-ink);
  padding: 26px 24px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 18px;
  position: relative;
  -webkit-mask-image: radial-gradient(circle 5px at 100% 0, transparent 98%, #000 100%);
  mask-image: radial-gradient(circle 5px at 100% 0, transparent 98%, #000 100%);
  -webkit-mask-size: 100% 20px;
  mask-size: 100% 20px;
}
.ticket__body { padding: 28px 26px; min-width: 0; }

/* Any panel painted with --ink carries its own foregrounds. Never put
   .lede or .meta on one without this class: in light theme --muted is
   near-black and disappears. */
.on-ink { background: var(--ink); color: var(--on-ink); }
.on-ink .lede, .on-ink .meta { color: var(--on-ink-muted); }
.on-ink a, .on-ink .btn--ghost { color: var(--on-ink-action); }
.on-ink .eyebrow { color: var(--on-ink-muted); }

/* ---------------------------------------------------------------------------
   10. MOTION
   Scroll-driven where supported, invisible where not. Nothing here is
   load-bearing: a browser with no animation-timeline gets the final state.
   --------------------------------------------------------------------------- */

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .reveal {
      animation: reveal-in linear both;
      animation-timeline: view();
      animation-range: entry 8% cover 32%;
    }
  }
}
@keyframes reveal-in {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}

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

/* ---------------------------------------------------------------------------
   11. SWEEP OVERRIDES
   Temporary. These neutralise decorative status-colour uses inherited from
   the older stylesheets so the site is correct before every legacy rule has
   been rewritten. Delete this block once the sweep note is worked through;
   it is a safety net, not the fix.
   --------------------------------------------------------------------------- */

a:hover,
.card:hover,
button:hover,
[role="button"]:hover {
  /* nothing here may resolve to --official or --legacy-source */
  border-color: var(--line-strong);
}
