/* site/app/screens/bookings.css — Tickets.
 * Depends on tokens.css + ui.css + shell.css. Loaded after them.
 *
 * The whole screen is one contrast: a live ticket is an object you hold up, a used one is a
 * receipt in a pile. Nothing here is styled to sit between those two sizes, because the
 * moment they read as the same card at different scales the screen stops answering the only
 * question a player has when they open it — "what do I need in the next hour?"
 *
 * ⚠ No hex codes in this file. Every colour is a token, so all of it follows the app into
 *   dark mode; a raw hex would be a light-mode ticket floating on a navy page at 9pm.
 */

/* ── Section furniture ─────────────────────────────────────────────────────
 * shell.css styles `.sec-head button` in the action colour. The count on the right is a
 * SPAN, not a button, and deliberately: orange is the one thing you are meant to press, and
 * on this screen that is "Manage the session". A second orange thing would make both weaker.
 */
.tkts .sec-count{
  font: 500 12px/1 var(--font-ui); letter-spacing: .01em;
  color: var(--ink-tertiary); font-variant-numeric: tabular-nums;
}

/* ── The live ticket ───────────────────────────────────────────────────────
 * Navy ground, cream ink — DESIGN.md's identity surface, and the highest-contrast pair the
 * brand owns. That matters here more than anywhere: this is read at arm's length, outdoors,
 * by someone who is not looking at their own phone.
 */
.tkts .tkt{
  --tkt-ground: var(--navy);
  --tkt-ink: var(--cream);
  --tkt-dim: color-mix(in srgb, var(--cream) 62%, transparent);
  --tkt-hair: color-mix(in srgb, var(--cream) 22%, transparent);

  position: relative; overflow: hidden;
  margin: 0 var(--gutter) 18px; padding: 20px 18px 18px;
  border-radius: var(--r-xl);
  background: var(--tkt-ground); color: var(--tkt-ink);
  box-shadow: var(--shadow-2);
}

/* At night the page ground is already navy, so a navy ticket would vanish into it. Lift the
   ticket one step and give it a hairline instead of relying on a shadow nobody can see. */
@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]) .tkts .tkt{
    --tkt-ground: var(--navy-90);
    border: 1px solid var(--tkt-hair);
  }
}
:root[data-theme="dark"] .tkts .tkt{
  --tkt-ground: var(--navy-90);
  border: 1px solid var(--tkt-hair);
}

.tkts .tkt-top{
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  margin-bottom: 12px;
}
.tkts .tkt-kind{
  font: 600 11px/1 var(--font-ui); letter-spacing: .09em; text-transform: uppercase;
  color: var(--tkt-dim);
}
/* The default .tag from shell.css is a cream chip on a cream page; on navy it disappears. */
.tkts .tkt .tag:not(.live){
  background: color-mix(in srgb, var(--cream) 14%, transparent);
  color: var(--tkt-ink);
}

.tkts .tkt-title{
  font-family: var(--font-display); font-weight: 600;
  font-size: 26px; line-height: 1.1; letter-spacing: -.018em;
  color: var(--tkt-ink); margin: 0;
}
.tkts .tkt-sub{
  margin: 4px 0 0; font: 400 13px/1.4 var(--font-ui); color: var(--tkt-dim);
}

/* ── The code plate ───────────────────────────────────────────────────────
 * Always a light plate, in both themes, because the canvas inside it is always drawn dark on
 * light. A scanner reads contrast, not taste.
 *
 * ⚠ THE QUIET ZONE IS NOW INSIDE THE CANVAS, NOT THIS PADDING. qr.js draws four clear
 *   modules on every side of the code, because a margin that lives in CSS is a margin that a
 *   crop, a screenshot or the next person restyling this plate can take away — and a QR with
 *   no quiet zone is one a scanner may never lock on to. The padding here is now only the
 *   visual inset of the plate; the code is legal without it.
 * ⚠ --tkt-code-size MIRRORS QR_SIZE IN qr.js. Change one, change the other. The cap
 *   matters on a narrow phone: without it a 231px canvas plus padding overflows a 320px
 *   screen's card, and the code is clipped rather than shrunk. */
.tkts .tkt-plate{
  --tkt-code-size: 231px;
  margin: 18px auto 10px;
  width: 100%; max-width: calc(var(--tkt-code-size) + 20px); padding: 10px;
  background: var(--cream); border-radius: var(--r-md);
}
/* height:auto with no inline height (see drawCode) so a shrunk code stays square. A stretched
   code is not a code. */
