/* Mobile-first. No fixed widths above 100%, nothing that can force sideways scroll at 390px.
   Component language ported from the live BoredAI page's own CSS
   (public/clients/sps/site/boredai.css) — glass seat tiles, accent glow, Big Shoulders Display
   for headings/amounts/buttons, Archivo for body, the label font the side-nav uses for small caps. */

:root {
  --heading-font: 'Big Shoulders Display', system-ui, sans-serif;
  --body-font: 'Archivo', system-ui, sans-serif;
  --label-font: 'Race Sport', 'Big Shoulders Display', sans-serif;
  --ease: cubic-bezier(.45,0,.25,1);
  --float: cubic-bezier(.45,0,.15,1);
  --accent-1: #3B8BFF;
  --accent-2: #86FF3C;
  --accent-3: #A66BFF;
  --bg: #03060E;
  --panel: #12151c;
  --text: #EDF1F8;
  --muted: rgba(237,241,248,.6);
  --danger: #FF8A8A;
  --ok: #3ddc84;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  min-width: 0;
  overflow-x: hidden;
  font-family: var(--body-font);
}

body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--body-font);
  -webkit-text-size-adjust: 100%;
  -webkit-font-smoothing: antialiased;
  position: relative;
  /* There's no real text input anywhere in this checkout — every value a buyer might want is
     already behind its own copy button (ui-kit.js's hash chips). Without this, plain static text
     (a heading, a seat tile, the footer) is still selectable by default, and tapping it collapses
     a zero-length text Selection right where the tap landed — which several mobile browsers draw
     as a blinking text caret, exactly the "cursor wherever ARK clicks" this fixes (batch 3, 7 Oct
     2026). Buttons and links keep working normally; this only stops plain text from behaving like
     an editable field it was never meant to be. */
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}

/* The one place manual selection still makes sense: an expanded hash chip's full value
   (ui-kit.js), for a buyer who wants to select it by hand instead of using its copy button. */
.hash-chip-full {
  -webkit-user-select: text;
  user-select: text;
}

/* ambient glow, same language as the BoredAI page's .atmo — a plain radial wash, no extra markup */
body::before {
  content: '';
  position: fixed;
  inset: -20% -20% auto -20%;
  height: 70vh;
  background:
    radial-gradient(900px 900px at 10% -10%, color-mix(in oklab, var(--accent-1) 22%, transparent), transparent 60%),
    radial-gradient(760px 760px at 90% -5%, color-mix(in oklab, var(--accent-1) 14%, transparent), transparent 60%);
  pointer-events: none;
  z-index: 0;
}

.app {
  position: relative;
  z-index: 1;
  max-width: 640px;
  margin: 0 auto;
  padding: 4.5rem 1rem 1rem;
  width: 100%;
}

.loading, .error {
  text-align: center;
  color: var(--muted);
  padding: 3rem 1rem;
}

h2, h3 {
  font-family: var(--heading-font);
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.01em;
  margin: 0 0 0.75rem;
  line-height: 0.95;
}

h2 { font-size: clamp(1.15rem, 5vw, 1.4rem); }

/* The BoredAI wordmark, ported from the landing page's own header (public/clients/sps/site/
   boredai.css's .wordmark/.brand-crest) — the crest image is the exact same file. */
.brand-wordmark {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin: 0 0 0.75rem;
}
.brand-wordmark .brand-crest {
  width: 46px;
  height: 46px;
  flex: none;
  object-fit: contain;
}
.brand-wordmark .wordmark-text {
  font-family: var(--heading-font);
  font-weight: 800;
  font-size: clamp(1.8rem, 7vw, 2.4rem);
  letter-spacing: 0.01em;
  text-transform: uppercase;
  line-height: 0.95;
}
.brand-wordmark .wordmark-text b {
  color: var(--accent-1);
  font-weight: 800;
}

.steps {
  display: flex;
  gap: 0.35rem;
  margin-bottom: 1rem;
  flex-wrap: wrap;
}

.step-dot {
  flex: 1;
  min-width: 0;
  height: 4px;
  border-radius: 2px;
  background: rgba(255,255,255,.1);
}

.step-dot.active { background: var(--accent-1); box-shadow: 0 0 10px var(--accent-1); }
.step-dot.done { background: var(--accent-2); }

.panel {
  --c: var(--accent-1);
  position: relative;
  background: linear-gradient(180deg, rgba(255,255,255,.06), rgba(255,255,255,.015));
  border: 1px solid rgba(255,255,255,.09);
  border-radius: 20px;
  padding: 1.1rem;
  margin-bottom: 1rem;
  word-break: break-word;
  backdrop-filter: blur(18px) saturate(140%);
  -webkit-backdrop-filter: blur(18px) saturate(140%);
  box-shadow: 0 20px 50px rgba(0,0,0,.35), 0 0 40px color-mix(in oklab, var(--c) 10%, transparent);
  animation: step-in .36s var(--ease) both;
}

/* The one shared step-transition (brief item 1, 8 Oct 2026) — every step's root .panel fades/
   lifts in with this same animation, no exceptions and never hand-tuned per screen. The outgoing
   step fades out via .step-ghost below (see app.js's mountStepGhost), a disposable, inert clone
   of the previous #step-body positioned exactly over where it was, so the two together read as
   one soft crossfade rather than the old instant swap. */
