/* The buyer pages, in Ticket Talkies colours.
 *
 * The ticket stub is still the idea: card stock, a punched tear line, a stub,
 * and a reference number in a typewriter face. The perforation is a real
 * repeating radial-gradient of punched holes rather than a dashed border,
 * because a dashed rule reads as a divider and punched holes read as a ticket.
 * That part of the original design was right and none of it has moved.
 *
 * What changed on 7 September 2026 is the palette. This sheet used to carry a
 * warm-paper look of its own, so the site had two brands: the buyer walked
 * from a near-black home page onto a cream ticket page. Every value below is
 * now a --tt-* token from ticket-talkies.css, which is the one place the brand
 * is defined.
 *
 * That sheet loads first and this one second. Pages using it need class="tt"
 * on <body>, exactly as the organiser screens do.
 *
 * Every class name here is unchanged on purpose. app.js, event.js, ticket.js
 * and door.js write these names at runtime, and renaming them would mean
 * editing the code that sells tickets in order to change a colour. */

:root {
  /* Left as the old names so nothing downstream has to move. The values are
     the brand's, and the brand is defined once, in ticket-talkies.css. */
  --paper: var(--tt-bg);
  --paper-warm: var(--tt-surface-2);
  --card: var(--tt-surface);
  --card-sunk: var(--tt-surface-2);

  --ink: var(--tt-text);
  --ink-soft: var(--tt-text-soft);
  --ink-mute: var(--tt-muted);

  /* "Signal" now means attention, and nothing else. It used to do two jobs at
     once, the brand accent and the error colour, which is workable in
     vermilion on cream and not in a palette where the accent is cyan. The
     three decorative uses are named explicitly further down. */
  --signal: var(--tt-bad);
  --signal-deep: var(--tt-bad-soft);
  --signal-wash: rgba(255, 91, 108, .12);

  --blue: var(--tt-blue);
  --blue-wash: rgba(87, 165, 255, .12);
  --green: var(--tt-good);
  --green-wash: rgba(206, 255, 90, .12);
  --gold: var(--tt-warn);

  --line: var(--tt-line);
  --line-strong: var(--tt-line-strong);
  --shadow: 0 1px 0 rgba(0, 0, 0, .3), 0 18px 40px -24px rgba(0, 0, 0, .9);

  --sans: var(--tt-body);
  --mono: var(--tt-mono);

  --r: var(--tt-r);
  --wrap: 62rem;
}

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

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

/* body.tt owns the ground and the two brand glows behind it. This is only the
   fallback for a page that somehow loads this sheet on its own. */
body { margin: 0; background: var(--paper); color: var(--ink); font: 16px/1.55 var(--sans); }

/* body.tt is a column flex container, so main has to be told to take the space
   left over. Without this the footer floats up under a short page. */
body.tt > main { flex: 1 1 auto; }

.wrap { max-width: var(--wrap); margin: 0 auto; padding: 0 1.25rem; }
.wrap-wide { max-width: 72rem; }   /* agrees with .tt-wrap-wide */

h1, h2, h3 { line-height: 1.18; margin: 0 0 .5rem; letter-spacing: -.015em; }
h1 { font-size: clamp(1.9rem, 4.6vw, 2.9rem); }
h2 { font-size: clamp(1.35rem, 2.6vw, 1.8rem); }
h3 { font-size: 1.05rem; }
p { margin: 0 0 1rem; }
a { color: var(--signal-deep); }

.muted { color: var(--ink-mute); }
.soft { color: var(--ink-soft); }
.small { font-size: .875rem; }
.tiny { font-size: .78rem; }
.mono { font-family: var(--mono); letter-spacing: .04em; }
.center { text-align: center; }
.stack > * + * { margin-top: 1rem; }
.row { display: flex; gap: .75rem; flex-wrap: wrap; align-items: center; }
.grow { flex: 1 1 auto; }
[hidden] { display: none !important; }   /* author rules otherwise beat it */

/* ------------------------------------------------------------- eyebrow -- */
.eyebrow {
  font-size: .72rem; font-weight: 700; letter-spacing: .13em;
  text-transform: uppercase; color: var(--ink-mute);
}