/* ⚠ aspect-ratio, NOT AN INLINE HEIGHT. A <canvas> with no width/height attributes set yet
   reports the HTML default 300×150, so between the node being appended and drawCode running in
   the next frame the plate would flash a 2:1 rectangle — and if drawing ever failed it would
   stay one. Squared here, so the plate is the right shape before a single module is painted. */
.tkts .tkt-canvas{
  display: block; margin-inline: auto;
  width: var(--tkt-code-size); max-width: 100%; height: auto; aspect-ratio: 1 / 1;
}

.tkts .tkt-code{
  text-align: center;
  font: 500 12px/1 var(--font-mono); letter-spacing: .12em;
  color: var(--tkt-dim); font-variant-numeric: tabular-nums;
}

/* ── Holder ────────────────────────────────────────────────────────────────
 * Second-largest thing on the ticket after the code. The door check is a person reading this
 * name and looking at a face; DESIGN.md §7 makes that the entire defence against a forwarded
 * screenshot, so it does not get shrunk to make room for anything.
 */
.tkts .tkt-holder{ text-align: center; margin-top: 16px; }
.tkts .tkt-name{
  font-family: var(--font-display); font-weight: 600;
  font-size: 27px; line-height: 1.1; letter-spacing: -.02em; color: var(--tkt-ink);
}
.tkts .tkt-handle{
  margin-top: 3px; font: 500 14px/1 var(--font-ui); color: var(--tkt-dim);
}

/* ── The perforation ──────────────────────────────────────────────────────
 * A dashed rule with a bite taken out of both edges. The bites are circles in the PAGE
 * colour, half outside the card, clipped by the card's own overflow — so they follow the
 * theme for free and never need a second colour of their own.
 */
.tkts .tkt-perf{
  position: relative; height: 0;
  margin: 18px -18px;
  border-top: 1.5px dashed var(--tkt-hair);
}
.tkts .tkt-perf::before, .tkts .tkt-perf::after{
  content: ""; position: absolute; top: 0;
  width: 22px; height: 22px; border-radius: 50%;
  background: var(--bg); transform: translateY(-50%);
}
.tkts .tkt-perf::before{ left: -11px; }
.tkts .tkt-perf::after{ right: -11px; }

/* ── The stub ─────────────────────────────────────────────────────────────── */
.tkts .tkt-facts{
  display: grid; grid-template-columns: 1fr 1fr; gap: 12px 14px;
  margin: 0;
}
.tkts .tkt-facts .fact{ min-width: 0; }
.tkts .tkt-facts dt{
  font: 600 10px/1 var(--font-ui); letter-spacing: .08em; text-transform: uppercase;
  color: var(--tkt-dim);
}
.tkts .tkt-facts dd{
  margin: 5px 0 0; font: 500 15px/1.25 var(--font-ui); letter-spacing: -.006em;
  color: var(--tkt-ink); font-variant-numeric: tabular-nums;
}

.tkts .tkt-with{
  margin: 14px 0 0; font: 400 13px/1.45 var(--font-ui); color: var(--tkt-dim);
}

/* ── The one action on this screen ────────────────────────────────────────── */
.tkts .tkt-score{ margin-top: 18px; }

.tkts .soon{
  display: block; margin-top: 10px; padding: 14px;
  border-radius: var(--r-md);
  background: color-mix(in srgb, var(--cream) 10%, transparent);
  border: 1px solid var(--tkt-hair);
  opacity: 1; transform: translateY(0);
  transition: opacity var(--t-menu) var(--ease-out), transform var(--t-menu) var(--ease-out);
}
@starting-style{ .tkts .soon{ opacity: 0; transform: translateY(-4px); } }
/* `hidden` loses to the display above unless it is said again. */
.tkts .soon[hidden]{ display: none; }
/* ⚠ NO `.soon p` RULE ANY MORE. The panel used to carry a paragraph explaining what the
   scorekeeper will do and which question about it is still open — the app narrating its own
   roadmap, struck out with the rest of that voice on 2026-09-03. The panel is now one
   state line, so a rule for a paragraph that cannot exist would be a trap for the next
   person who adds one and wonders why it is styled. */
.tkts .soon b{
  display: block; font: 600 14px/1.3 var(--font-ui); letter-spacing: -.006em;
  color: var(--tkt-ink);
}