@keyframes step-in {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: translateY(0); }
}
.step-ghost {
  position: fixed;
  z-index: 55;
  margin: 0;
  pointer-events: none;
  opacity: 1;
  transform: translateY(0);
  transition: opacity .32s var(--ease), transform .32s var(--ease);
}
.step-ghost.step-ghost-out {
  opacity: 0;
  transform: translateY(-10px);
}
/* The ghost wraps a clone of the outgoing .panel, which would otherwise replay its own
   step-in entrance animation the instant it's inserted — fighting the ghost's own fade-out
   transition on the same property. The ghost's wrapper opacity is the only thing that should
   move here. */
.step-ghost .panel { animation: none; opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .panel { animation: none; }
  .step-ghost { transition: none; opacity: 0; }
}

/* Amount step (checkout-amount-not-seat brief, 8 Oct 2026) — a USD input plus a row of quick-pick
   chips, replacing the old numbered seat grid below. Chips wrap onto a second line on a narrow
   phone rather than scroll sideways — this checkout has a standing "no sideways scroll on phone"
   rule the old seat rail was the one deliberate exception to; the amount step is not another one. */
.amount-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin-bottom: 0.9rem;
}

.amount-chip {
  flex: 0 0 auto;
  width: auto;
  border: 1px solid rgba(255,255,255,.14);
  border-radius: 999px;
  padding: 0.5rem 1.1rem;
  background: rgba(255,255,255,.05);
  color: var(--text);
  /* Was var(--body-font) — the wrong, non-brand font for a chip/pill (ARK, 8 Oct 2026); every
     other pill/label in this app (.pill, footer, explorer-link) uses --label-font uppercase. */
  font-family: var(--label-font);
  text-transform: uppercase;
  letter-spacing: .02em;
  font-weight: 600;
  font-size: 0.9rem;
  cursor: pointer;
  transition: border-color .3s var(--ease), background .3s var(--ease);
}

.amount-chip:hover {
  border-color: color-mix(in oklab, var(--accent-1) 45%, transparent);
}

.amount-chip.selected {
  border-color: var(--accent-1);
  background: color-mix(in oklab, var(--accent-1) 18%, rgba(255,255,255,.05));
  box-shadow: 0 0 0 1px var(--accent-1) inset;
}

/* ApeFest 20%-off badge (ARK/Money/Njord, 12 Oct 2026): the full price struck through, the
   discounted price next to it in the accent colour — same "selected chip" accent so the two
   numbers read as one price, not two unrelated labels. Restyled (pay-page-polish brief, 12 Oct
   2026) to read as a real limited-time sale rather than small subtext: each tile now carries a
   pinned "-20%" corner tag and the charged price is sized and glowed well above the struck-through
   one, and the discount line above the tiles became its own lit banner instead of a plain
   .notice paragraph. */
.amount-chip {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.12rem;
  padding: 0.7rem 1.3rem 0.65rem;
}
.amount-chip-prices { display: flex; align-items: baseline; gap: 0.45rem; }
.amount-chip-full {
  text-decoration: line-through;
  opacity: 0.55;
  font-size: 0.82em;
}
.amount-chip-charged {
  color: color-mix(in oklab, var(--accent-1) 70%, white);
  font-weight: 800;
  font-size: 1.18em;
  text-shadow: 0 0 18px color-mix(in oklab, var(--accent-1) 35%, transparent);
}
.amount-chip-save {
  position: absolute;
  top: -0.6rem;
  right: -0.35rem;
  background: var(--accent-1);
  color: #04131f;
  font-family: var(--label-font);
  font-weight: 800;
  font-size: 0.62rem;
  letter-spacing: .03em;
  padding: 0.2rem 0.45rem;
  border-radius: 999px;
  box-shadow: 0 4px 10px rgba(0,0,0,.4);
}
/* Extra top margin so the pinned -20% tag above doesn't get clipped by the panel edge. */
.amount-chips { margin-top: 0.5rem; }

.apefest-banner {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin: 0.9rem 0 1.1rem;
  padding: 0.65rem 0.9rem;
  border-radius: 14px;
  background: linear-gradient(135deg, color-mix(in oklab, var(--accent-1) 30%, rgba(255,255,255,.04)), color-mix(in oklab, var(--accent-1) 10%, rgba(255,255,255,.02)));
  border: 1px solid color-mix(in oklab, var(--accent-1) 55%, transparent);
  box-shadow: 0 0 26px color-mix(in oklab, var(--accent-1) 22%, transparent);
}
.apefest-banner-percent {
  flex: 0 0 auto;
  font-family: var(--heading-font);
  font-weight: 800;
  font-size: 0.9rem;
  letter-spacing: .02em;
  text-transform: uppercase;
  color: #04131f;
  background: var(--accent-1);
  padding: 0.3rem 0.6rem;
  border-radius: 999px;
}
.apefest-banner-text {
  font-family: var(--body-font);
  font-size: 0.82rem;
  line-height: 1.35;
  font-weight: 600;
  color: color-mix(in oklab, var(--accent-1) 25%, var(--text));
}