/* ---------------------------------------------------------------- ticket -- */
/* The core component. A body, a punched line, a stub. */
.ticket {
  position: relative;
  display: grid;
  grid-template-columns: 1fr 11.5rem;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r);
  box-shadow: var(--shadow);
  overflow: hidden;
}
.ticket::before {                       /* the coloured spine */
  content: ""; position: absolute; inset: 0 auto 0 0; width: 5px; background: var(--tt-grad);
}
.ticket-body { padding: 1.35rem 1.5rem 1.35rem 1.75rem; min-width: 0; }
.ticket-stub {
  position: relative;
  padding: 1.35rem 1rem;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: .35rem; text-align: center;
  background: var(--card-sunk);
}
/* The perforation. Punched holes, not a dashed rule. */
.ticket-stub::before {
  content: ""; position: absolute; inset: .55rem auto .55rem -1px; width: 2px;
  background-image: radial-gradient(circle at 1px 3px, var(--line) 1.6px, transparent 1.8px);
  background-size: 2px 8px;
}
/* The two half-circles bitten out of the edges at the tear line. */
.ticket-stub::after {
  content: ""; position: absolute; left: -7px; top: -7px; width: 14px; height: 14px;
  border-radius: 50%; background: var(--paper); box-shadow: 0 calc(100% + 14px) 0 var(--paper);
}
@media (max-width: 34rem) {
  .ticket { grid-template-columns: 1fr; }
  .ticket-stub { border-top: 1px solid transparent; }
  .ticket-stub::before {
    inset: -1px .55rem auto .55rem; width: auto; height: 2px;
    background-image: radial-gradient(circle at 3px 1px, var(--line) 1.6px, transparent 1.8px);
    background-size: 8px 2px;
  }
  .ticket-stub::after {
    left: -7px; top: -7px; box-shadow: calc(100vw) 0 0 var(--paper);
  }
}

.ref {
  font-family: var(--mono); font-weight: 700; font-size: 1.3rem;
  color: var(--tt-cyan); letter-spacing: .06em;
}

/* ----------------------------------------------------------------- cards -- */
.card {
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--r); padding: 1.25rem; box-shadow: var(--shadow);
}
.card-flat { box-shadow: none; }
.card-sunk { background: var(--card-sunk); }
.note {
  border-left: 4px solid var(--gold); background: var(--tt-warn-bg);
  padding: .85rem 1rem; border-radius: 0 8px 8px 0; font-size: .92rem;
}
.note-bad { border-left-color: var(--signal); background: var(--signal-wash); }
.note-good { border-left-color: var(--green); background: var(--green-wash); }
.note-info { border-left-color: var(--blue); background: var(--blue-wash); }

/* --------------------------------------------------------------- buttons -- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .45rem;
  font: inherit; font-weight: 600; line-height: 1;
  min-height: 48px; padding: .85rem 1.2rem;
  border-radius: 10px; border: 1px solid var(--line-strong);
  background: var(--card-sunk); color: var(--ink); cursor: pointer; text-decoration: none;
  transition: background .15s ease, transform .06s ease, border-color .15s ease;
}
.btn:hover { background: var(--tt-hover); border-color: var(--tt-hover-line); }
.btn:active { transform: translateY(1px); }
.btn:disabled, .btn[aria-disabled="true"] { opacity: .45; cursor: not-allowed; }
.btn-signal { background: var(--tt-grad); border-color: transparent; color: var(--tt-on-accent); }
.btn-signal:hover { background: var(--tt-grad); filter: brightness(1.08); }
/* A gradient at 45 per cent on a near-black card is still the brightest thing
   on the screen, so a disabled action reads as the one to press. Dropping the
   gradient entirely is what makes it look unavailable rather than merely dim. */
.btn-signal:disabled, .btn-signal[aria-disabled="true"] {
  background: var(--card-sunk); color: var(--ink-mute); border-color: var(--line);
}
/* An anchor styled as a button loses to ".tt a", which sets the cyan link
   colour at the same specificity but later. Naming the element wins it back. */
.tt a.btn { color: var(--ink); }
.tt a.btn-signal { color: var(--tt-on-accent); }
.btn-ghost { background: transparent; color: var(--ink); border-color: var(--line); }
.btn-ghost:hover { background: var(--card-sunk); }
.btn-block { width: 100%; }
.btn-lg { padding: .95rem 1.4rem; font-size: 1.05rem; }

/* ----------------------------------------------------------------- forms -- */
label { display: block; font-weight: 600; font-size: .9rem; margin-bottom: .3rem; }
input[type="text"], input[type="email"], input[type="tel"], input[type="number"], select {
  width: 100%; font: inherit; padding: .8rem .9rem; min-height: 48px;
  border: 1px solid var(--line-strong); border-radius: 10px;
  background: var(--tt-bg); color: var(--ink);
}
input::placeholder { color: var(--tt-placeholder); }
/* A navy outline is invisible on a near-black page. */
input:focus-visible, select:focus-visible, .btn:focus-visible, .step:focus-visible,
.tt a:focus-visible {
  outline: 2px solid var(--tt-cyan); outline-offset: 2px;
}
.field + .field { margin-top: .85rem; }