/* ── The door note ────────────────────────────────────────────────────────── */
.tkts .tkt-note{ margin: 2px var(--gutter) 0; }
.tkts .tkt-note p{
  margin: 0; font: 400 13px/1.5 var(--font-ui); color: var(--ink-tertiary);
}

/* ── The pile ─────────────────────────────────────────────────────────────
 * Absolutely-positioned cards inside a container whose height JS sets. Each card's `translate`,
 * `scale` and `opacity` belong to springs the compositor plays (bookings.js, motion.js's glide()) —
 * nothing in CSS may transition or declare them here: a transition would start on every resting
 * write and outrank the run in the cascade, and a declaration would be dead under the inline value.
 */
.tkts .pile{ margin: 0 var(--gutter); }

.tkts .pile-stack{
  position: relative;
  /* Scaling shrinks each card from its top edge, so the ones behind peek out below. Scaling
     from the centre would pull their bottom edges UP and the stack would look like one card
     with a shadow. */
  transform-origin: top center;
}

.tkts .pcard{
  position: absolute; top: 0; left: 0; right: 0;
  transform-origin: top center;
  will-change: translate, scale, opacity;
  background: var(--bg-raised); border-radius: var(--r-lg);
  box-shadow: var(--shadow-1);
  overflow: hidden;
}

.tkts .pcard-face{
  display: block; width: 100%; text-align: left;
  border: 0; background: transparent; cursor: pointer;
  padding: 14px 16px; color: inherit;
  touch-action: manipulation; -webkit-tap-highlight-color: transparent;
  transition: transform var(--t-press) var(--ease-out);
}
.tkts .pcard-head{
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
}
.tkts .pcard-title{
  font: 600 15px/1.25 var(--font-ui); letter-spacing: -.008em; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tkts .pcard-meta{
  display: flex; flex-wrap: wrap; gap: 5px; margin-top: 5px;
  font: 400 13px/1.35 var(--font-ui); color: var(--ink-tertiary);
  font-variant-numeric: tabular-nums;
}
.tkts .pcard-dot{ opacity: .5; }

@media (hover: hover) and (pointer: fine){
  .tkts .pile.is-open .pcard-face:hover{ background: var(--bg-sunken); }
}

/* The tap target for the collapsed stack. One control, over the whole pile. */
.tkts .pile-hit{
  position: absolute; inset: 0; z-index: 50;
  border: 0; background: transparent; cursor: pointer; padding: 0;
  /* ⚠ inset:0 MEANS THIS CONTROL IS AS SHORT AS THE PILE, AND THE COLLAPSED PILE IS 42px.
     It looked exempt from the 44px floor because it has no painted box of its own — but it
     is the only way to open the stack, and an invisible target is measured by the thumb like
     any other. It grows downward into the 12px gap above .pile-hint, so nothing is covered. */
  min-height: var(--tap-min);
  touch-action: manipulation; -webkit-tap-highlight-color: transparent;
}
.tkts .pile-hit[hidden]{ display: none; }

.tkts .pile-hint{
  margin: 12px 0 0; text-align: center;
  font: 500 12px/1 var(--font-ui); letter-spacing: .01em; color: var(--ink-tertiary);
}
.tkts .pile-hint[hidden]{ display: none; }

.tkts .pile-less{
  display: block; width: 100%; margin-top: 12px;
  border: 0; background: transparent; cursor: pointer;
  min-height: var(--tap-min);
  font: 600 13px/1 var(--font-ui); letter-spacing: -.004em; color: var(--ink-tertiary);
  touch-action: manipulation; -webkit-tap-highlight-color: transparent;
  transition: transform var(--t-press) var(--ease-out);
}
.tkts .pile-less[hidden]{ display: none; }

/* The pile with nothing in it, on a real account (bookings.js pastFrameCard): one still card in the
   flow, the shell's `.skel` where the words go. No spring owns it, so it sits where it is drawn. */
.tkts .pile.is-frame .pcard{ position: relative; }
.tkts .pile.is-frame .pcard-face{ cursor: default; }
.tkts .pile.is-frame .pcard-meta{ align-items: center; gap: 8px; min-height: 17px; }
.tkts .pcard-skel-t{ height: 12px; }
/* (Under "No tickets" the pile used to be pulled out to the empty box's 20px; every card is on the
   page gutter now, the two offers above it included, so the pile's own margin is the line.) */

/* ── The receipt ──────────────────────────────────────────────────────────
 * Appears instantly, then the cards below spring down around it. The alternative — animating
 * the card's own height — is the one thing the house rules forbid outright, and it would also
 * make every measurement below it wrong for the length of the animation.
 */
.tkts .pcard-receipt{ display: none; padding: 0 16px 16px; }
.tkts .pcard.is-open .pcard-receipt{ display: block; }

.tkts .receipt-stamp{
  display: inline-block;
  font: 700 11px/1 var(--font-ui); letter-spacing: .16em; text-transform: uppercase;
  color: var(--ink-tertiary);
  border: 1.5px solid var(--rule); border-radius: var(--r-sm);
  padding: 6px 10px; transform: rotate(-3deg); transform-origin: left center;
  margin-bottom: 12px;
}

.tkts .receipt-scan{
  margin: 0 0 12px; font: 500 13px/1.45 var(--font-ui); color: var(--ink-secondary);
  font-variant-numeric: tabular-nums;
}

.tkts .receipt-facts{
  margin: 0; padding-top: 12px; border-top: 1px solid var(--rule);
  display: grid; gap: 9px;
}
.tkts .receipt-facts .rfact{
  display: flex; align-items: baseline; justify-content: space-between; gap: 14px;
}
.tkts .receipt-facts dt{
  flex: none; font: 600 11px/1.3 var(--font-ui); letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink-tertiary);
}
.tkts .receipt-facts dd{
  margin: 0; text-align: right; min-width: 0;
  font: 400 13px/1.4 var(--font-ui); color: var(--ink);
  font-variant-numeric: tabular-nums; overflow-wrap: anywhere;
}