/* The already-chosen amount, carried forward onto every checkout step between the amount screen
   and the quote/wallet-pay screens (which already show it themselves) — pay-page-polish brief,
   12 Oct 2026, item 1: a buyer used to see nothing confirming their pick once they left the
   amount screen. */
.checkout-amount-banner {
  display: flex;
  align-items: baseline;
  justify-content: center;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: 1.1rem;
  padding: 0.7rem 0.9rem;
  border-radius: 14px;
  background: rgba(255,255,255,.04);
  border: 1px solid rgba(255,255,255,.1);
}
.checkout-amount-label {
  font-family: var(--label-font);
  text-transform: uppercase;
  letter-spacing: .04em;
  font-size: 0.7rem;
  color: var(--muted);
}
.checkout-amount-full {
  text-decoration: line-through;
  opacity: 0.5;
  font-family: var(--heading-font);
  font-size: 0.95rem;
}
.checkout-amount-charged {
  font-family: var(--heading-font);
  font-weight: 800;
  font-size: 1.15rem;
  color: color-mix(in oklab, var(--accent-1) 70%, white);
}

.amount-input-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  background: rgba(255,255,255,.05);
  border: 1px solid rgba(255,255,255,.14);
  border-radius: 12px;
  padding: 0.7rem 0.9rem;
}

.amount-input-row:focus-within {
  border-color: color-mix(in oklab, var(--accent-1) 60%, transparent);
}

.amount-input-prefix {
  /* Was var(--heading-font) — same "wrong font" fix as .amount-chip below (ARK, 8 Oct 2026). */
  font-family: var(--label-font);
  text-transform: uppercase;
  font-weight: 800;
  color: color-mix(in oklab, var(--accent-1) 60%, white);
}

.amount-input {
  flex: 1 1 auto;
  min-width: 0;
  width: 100%;
  background: none;
  border: none;
  color: var(--text);
  /* Was var(--body-font) — same "wrong font" fix (ARK, 8 Oct 2026); this is also the font the
     "Custom amount" placeholder renders in, which the brief named directly. */
  font-family: var(--label-font);
  text-transform: uppercase;
  font-size: 1.1rem;
  -webkit-user-select: text;
  user-select: text;
}
.amount-input:focus { outline: none; }
/* Removes the increment/decrement stepper arrows on the custom-amount field (ARK, 8 Oct 2026:
   "user just types") — type=number is kept for the numeric mobile keypad, just without the
   spinner UI, in both the WebKit/Blink and Firefox engines this checkout ships to. */
.amount-input::-webkit-outer-spin-button,
.amount-input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
.amount-input[type="number"] {
  -moz-appearance: textfield;
  appearance: textfield;
}

.amount-input-suffix {
  flex: 0 0 auto;
  font-family: var(--label-font);
  text-transform: uppercase;
  letter-spacing: .08em;
  font-size: 0.7rem;
  color: var(--muted);
}

/* More generous, even padding throughout — same reasoning the old seat-select screen's own
   padding note gave: the price-not-confirmed/limits note and the dev-only test tile both read
   cramped against the panel edge otherwise. Scoped to .amount-panel so no other step's shared
   .notice/.panel spacing moves. */
.amount-panel { padding: 1.4rem 1.1rem 1.25rem; }
.amount-panel .notice { padding: 0 0.1rem; margin-top: 0.9rem; }
.amount-panel .notice.warn {
  margin-top: 1rem;
  padding: 0.8rem 0.9rem;
  border-radius: 14px;
  background: rgba(255,178,90,.08);
  border: 1px dashed rgba(255,178,90,.35);
}
.amount-panel .demo-tile { margin-top: 1.1rem; padding: 1.1rem 1.15rem; }
.amount-panel #amount-next { margin-top: 1.25rem; }

.method-split {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.6rem;
}
@media (max-width: 480px) {
  /* Three tiles side by side get too cramped under ~480px for the sub-line text to stay
     readable without wrapping badly — stack instead. Never sideways scroll (house rule). */
  .method-split { grid-template-columns: 1fr; }
  /* Same reasoning for the QR capture row's email input + Continue button — stack rather than
     squeeze a full-width "CONTINUE" button against an input in the same row. */
  .capture-row { flex-direction: column; }
  .capture-row button { width: 100%; }
}