/* --------------------------------------------------------------- tiers -- */
.checkout-grid {
  display: grid;
  gap: 1.5rem;
  grid-template-columns: minmax(0, 1.6fr) minmax(18rem, 1fr);
  align-items: start;
}

.tier {
  display: grid; grid-template-columns: 1fr auto; gap: 1rem; align-items: center;
  padding: .95rem 0; border-bottom: 1px solid var(--line);
}
.tier:last-child { border-bottom: 0; }
.tier-name { font-weight: 650; }
.tier-price { font-family: var(--mono); color: var(--ink-soft); font-size: .92rem; }
.tier.sold-out { opacity: .5; }

.stepper { display: inline-flex; align-items: center; gap: .1rem; }
.step {
  width: 2.3rem; height: 2.3rem; border: 1px solid var(--line); background: var(--card);
  font: inherit; font-size: 1.15rem; line-height: 1; cursor: pointer; color: var(--ink);
}
.step:first-child { border-radius: 8px 0 0 8px; }
.step:last-child { border-radius: 0 8px 8px 0; }
.step:disabled { opacity: .35; cursor: not-allowed; }
.qty {
  width: 2.6rem; text-align: center; font-family: var(--mono); font-weight: 700;
  border-block: 1px solid var(--line); height: 2.3rem; line-height: 2.3rem;
}

@media (max-width: 46rem) {
  .checkout-grid { grid-template-columns: minmax(0, 1fr); }
}

/* The running total on a phone. Hidden on desktop, where the order panel is
   already beside the tickets and nothing needs pinning. */
.sticky-buy { display: none; }

@media (max-width: 46rem) {
  .sticky-buy:not([hidden]) {
    position: fixed; inset: auto 0 0 0; z-index: 20;
    display: flex; align-items: center; gap: .9rem;
    padding: .7rem 1.25rem calc(.7rem + env(safe-area-inset-bottom, 0px));
    background: var(--card); border-top: 1px solid var(--line);
    box-shadow: 0 -6px 24px rgba(0, 0, 0, .55);
  }
  .sticky-buy-sum { display: flex; flex-direction: column; line-height: 1.25; }
  .sticky-buy-sum strong { font-size: 1.15rem; }
  .sticky-buy .btn { margin-left: auto; flex: none; }

  /* Stop the bar covering the last of the page when it is up. */
  body.has-sticky-buy .tt-foot { padding-bottom: 6rem; }
}

.chip {
  display: inline-block; padding: .18rem .5rem; border-radius: 999px;
  font-size: .72rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
}
.chip-good { background: var(--green-wash); color: var(--green); }
.chip-bad { background: var(--signal-wash); color: var(--signal-deep); }
.chip-info { background: var(--blue-wash); color: var(--blue); }

/* ----------------------------------------------------------------- misc -- */
/* White, on a near-black page, deliberately. A scanner needs a light quiet zone
   around the code and dark modules on it. Inverting this to match the brand is
   the change that makes tickets stop scanning at the door. */
.qr { background: #fff; padding: .6rem; border-radius: 8px; display: inline-block; }
.qr img { display: block; width: 100%; height: auto; max-width: 15rem; image-rendering: pixelated; }

table.data { width: 100%; border-collapse: collapse; font-size: .92rem; }
table.data th, table.data td { padding: .55rem .6rem; text-align: left; border-bottom: 1px solid var(--line); }
table.data th { font-size: .76rem; text-transform: uppercase; letter-spacing: .07em; color: var(--ink-mute); }
table.data td.num, table.data th.num { text-align: right; font-family: var(--mono); }
.scroll-x { overflow-x: auto; }

.site-foot {
  margin-top: 3.5rem; border-top: 1px solid var(--line);
  padding: 1.5rem 0 2.5rem; font-size: .85rem; color: var(--ink-mute);
}

.spinner {
  width: 1rem; height: 1rem; border: 2px solid currentColor; opacity: .9;
  border-top-color: transparent; border-radius: 50%; animation: spin .7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .001ms !important; transition-duration: .001ms !important; }
}

/* Print one ticket, because somebody always will. */
@media print {
  .tt-head, .tt-foot, .no-print { display: none !important; }
  body.tt { background: #fff; background-image: none; color: #17181a; }
  .tt-wrap, .wrap { max-width: none; }
  .ticket, .card {
    box-shadow: none; page-break-inside: avoid;
    background: #fff; border-color: #ddd6c9; color: #17181a;
  }
  .ticket::before { background: #17181a; }
  .ticket-stub { background: #faf7f1; }
  .ticket-stub::after { background: #fff; box-shadow: 0 calc(100% + 14px) 0 #fff; }
  .ref { color: #17181a; }
  .muted, .soft, .small, .tiny { color: #45484d; }
}