/* ── Empty state ──────────────────────────────────────────────────────────── */
/* ══ THE EMPTY TICKETS SCREEN — TWO DOORS ═══════════════════════════════════
   Syd, 2026-09-16: "upsell the open play and private bookings in a nice way", "how apple would do
   it", and of what was here, "this looks like AI slop". emptyState() in bookings.js carries the
   reasoning; this is the surface.

   ⚠ NO `.empty` CLASS ON THE CONTAINER ANY MORE. shell.css's `.empty` centres its text and draws a
     dashed box — the visual grammar for "there is nothing here", which is exactly the message this
     screen stopped sending. It is a left-aligned stack of real offers now. */
.tkts .tkt-empty{ padding: 14px var(--gutter) 22px; }

/* ⚠ A HEADING AND A RULE, NOT A SENTENCE AT THE READER. The heading names the state in two words
   and the line under it states the policy in the third person — see bookings.js for the voice rule
   this replaced a breach of. */
.tkts .tkt-empty-h{
  margin: 0 0 4px;
  font-family: var(--font-display); font-weight: 600;
  font-size: 19px; line-height: 1.2; letter-spacing: -.014em; color: var(--ink);
}
.tkts .tkt-empty-quiet{
  margin: 0; color: var(--ink-secondary);
  font: 400 13.5px/1.45 var(--font-ui);
}
.tkts .tkt-eyebrow{
  margin: 21px 0 9px;
  font: 650 10.5px/1 var(--font-ui); letter-spacing: .13em; text-transform: uppercase;
  color: var(--ink-tertiary);
}

/* ── The two doors ──────────────────────────────────────────────────────────
   ⚠ A <button> THAT LOOKS LIKE A CARD, not a card with a button in it. The whole offer is the tap
     target — 
     there is exactly one thing to do with each of these, and a card whose only control is a small
     pill at the bottom makes a thumb aim for the pill. */
.tkts .tkt-card{
  display: block; width: 100%; text-align: left;
  border-radius: 18px; padding: 15px 15px 14px;
  border: 1px solid transparent; background: transparent;
  color: inherit; font: inherit; cursor: pointer;
  -webkit-tap-highlight-color: transparent; touch-action: manipulation;
}
.tkts .tkt-card.is-hero{ background: var(--bg-raised); border-color: var(--rule); }
.tkts .tkt-card.is-second{ margin-top: 10px; border-color: var(--rule); }
.tkts .tkt-card:focus-visible{ outline: 2px solid var(--focus); outline-offset: 2px; }

.tkts .tkt-card-top{
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  margin-bottom: 9px;
}
.tkts .tkt-live{
  display: inline-flex; align-items: center; gap: 6px;
  font: 600 12px/1 var(--font-ui); color: var(--ink);
}
/* ⚠ CHARTREUSE IS "LIVE", NEVER "SUCCESS" — the brand guide's own words. It marks the one fact on
   this screen that is happening right now, and nothing else. */