.method-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.35rem;
  color: var(--text);
  border: 1px solid rgba(255,255,255,.09);
  border-radius: 18px;
  padding: 1.4rem 0.9rem;
  background: linear-gradient(180deg, rgba(255,255,255,.07), rgba(255,255,255,.015));
  cursor: pointer;
  min-width: 0;
  width: 100%;
  margin-top: 0;
  text-transform: none;
  transition: transform .6s var(--float), border-color .5s var(--ease), box-shadow .6s var(--float);
}
.method-tile:hover:not(:disabled) {
  transform: translateY(-3px);
}
/* Crypto is the live, bookable method: lit and glowing even at rest, not just on hover. */
.method-tile:not(:disabled) {
  border-color: color-mix(in oklab, var(--accent-1) 55%, transparent);
  background: linear-gradient(180deg, color-mix(in oklab, var(--accent-1) 16%, rgba(255,255,255,.07)), rgba(255,255,255,.015));
  box-shadow: 0 16px 36px rgba(0,0,0,.4), 0 0 32px color-mix(in oklab, var(--accent-1) 30%, transparent);
}
.method-tile:hover:not(:disabled) {
  border-color: color-mix(in oklab, var(--accent-1) 70%, transparent);
  box-shadow: 0 20px 44px rgba(0,0,0,.45), 0 0 44px color-mix(in oklab, var(--accent-1) 42%, transparent);
}
/* Card ("coming soon") stays greyed but must read clearly, not vanish into the background —
   dim the panel itself, not the text sitting on it. */
.method-tile:disabled {
  cursor: not-allowed;
  background: rgba(255,255,255,.03);
  border-color: rgba(255,255,255,.07);
}
.method-tile:disabled .method-tile-icon,
.method-tile:disabled .method-tile-name { opacity: 0.62; }
.method-tile:disabled .method-tile-note { opacity: 0.8; }
.method-tile-icon { font-size: 1.8rem; line-height: 1; }
.method-tile-icon-svg svg { width: 1.6rem; height: 1.6rem; display: block; }
.method-tile-name { font-family: var(--heading-font); font-weight: 800; font-size: 1rem; text-transform: uppercase; letter-spacing: .02em; }
.method-tile-note { color: var(--muted); font-size: 0.72rem; text-transform: none; letter-spacing: 0; font-family: var(--body-font); line-height: 1.3; }
/* QR is the de-emphasised, always-works fallback (brief item 1) — still fully live and
   clickable, just dimmer at rest than wallet/card so it reads as third choice, not hidden. */
.method-tile-soft:not(:disabled) {
  box-shadow: none;
  background: linear-gradient(180deg, rgba(255,255,255,.05), rgba(255,255,255,.015));
  border-color: rgba(255,255,255,.09);
}
.method-tile-soft:not(:disabled):hover {
  border-color: color-mix(in oklab, var(--accent-1) 45%, transparent);
  box-shadow: 0 12px 28px rgba(0,0,0,.35);
}

.capture-panel-note { margin-bottom: 0.9rem; }
.capture-row {
  display: flex;
  gap: 0.5rem;
  margin-bottom: 0.9rem;
}
/* Every button in this app defaults to width:100% (stacked, full-width buttons everywhere
   else) — inside this one flex row that fights the email input for space, so it's pinned back
   to its own content width here instead. */
.capture-row button {
  width: auto;
  flex: 0 0 auto;
  white-space: nowrap;
}
.capture-email-input {
  flex: 1 1 auto;
  min-width: 0;
  background: rgba(255,255,255,.05);
  border: 1px solid rgba(255,255,255,.14);
  border-radius: 12px;
  padding: 0.7rem 0.9rem;
  color: var(--text);
  font-family: var(--body-font);
  font-size: 0.95rem;
  -webkit-user-select: text;
  user-select: text;
}
.capture-email-input:focus {
  outline: none;
  border-color: color-mix(in oklab, var(--accent-1) 60%, transparent);
}
.capture-divider {
  text-align: center;
  color: var(--muted);
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: .08em;
  margin: 0.7rem 0;
}

/* The handle + avatar + verified tick shown once X/Discord is connected — same markup on the
   pay-method, quote/waiting and confirmed screens (socialIdentityHtml() in app.js). Sits above
   the screen's own heading, never replacing it. */