.tkts .tkt-livedot{
  position: relative;
  width: 7px; height: 7px; border-radius: 50%; flex: none;
  background: var(--chartreuse);
}
/* ⚠⚠ THE PULSE IS A DISC THAT GROWS AND FADES, NOT A box-shadow THAT SPREADS — the same ring, drawn
   by the compositor. A box-shadow animation repaints on the page's main thread every frame, forever,
   for as long as a live ticket is on screen: a 60 fps loop on a 120 Hz phone (Syd, 2026-10-04: "the
   animations … look like 30 fps when the page scrolls with like 140 fps"), and work the CPU does
   while the player is only reading. The old ring was the dot's own colour at 50% → 0%, spreading
   0 → 7px around a 3.5px radius: a 7px disc scaled ×3 (3.5 + 7 = 10.5) while its opacity goes
   .5 → 0 is that ring, edge for edge. It sits over the dot, which is invisible: chartreuse over
   chartreuse. */
.tkts .tkt-livedot::after{
  content: ""; position: absolute; inset: 0; border-radius: 50%;
  background: var(--chartreuse); opacity: 0; pointer-events: none;
}
[data-ready] .tkts .tkt-livedot::after{ animation: tkt-pulse 2.6s var(--ease-out) infinite; }
@keyframes tkt-pulse{
  0%   { opacity: .5; transform: scale(1); }
  70%  { opacity: 0;  transform: scale(3); }
  100% { opacity: 0;  transform: scale(1); }
}
.tkts .tkt-spots{
  font: 600 12px/1 var(--font-ui); color: var(--ink-secondary);
  font-variant-numeric: tabular-nums;
}
.tkts .tkt-card-h{
  margin: 0; font-family: var(--font-display); font-weight: 600;
  font-size: 19px; line-height: 1.18; letter-spacing: -.014em; color: var(--ink);
}
/* The heading's skeleton while the sessions read has said nothing: 22px tall, as the heading is. */
.tkts .tkt-skel-h{ height: 14px; margin: 4px 0; }
.tkts .tkt-meta{
  margin: 5px 0 0; font: 500 13px/1.4 var(--font-ui); color: var(--ink-secondary);
  font-variant-numeric: tabular-nums;
}
.tkts .tkt-dot{ opacity: .5; margin: 0 6px; }
.tkts .tkt-price{ color: var(--ink); font-weight: 600; }

/* ⚠⚠ THE ONLY ORANGE ON THIS SCREEN. tokens.css: orange is what you are meant to press and nothing
   else. It sits on the single act the screen exists to produce — joining tonight's session — and
   the court door gets a chevron instead, which is the hierarchy stated in colour as well as fill. */
.tkts .tkt-go{
  display: grid; place-items: center; width: 100%;
  margin-top: 13px; min-height: var(--tap-min); padding: 12px 18px;
  border-radius: var(--r-full); background: var(--action); color: var(--action-ink);
  font: 650 15px/1 var(--font-ui);
}
.tkts .tkt-go-quiet{
  display: flex; align-items: center; justify-content: space-between;
  margin-top: 12px; padding-top: 11px; border-top: 1px solid var(--rule);
  min-height: 34px;
  font: 600 14.5px/1 var(--font-ui); color: var(--ink);
}
.tkts .tkt-chev{ color: var(--ink-tertiary); font-size: 17px; line-height: 1; }

/* ── Arrival ────────────────────────────────────────────────────────────────
   ⚠⚠ A TRANSITION OFF A CLASS, NOT AN ANIMATION WITH fill-mode: both — AND THE FIRST VERSION OF
     THIS SHIPPED THE BUG ITS OWN COMMENT WARNED ABOUT. `both` holds the keyframe's FROM state
     through the delay, so a card sits at opacity 0 until the animation runs; in a document where
     animations never advance it sits there for good. Measured: both cards invisible, the eyebrow
     and the quiet line above them fine, in a browser pane that reports itself hidden.
   ⚠ THE REST STATE IS THE VISIBLE ONE. `.is-in` is the displaced state, added at build time and
     removed by a setTimeout — and setTimeout runs in a hidden document where rAF and animations do
     not, so the worst case is the finished layout with no movement, never a blank one. Same shape
     as takeover.js's enter, for the same reason.
   ⚠ CRITICALLY DAMPED, NO OVERSHOOT: no gesture preceded these, so there is no momentum to express
     (apple-design — bounce is for what a finger threw). */
[data-ready] .tkts .tkt-card{
  transition: opacity 520ms cubic-bezier(.22,.61,.36,1),
              transform 520ms cubic-bezier(.22,.61,.36,1);
}
.tkts .tkt-card.is-in{ opacity: 0; transform: translateY(9px) scale(.985); }

@media (prefers-reduced-motion: reduce){
  [data-ready] .tkts .tkt-card{ transition: none; }
  .tkts .tkt-card.is-in{ opacity: 1; transform: none; }
  [data-ready] .tkts .tkt-livedot::after{ animation: none; }
}

/* ── Waiting for the first answer ─────────────────────────────────────────
 * PLAN.md §5: loading has no words, and a spinner only once the wait has lasted 600 ms —
 * bookings.js adds `.is-on` on a timer, so a read that answers quickly never shows one. A ring in
 * the rule colour with one darker arc: the quietest thing that still says "working".
 */
.tkts .tkt-wait{
  width: 22px; height: 22px; margin: 28px auto 0;
  border-radius: 50%;
  border: 2px solid var(--rule); border-top-color: var(--ink-tertiary);
  opacity: 0;
}
.tkts .tkt-wait.is-on{ opacity: 1; animation: tkt-spin 800ms linear infinite; }
@keyframes tkt-spin{ to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce){
  .tkts .tkt-wait.is-on{ animation: none; }
}

/* ── Prototype control ────────────────────────────────────────────────────
 * Not part of the product, and drawn in sample mode only (bookings.js): it exists so the
 * first-run state can be seen in the samples. A real account never sees it.
 */
/* 32px tall before the min-height: under the 44px floor even for scaffolding. */
.tkts .demo-toggle{
  display: block; margin: 26px auto 0;
  border: 1px dashed var(--rule); background: transparent; cursor: pointer;
  border-radius: var(--r-full); padding: 10px 16px; min-height: var(--tap-min);
  font: 500 12px/1 var(--font-ui); color: var(--ink-tertiary);
  touch-action: manipulation; -webkit-tap-highlight-color: transparent;
  transition: transform var(--t-press) var(--ease-out);
}

/* ── Reduced motion, transparency, contrast ───────────────────────────────
 * Naming this file's own animations, per DESIGN.md §4.8. The pile's springs already collapse
 * to a jump inside motion.js when reduce is set — spring().to() checks the query at call time
 * — so what is left here is the press feedback and the receipt's entrance.
 */
@media (prefers-reduced-motion: reduce){
  .tkts .pcard-face, .tkts .pile-less, .tkts .demo-toggle{ transition-property: background-color; }
  .tkts .soon{ transition-property: opacity; transform: none; }
  @starting-style{ .tkts .soon{ opacity: 0; transform: none; } }
  .tkts .receipt-stamp{ transform: none; }
}

@media (prefers-contrast: more){
  .tkts .tkt{ --tkt-dim: var(--tkt-ink); --tkt-hair: var(--tkt-ink); }
  .tkts .pcard{ border: 1px solid var(--rule); }
  .tkts .receipt-stamp{ border-color: var(--ink); color: var(--ink); }
}

/* ⚠⚠ THE TICKET FOR THE SESSION HAPPENING NOW. Syd, 2026-09-03: "when you are scanned into
   your session, your ticket for that BOOKING should be highlighted / floating."
   Every ticket in this list is paid and valid; exactly one of them is the thing you are
   standing inside, and drawing all of them identically makes you read four dates to find it.
   ⚠ LIFTED AND EDGED, NOT TINTED. A tint on a card that is already a pale plate on a pale
     ground is worth about two shades and disappears in sunlight. A shadow and a coloured edge
     read as depth, which is a difference the eye finds without looking for it — and the
     chartreuse edge is the same "this is happening now" this app uses everywhere else.
   ⚠ NO ANIMATION ON THE LIFT. "Floating" is a resting state, not a loop: a card that pulses
     in a list is the one thing you cannot then read. DESIGN.md §4.1 — anything seen on every
     visit gets its animation removed. */
.tkts .tkt.is-live {
  border-color: color-mix(in srgb, var(--live) 62%, transparent);
  box-shadow: var(--shadow-3), 0 0 0 3px color-mix(in srgb, var(--live) 20%, transparent);
  transform: translateY(-2px);
}
@media (prefers-contrast: more) {
  .tkts .tkt.is-live { border-color: var(--ink); box-shadow: none; }
}