.social-identity-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  margin-bottom: 0.8rem;
  padding: 0.3rem 0.7rem 0.3rem 0.3rem;
  border-radius: 999px;
  background: rgba(255,255,255,.06);
  border: 1px solid rgba(255,255,255,.12);
  font-family: var(--body-font);
  font-size: 0.85rem;
}
.social-identity-avatar { border-radius: 999px; display: block; }
.social-identity-tick { width: 1rem; height: 1rem; color: #4fd37a; flex-shrink: 0; }

/* The QR capture gate's own two buttons (brief: "above the email field") — full width, stacked,
   same as every other button in this app, never side by side (no sideways scroll, house rule). */
.qr-social-buttons { display: flex; flex-direction: column; gap: 0.5rem; margin-bottom: 0.4rem; }
.social-continue-btn { display: flex; align-items: center; justify-content: center; gap: 0.5rem; }
.social-continue-btn svg { width: 1rem; height: 1rem; flex-shrink: 0; }

.chain-list {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.chain-tile {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  border: 1px solid rgba(255,255,255,.09);
  border-radius: 16px;
  padding: 1rem;
  background: linear-gradient(180deg, rgba(255,255,255,.06), rgba(255,255,255,.015));
  min-width: 0;
  width: 100%;
  margin-top: 0;
  text-transform: none;
  font-family: var(--body-font);
}

.chain-tile-icon { width: 28px; height: 28px; object-fit: contain; flex-shrink: 0; }
/* ApeChain's real mark is a wide wordmark badge, not a square icon like the other chains' —
   give it its own wider slot instead of squashing it into the 28x28 square. */
.chain-tile-icon-wide { width: 68px; height: 28px; object-fit: contain; object-position: left center; flex-shrink: 0; }
/* STONKBROKER's source file is a flat orange square with no circle of its own, unlike every
   other token mark here (ETH/APE/USDG are already circles with transparent padding baked into
   their own source files) — clipped round so it reads as the same orange disc as the rest of
   the row (stonk-icon-padding brief, 12 Oct 2026). The image itself was shrunk ~78% centred on
   its own sampled orange so the clip never shows anything but one continuous disc, same
   technique already proven on the landing page's own pre-pay token row. */
.chain-tile-icon-stonk { border-radius: 50%; }
.chain-tile-name { font-family: var(--heading-font); font-weight: 800; letter-spacing: .01em; flex: 1; text-align: left; }

/* Same rest/hover language as the landing page's Angel Investor seat tile (public/clients/sps/
   site/boredai.css's .seat): grey edge at rest, lift + blue outer glow only on hover — not a
   permanently-lit flat colour box. */
.chain-tile-live {
  cursor: pointer;
  border-color: rgba(255,255,255,.09);
  background: linear-gradient(180deg, rgba(255,255,255,.06), rgba(255,255,255,.015));
  transition: transform .6s var(--float), border-color .5s var(--ease), box-shadow .6s var(--float);
}
.chain-tile-live:hover:not(:disabled), .chain-tile-live:focus-visible {
  transform: translateY(-3px);
  border-color: color-mix(in oklab, var(--accent-1) 45%, transparent);
  box-shadow: 0 16px 36px rgba(0,0,0,.4), 0 0 36px color-mix(in oklab, var(--accent-1) 28%, transparent);
}
.chain-tile-live:disabled { opacity: 0.6; cursor: wait; }
.chain-tile-live .chain-tile-name { color: color-mix(in oklab, var(--accent-1) 50%, white); text-shadow: 0 0 18px color-mix(in oklab, var(--accent-1) 30%, transparent); }

.chain-tile-soon { opacity: 0.4; cursor: not-allowed; }

/* The "$1 test payment" entry point (config.demo.enabled, dev only) — orange/warn, same colour
   language as the existing "test address" pill, so it reads as a test action at a glance and
   never like a sixth seat. A single full-width block below the seat grid, stacked like every
   other card on this page — never a second column, so it can't introduce sideways scroll. */
.demo-tile {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.3rem;
  width: 100%;
  min-width: 0;
  margin-top: 0.9rem;
  padding: 0.9rem 1rem;
  text-align: left;
  font-family: var(--body-font);
  color: var(--text);
  border: 1.5px dashed rgba(255,178,90,.45);
  border-radius: 16px;
  background: rgba(255,178,90,.08);
  cursor: pointer;
  transition: transform .6s var(--float), border-color .5s var(--ease), box-shadow .6s var(--float);
}
.demo-tile:hover { transform: translateY(-3px); border-color: rgba(255,178,90,.7); box-shadow: 0 16px 36px rgba(0,0,0,.35), 0 0 28px rgba(255,178,90,.2); }
.demo-tile-label {
  font-family: var(--heading-font);
  font-weight: 800;
  letter-spacing: .01em;
  text-transform: uppercase;
  color: #ffb25a;
}
.demo-tile-note { color: var(--muted); font-size: 0.75rem; }

.pill {
  display: inline-block;
  font-family: var(--label-font);
  font-weight: 400;
  font-size: 0.65rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  padding: 0.25rem 0.6rem;
  border-radius: 999px;
  background: rgba(255,255,255,.08);
  border: 1px solid rgba(255,255,255,.12);
  color: var(--muted);
  white-space: nowrap;
}

.pill.warn { background: rgba(255,178,90,.12); border-color: rgba(255,178,90,.35); color: #ffb25a; }

/* Amount and dollar value one above the other (ARK, 7 Oct) — a big token amount, a smaller
   muted USD line directly under it, never joined on one line with a middot. */
.quote-amount-main {
  font-family: var(--heading-font);
  font-weight: 800;
  font-size: clamp(1.9rem, 8vw, 2.4rem);
  letter-spacing: .01em;
  color: color-mix(in oklab, var(--accent-1) 60%, white);
  word-break: break-all;
  text-shadow: 0 0 30px color-mix(in oklab, var(--accent-1) 30%, transparent);
}
.quote-amount-usd {
  font-family: var(--heading-font);
  font-weight: 700;
  font-size: 1.1rem;
  color: var(--muted);
  margin: 0.15rem 0 0.25rem;
}

/* Two columns on desktop — text info left, QR right; stacks text-then-QR on phones, source
   order already puts text first so no column juggling is needed at the narrow breakpoint. */
.quote-layout {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.25rem;
  align-items: start;
  margin-top: 0.75rem;
}
.quote-col-qr { display: flex; justify-content: center; }
@media (min-width: 640px) {
  /* Was 200px — too tight for the svg's own 200px max-width plus its 10px white padding on
     every side, which read as "squashed" (ARK, round 2). Widened so the code has real room. */
  .quote-layout { grid-template-columns: 1fr 260px; }
  .quote-col-qr { justify-content: flex-end; }
}

.countdown {
  font-size: 0.95rem;
  color: var(--muted);
}

.countdown.low { color: var(--danger); }

.address-box {
  font-family: ui-monospace, monospace;
  font-size: 0.85rem;
  background: rgba(0,0,0,.3);
  border: 1px solid rgba(255,255,255,.14);
  border-radius: 12px;
  padding: 0.7rem;
  word-break: break-all;
  margin: 0.5rem 0;
}

button {
  font-family: var(--heading-font);
  font-weight: 800;
  letter-spacing: .04em;
  text-transform: uppercase;
  font-size: 0.95rem;
  width: 100%;
  padding: 0.9rem 1rem;
  border-radius: 999px;
  border: none;
  cursor: pointer;
  margin-top: 0.5rem;
  transition: transform .5s var(--float), box-shadow .5s var(--float), border-color .5s var(--ease);
}

button.primary {
  background: var(--accent-1);
  color: #04070d;
  box-shadow: 0 0 26px color-mix(in oklab, var(--accent-1) 40%, transparent);
}
button.primary:hover:not(:disabled) { transform: translateY(-2px); }

button.secondary {
  background: linear-gradient(180deg, rgba(255,255,255,.08), rgba(255,255,255,.02));
  color: var(--text);
  border: 1px solid rgba(255,255,255,.14);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
}
button.secondary:hover:not(:disabled) { border-color: color-mix(in oklab, var(--accent-1) 50%, transparent); }

button:disabled {
  opacity: 0.4;
  cursor: not-allowed;
  transform: none;
  box-shadow: none;
}

.notice {
  font-size: 0.8rem;
  color: var(--muted);
  margin-top: 0.5rem;
}

.notice.warn { color: #ffb25a; }

.wallet-address {
  font-family: ui-monospace, monospace;
  background: rgba(0,0,0,.3);
  border-radius: 8px;
  padding: 0.4rem 0.6rem;
  display: inline-block;
  font-size: 0.85rem;
}

footer.app-footer {
  position: relative;
  z-index: 1;
  text-align: center;
  color: var(--muted);
  font-family: var(--label-font);
  letter-spacing: .08em;
  text-transform: uppercase;
  font-size: 0.68rem;
  padding: 1.5rem 0.5rem;
}

.address-row {
  display: flex;
  align-items: stretch;
  gap: 0.5rem;
  margin: 0.5rem 0;
}
.address-row .address-box { flex: 1; min-width: 0; margin: 0; }

/* Bigger, clearer wallet-address pill (ARK, 7 Oct: "it just doesn't look right"), and the copy
   icon now sits INSIDE that same pill at its right end rather than as its own separate button
   (ARK, round 2, brief item 6) — one outer pill (.hash-chip) carries the background/border, the
   toggle and copy buttons inside it are plain and borderless, divided by a faint hairline. */
.address-row .hash-chip {
  display: flex;
  align-items: stretch;
  width: 100%;
  background: rgba(0,0,0,.3);
  border: 1px solid rgba(255,255,255,.14);
  border-radius: 12px;
  overflow: hidden;
}
.address-row .hash-chip-toggle {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 0.95rem;
  text-align: left;
  padding: 0.85rem 0.5rem 0.85rem 1rem;
  border-radius: 0;
  background: transparent;
  border: none;
}
.address-row .hash-chip-copy {
  flex: 0 0 auto;
  width: auto;
  height: auto;
  min-width: 48px;
  padding: 0.85rem 1.1rem;
  border-radius: 0;
  background: transparent;
  border: none;
  border-left: 1px solid rgba(255,255,255,.14);
}
.address-row .hash-chip-copy svg { width: 18px; height: 18px; }
.address-row .hash-chip-copy:hover { background: rgba(255,255,255,.06); }

.qr-wrap {
  position: relative;
  display: flex;
  justify-content: center;
  /* No top margin — the grid's own gap handles spacing; a top margin here is what pushed the QR
     below the token-amount line's top instead of level with it (ARK, round 2, brief item 8). */
  margin: 0 0 0.75rem;
}
.qr-wrap svg {
  max-width: 240px;
  width: 100%;
  height: auto;
  border-radius: 12px;
  background: #fff;
  padding: 10px;
}
/* The live "Ace is watching / checking" status (brief item 5) — a small banana-peel loop next
   to a one-line status, shown on both the quote screen (before a transfer is seen) and the
   waiting screen (after). Purely cosmetic: it reads whatever state.buyerConfirmedSent/the
   watcher's own poll result already say, never drives payment logic itself. */
.watch-status {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin-top: 1rem;
  padding: 0.7rem 0.9rem;
  border-radius: 14px;
  background: rgba(255,255,255,.04);
  border: 1px solid rgba(255,255,255,.08);
}
.watch-status-banana { flex: none; display: flex; }
.watch-status-banana svg { display: block; }
.watch-status-line { margin: 0; font-size: 0.85rem; color: var(--muted); }
.watch-dots { display: inline-block; min-width: 1.2em; text-align: left; }

.panel.celebration {
  text-align: center;
}
.panel.celebration h2 {
  color: color-mix(in oklab, var(--accent-2) 70%, white);
  text-shadow: 0 0 30px color-mix(in oklab, var(--accent-2) 35%, transparent);
}

.explorer-link {
  display: inline-block;
  margin-top: 0.5rem;
  color: color-mix(in oklab, var(--accent-1) 70%, white);
  font-family: var(--label-font);
  letter-spacing: .06em;
  text-transform: uppercase;
  font-size: 0.75rem;
  text-decoration: none;
  border-bottom: 1px solid color-mix(in oklab, var(--accent-1) 50%, transparent);
  padding-bottom: 2px;
}

.wof-card {
  margin-top: 1.25rem;
  padding: 1rem;
  border-radius: 16px;
  border: 1px solid color-mix(in oklab, var(--accent-3) 45%, transparent);
  background: linear-gradient(180deg, color-mix(in oklab, var(--accent-3) 14%, transparent), rgba(255,255,255,.015));
  box-shadow: 0 0 36px color-mix(in oklab, var(--accent-3) 20%, transparent);
}
.wof-card.loading-wof {
  color: var(--muted);
  font-size: 0.85rem;
  border-style: dashed;
}
.wof-card-label {
  font-family: var(--label-font);
  letter-spacing: .08em;
  text-transform: uppercase;
  font-size: 0.65rem;
  color: color-mix(in oklab, var(--accent-3) 80%, white);
}
.wof-card-handle {
  font-family: var(--heading-font);
  font-weight: 800;
  font-size: 1.4rem;
  margin-top: 0.2rem;
}
.wof-card-tier {
  font-size: 0.75rem;
  color: var(--muted);
  margin-top: 0.15rem;
}

@media (min-width: 480px) {
  .quote-amount-main { font-size: 2.4rem; }
}

/* The instant "fetching price" pop-up (brief item 2) — appears the moment a chain/token tile is
   clicked, not after the quote request resolves. Soft fade + slight lift, ease-out, ~380ms,
   nothing snaps. Mounted once outside #app (see app.js's mountFetchingModal) so it survives
   render() rebuilding the step body underneath it while the request is in flight. */
.fetching-modal {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1.25rem;
  background: rgba(3,6,14,.62);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  opacity: 0;
  pointer-events: none;
  transition: opacity .38s var(--ease);
}
.fetching-modal.visible { opacity: 1; pointer-events: auto; }
.fetching-modal-card {
  width: 100%;
  max-width: 300px;
  text-align: center;
  padding: 2rem 1.5rem 1.75rem;
  border-radius: 22px;
  background: linear-gradient(180deg, rgba(255,255,255,.08), rgba(255,255,255,.02)), var(--panel);
  border: 1px solid rgba(255,255,255,.1);
  box-shadow: 0 30px 70px rgba(0,0,0,.5), 0 0 50px color-mix(in oklab, var(--accent-1) 16%, transparent);
  transform: translateY(16px) scale(.96);
  opacity: 0;
  transition: transform .4s var(--float), opacity .4s var(--ease);
}
.fetching-modal.visible .fetching-modal-card { transform: translateY(0) scale(1); opacity: 1; }
.fetching-banana { display: flex; justify-content: center; margin-bottom: 0.75rem; }
.fetching-banana svg { display: block; }
.fetching-line {
  margin: 0;
  min-height: 2.4em;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--heading-font);
  font-weight: 700;
  font-size: 1.05rem;
  color: var(--text);
  transition: opacity .26s var(--ease);
}
.fetching-line.fading { opacity: 0; }
.fetching-sub {
  margin: 0.4rem 0 0;
  font-size: 0.78rem;
  color: var(--muted);
}

/* The expired-quote "try again" modal (ARK, 7 Oct 2026) — same .fetching-modal fade/lift shell
   above, just a wider card since this one carries a sentence and buttons rather than one loader
   line. */
.retry-modal-card { max-width: 340px; text-align: center; padding: 1.75rem 1.5rem; }
.retry-modal-title {
  margin: 0 0 0.5rem;
  font-family: var(--heading-font);
  font-weight: 700;
  font-size: 1.1rem;
  color: var(--text);
}
.retry-modal-sub { margin: 0 0 1.1rem; font-size: 0.85rem; color: var(--muted); line-height: 1.45; }
.retry-modal-error { margin: 0 0 0.75rem; }
.retry-modal-actions button { margin-top: 0.6rem; }
.retry-modal-actions button:first-child { margin-top: 0; }

/* Calm, consistent styling for the overpayment/underpaid/expired outcome screens (brief item 6)
   — same panel language as the rest of the flow, plain wording, no alarm colours. The overpaid
   case keeps its logic exactly as is (ARK, 7 Oct: "good safety measure... should definitely be
   flagged") — only the look changed. */
.outcome-panel { text-align: left; }
.outcome-panel h2 { color: color-mix(in oklab, var(--accent-1) 55%, white); }
.outcome-panel .notice { font-size: 0.85rem; line-height: 1.5; }
.outcome-panel .wallet-address { margin-top: 0.25rem; }

/* The "confirm you're human" tick (ARK brief, 7 Oct 2026, 16:05) — the very first thing a buyer
   sees, ahead of the seat picker, so it needs to read as considered and on-theme rather than
   bolted on. A plain checkbox-style tick, not a puzzle: the cost that actually stops a script is
   server-side (see src/payments/humanCheck.js), so the visible side of this only ever needs to
   look light. */
.human-check-panel { text-align: left; }
.human-check-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.95rem 1rem;
  border: 1px solid rgba(255,255,255,.12);
  border-radius: 14px;
  background: rgba(255,255,255,.03);
  cursor: pointer;
  transition: border-color .4s var(--ease), background .4s var(--ease);
}
.human-check-row:has(#human-check-tick:checked) {
  border-color: color-mix(in oklab, var(--accent-2) 55%, transparent);
  background: color-mix(in oklab, var(--accent-2) 8%, transparent);
}
.human-check-row[aria-disabled="true"] { opacity: 0.7; cursor: wait; }
.human-check-tickbox {
  position: relative;
  flex: none;
  width: 1.4rem;
  height: 1.4rem;
  border-radius: 7px;
  border: 1.5px solid rgba(255,255,255,.3);
  background: rgba(255,255,255,.04);
  display: grid;
  place-items: center;
  transition: border-color .3s var(--ease), background .3s var(--ease);
}
.human-check-row:has(#human-check-tick:checked) .human-check-tickbox {
  border-color: var(--accent-2);
  background: color-mix(in oklab, var(--accent-2) 70%, transparent);
}
.human-check-tickbox svg {
  width: 0.95rem;
  height: 0.95rem;
  opacity: 0;
  stroke: var(--bg);
  transition: opacity .2s var(--ease);
}
.human-check-row:has(#human-check-tick:checked) .human-check-tickbox svg { opacity: 1; }
#human-check-tick {
  position: absolute;
  inset: 0;
  opacity: 0;
  margin: 0;
  cursor: pointer;
}
.human-check-label { font-family: var(--body-font); font-size: 0.95rem; color: var(--text); }
.human-check-status { margin-top: 0.6rem; font-size: 0.78rem; color: var(--muted); min-height: 1.1em; }

/* Wallet-picker-first screen (ARK-authorised, 8 Oct 2026) — reuses .chain-tile's own card shape
   (icon + name + pill) rather than inventing a second tile language. Detected/installed wallets
   are plain .chain-tile-live tiles; the rest get .wallet-tile-dim stacked on top — dimmed, but
   never cursor:not-allowed like .chain-tile-soon, because tapping one must still route to an
   install page or a deep link, never a dead button. */
.wallet-tile-dim {
  opacity: 0.55;
  cursor: pointer;
}
.wallet-tile-dim:hover, .wallet-tile-dim:focus-visible {
  opacity: 0.8;
}
.wallet-tile-icon-fallback {
  width: 28px;
  height: 28px;
  border-radius: 8px;
  flex-shrink: 0;
  display: grid;
  place-items: center;
  background: rgba(255,255,255,.08);
  color: var(--muted);
  font-family: var(--heading-font);
  font-weight: 800;
  font-size: 0.85rem;
}

/* The EVM coin screen's compact chain selector (wallet-picker-first brief: "chain as a compact
   selector above the coin list") — a row of small pills, wrapping rather than scrolling
   sideways, so a phone with several offered EVM chains never gets horizontal scroll. Shrunk and
   centred (stonk-icon-pills-rowmatch brief, 12 Oct 2026, items 2-3): the pills previously ran
   right to this row's own edge with nothing to spare, visually wider than they needed to be next
   to the token list below — smaller padding/icon/font leaves them clear of their own row edge,
   and the row is centred rather than left-hugging so it reads as one balanced group. */
.chain-pill-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.4rem;
  margin-bottom: 0.8rem;
}
.chain-pill {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  width: auto;
  margin-top: 0;
  padding: 0.32rem 0.6rem;
  border-radius: 999px;
  border: 1px solid rgba(255,255,255,.12);
  background: rgba(255,255,255,.05);
  color: var(--muted);
  font-family: var(--body-font);
  font-size: 0.76rem;
  font-weight: 400;
  text-transform: none;
  letter-spacing: normal;
  cursor: pointer;
  transition: border-color .3s var(--ease), background .3s var(--ease), color .3s var(--ease);
}
.chain-pill img { width: 14px; height: 14px; object-fit: contain; flex-shrink: 0; }
.chain-pill.active {
  border-color: color-mix(in oklab, var(--accent-1) 55%, transparent);
  background: color-mix(in oklab, var(--accent-1) 14%, transparent);
  color: var(--text);
}

/* Same brief, item 3: the token list below the chain-pill row used to run the full panel width
   while the pill row above it only ever reached about 90% of it, so the two rows finished at
   visibly different edges. Narrowed to match the pill row's own typical width and centred —
   scoped to .chain-list-paywith only (the coin-chain screen's token list), not the chain/wallet
   picker screens that reuse plain .chain-list elsewhere and were never part of this complaint. */
.chain-list-paywith { max-width: 90%; margin: 0 auto; }
.human-check-status.warn { color: var(--danger); }
