/* ============================================================
   PLOT TWIST — Written by Luxe
   Single page, four hash-routed views. Vanilla CSS, no build step.
   ------------------------------------------------------------
    1. TOKENS
    2. RESET + BASE
    3. TYPOGRAPHY
    4. LAYOUT + SECTION RHYTHM
    5. BUTTONS + LINKS
    6. HEADER, NAV, DRAWER
    7. VIEW ROUTER
    8. HERO
    9. BLOOD DRIP
   10. PARTY PROOF STRIP
   11. PITCH
   12. WHERE'S YOUR LINE
   13. TICKETS + VALUE STACK + CONFETTI BURST
   14. KIT
   15. TIMELINE
   16. BOOK CARD + BOOK VIEW
   17. LUXE VIEW + SIGNUP
   18. DETAILS + FAQ
   19. FOOTER + MOBILE BAR
   20. WATERMARK
   21. MOTION
   22. REDUCED MOTION
   ============================================================ */

/* ============================================================
   1. TOKENS
   ============================================================ */
:root {
  /* ---- brand ---- */
  --midnight:     #0B1018;
  --crimson:      #8C1A1B;   /* CTA fills only — never small type on dark */
  --brass:        #9B7536;   /* fills, rules, borders */
  --bone:         #E8DFCC;
  --vellum:       #EAE1CE;
  --vellum-lift:  #F2EADA;   /* tier-3 card only */
  --aubergine:    #3D2435;   /* kit showcase ground only */
  --brass-deep:   #7A5B28;   /* brass TEXT on vellum — 4.82:1 */
  --brass-banner: #8C692F;   /* hero banner ground — WHITE text hits 5.0:1 (AA) */
  --ink:          #1A1A1A;

  /* ---- contrast-corrected tints -------------------------------
     #8C1A1B is 2.07:1 on midnight and #9B7536 is 4.53:1, so neither
     survives as small type on the dark ground. These are the same
     hues lifted until they clear AA; the fills above stay untouched. */
  --crimson-bright: #C0272A; /* large display type on midnight — 3.24:1 */
  --crimson-text:   #D9484C; /* small crimson type on midnight — 4.52:1 */
  --brass-on-dark:  #C89F52; /* brass text on midnight — 7.75:1 */
  --crimson-light:  #B83324; /* slider: protection end — lighter red, 4.57:1 on vellum (the spec's #C4392B was 4.07 and failed AA) */
  --crimson-deep:   #5E0F10; /* slider: obsession end (deep red) — 10.4:1 */

  /* ---- per-ground aliases, reassigned by the section classes ---- */
  --fg:        var(--bone);
  --fg-soft:   rgba(232, 223, 204, 0.78);
  --accent:    var(--brass-on-dark);
  --hairline:  rgba(155, 117, 54, 0.4);

  /* ---- type ---- */
  --font-display: "Archivo Black", "Arial Black", Impact, sans-serif;
  --font-head:    "Archivo", system-ui, "Segoe UI", Helvetica, Arial, sans-serif;
  --font-body:    "Inter", system-ui, -apple-system, "Segoe UI", Helvetica, Arial, sans-serif;
  --font-mono:    "Space Mono", ui-monospace, "Courier New", monospace;

  /* ---- metrics ---- */
  --shell-max:   1200px;
  --text-max:    720px;
  --narrow-max:  620px;
  --section-pad: clamp(3rem, 6vw, 4.5rem);
  --gutter:      clamp(1.25rem, 4vw, 2rem);
  --header-h:    68px;

  /* ---- motion ---- */
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --ease:     cubic-bezier(0.4, 0, 0.2, 1);
}

/* Ground swaps. Every component reads the aliases, never the raw
   brand values, so a section only has to declare its ground once. */
.section-midnight {
  background: var(--midnight);
  --fg:       var(--bone);
  --fg-soft:  rgba(232, 223, 204, 0.78);
  --accent:   var(--brass-on-dark);
  --hairline: rgba(155, 117, 54, 0.4);
  color: var(--fg);
}
.section-vellum {
  background: var(--vellum);
  --fg:       var(--ink);
  --fg-soft:  rgba(26, 26, 26, 0.76);
  --accent:   var(--brass-deep);
  --hairline: rgba(122, 91, 40, 0.4);
  color: var(--fg);
}
/* Aubergine ground — Kit showcase only. Small brass accents use the
   lifted on-dark tint (#C89F52, 5.69:1); the raw #9B7536 is only 3.32:1
   here and fails AA at label sizes. */
.section-aubergine {
  background: var(--aubergine);
  --fg:       var(--bone);
  --fg-soft:  rgba(232, 223, 204, 0.85);
  --accent:   var(--brass-on-dark);
  --hairline: rgba(155, 117, 54, 0.35);
  color: var(--fg);
}

/* ============================================================
   2. RESET + BASE
   ============================================================ */
*, *::before, *::after { box-sizing: border-box; }

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

body {
  margin: 0;
  background: var(--midnight);
  color: var(--bone);
  font-family: var(--font-body);
  font-size: 1.0625rem;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img, svg, picture { display: block; max-width: 100%; }
img { height: auto; }

h1, h2, h3 { margin: 0 0 0.5em; line-height: 1.15; }
p { margin: 0 0 1rem; }

/* Orphan control, applied globally. `balance` evens out short display
   lines; `pretty` stops body text stranding a single word on the last
   line. The prominent standalone lines also carry a hard &nbsp; between
   their final two words as a belt-and-suspenders guarantee in browsers
   without text-wrap: pretty. */
h1, h2, h3,
.tier-card__name, .eyebrow, .pull-quote, .book-pull { text-wrap: balance; }
p, li, dd, .hero__tagline, .faq-a-inner p, .line-desc { text-wrap: pretty; }

:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 3px;
  border-radius: 2px;
}

.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip: rect(0 0 0 0); clip-path: inset(50%);
  overflow: hidden; white-space: nowrap;
}

.skip-link {
  position: absolute; left: 0.5rem; top: -3rem; z-index: 200;
  padding: 0.6rem 1rem;
  background: var(--brass); color: var(--midnight);
  font-family: var(--font-mono); font-size: 0.8rem;
  text-decoration: none;
  transition: top 150ms var(--ease);
}
.skip-link:focus { top: 0.5rem; }

/* ============================================================
   3. TYPOGRAPHY
   ============================================================ */
h1 {
  font-family: var(--font-display);
  font-weight: 400;               /* Archivo Black ships one weight */
  letter-spacing: -0.03em;
}
h2 {
  font-family: var(--font-head);
  font-weight: 800;
  font-size: clamp(1.6rem, 1.25rem + 1.7vw, 2.35rem);
  letter-spacing: -0.02em;
  text-wrap: balance;
  color: var(--fg);
}
h3 {
  font-family: var(--font-head);
  font-weight: 800;
  font-size: 1.15rem;
  letter-spacing: -0.01em;
  color: var(--fg);
}

.eyebrow {
  margin: 0 0 0.75rem;
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 0.72rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent);
}

.small-print {
  font-family: var(--font-mono);
  font-weight: 400;
  font-size: 0.8rem; line-height: 1.6;
  color: var(--fg-soft);
}

.section-sub {
  max-width: 56ch; margin-inline: auto;
  font-size: 1.05em;
  color: var(--fg-soft);
}
/* Bridge between the pull quote and the slider heading — matches the
   "Drag it. Be honest." sub line. (Brief said "bone 80%", but this is the
   vellum/light ground where bone is illegible; --fg-soft is the muted
   foreground the reference line uses — ~76%, the ~80% the brief meant.) */
.line-bridge {
  max-width: 56ch; margin: 32px auto 28px;
  font-family: var(--font-body);
  font-size: 1.05em;
  color: var(--fg-soft);
  text-align: center;
}

.rule { border: 0; border-top: 1px solid var(--hairline); margin: 1.75rem 0; }
.rule-short { width: 120px; margin-inline: auto; }

/* ============================================================
   4. LAYOUT + SECTION RHYTHM
   ============================================================ */
.shell {
  width: 100%; max-width: var(--shell-max);
  margin-inline: auto; padding-inline: var(--gutter);
}

.section {
  position: relative;
  padding-block: var(--section-pad);
  scroll-margin-top: var(--header-h);
  overflow: hidden;                /* clips the watermark bleed */
}
.section .shell { position: relative; z-index: 1; }

/* No section starts or ends on a stray margin stacked over its padding. */
.section > .shell > :first-child,
.section > .shell > :first-child > :first-child { margin-top: 0; }
.section > .shell > :last-child,
.section > .shell > :last-child > :last-child { margin-bottom: 0; }

/* --section-pad is per-section breathing room, but where two sections
   abut they stack it and the seam opens to 2 × 4.5rem = 144px — over the
   100px ceiling. Trim both sides at a seam; the ground change (or the
   hairline below) is what marks the boundary, not the empty space. */
.section + .section          { padding-top: clamp(2rem, 3.5vw, 2.75rem); }
.section:has(+ .section)     { padding-bottom: clamp(2rem, 3.5vw, 2.75rem); }

/* A hairline wherever two grounds meet. */
.section-vellum + .section-midnight,
.section-midnight + .section-vellum { border-top: 1px solid rgba(155, 117, 54, 0.4); }

.narrow { max-width: var(--narrow-max); margin-inline: auto; }
.text   { max-width: var(--text-max);   margin-inline: auto; }
.center { text-align: center; }

/* ============================================================
   5. BUTTONS + LINKS
   ============================================================ */
.btn {
  position: relative;
  display: inline-block;
  padding: 0.85rem 1.6rem;
  border: 1px solid transparent; border-radius: 2px;
  font-family: var(--font-mono);
  font-weight: 700; font-size: 0.78rem;
  letter-spacing: 0.14em; text-transform: uppercase;
  text-align: center; text-decoration: none;
  cursor: pointer;
  overflow: hidden;                /* keeps the shimmer inside the button */
  transition: transform 200ms var(--ease), background-color 200ms var(--ease),
              color 200ms var(--ease), border-color 200ms var(--ease),
              box-shadow 200ms var(--ease);
}
.btn:hover { transform: translateY(-2px); }
.btn:active { transform: translateY(0); }

.btn-sm  { padding: 0.55rem 1rem; font-size: 0.7rem; }
.btn-lg  { padding: 1.05rem 2.4rem; font-size: 0.85rem; }
.btn-block { display: block; width: 100%; }

.btn-crimson {
  background: var(--crimson); color: var(--bone);
  border-color: var(--crimson);
  box-shadow: 0 10px 24px rgba(140, 26, 27, 0.28);
}
.btn-crimson:hover { background: #A31E1F; border-color: #A31E1F; }

.btn-brass {
  background: var(--brass); color: var(--midnight);
  border-color: var(--brass);
}
.btn-brass:hover { background: #AD8340; border-color: #AD8340; }

.btn-brass-outline {
  background: transparent; color: var(--accent);
  border-color: var(--accent);
}
.btn-brass-outline:hover { background: var(--brass); color: var(--midnight); border-color: var(--brass); }

/* Placeholder CTAs. Hiding these would remove every buy button, so they
   stay visible, dim, and inert until the real URL lands in CONFIG. */
.btn.is-placeholder,
.text-link.is-placeholder {
  opacity: 0.45;
  cursor: not-allowed;
  box-shadow: none;
}
.btn.is-placeholder:hover { transform: none; }

.text-link {
  color: var(--accent);
  font-family: var(--font-mono);
  font-weight: 700; font-size: 0.8rem;
  letter-spacing: 0.1em; text-transform: uppercase;
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: border-color 200ms var(--ease), color 200ms var(--ease);
}
.text-link:hover { border-bottom-color: currentColor; }
.link-arrow { display: inline-block; transition: transform 200ms var(--ease); }
.text-link:hover .link-arrow { transform: translateX(4px); }

/* In-copy mention of the event that routes to the party tickets. Inline
   with running text, so it inherits size/weight from the sentence rather
   than the uppercase .text-link treatment. */
.party-link {
  color: var(--crimson);
  font-weight: 700;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}
.party-link:hover { text-decoration-thickness: 2px; }
/* --crimson is a CTA-fill red that fails AA as small type on dark grounds
   (its own token says so). The pre-order note and the FAQ answer both sit on
   midnight, so lift to the accessible crimson tint there — same handling as
   every other in-copy crimson. The underline follows via currentColor. */
.section-midnight .party-link,
.section-aubergine .party-link { color: var(--crimson-text); }

/* ============================================================
   6. HEADER, NAV, DRAWER
   ============================================================ */
.site-header {
  position: sticky; top: 0; z-index: 100;
  background: var(--midnight);
  border-bottom: 1px solid rgba(155, 117, 54, 0.25);
}
.header-inner {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem;
  min-height: var(--header-h);
}
.brand { display: block; flex: none; color: inherit; }
.brand img { width: 42px; height: 42px; }

.header-nav { display: flex; align-items: center; gap: 1.75rem; }
.nav-link {
  position: relative;
  padding-block: 0.4rem;
  font-family: var(--font-mono);
  font-weight: 700; font-size: 0.72rem;
  letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--bone);
  text-decoration: none;
  white-space: nowrap;
}
/* Underline sits 4px below the text and grows from the centre. */
.nav-link::after {
  content: ""; position: absolute;
  left: 0; right: 0; bottom: -4px; height: 1px;
  background: var(--brass-on-dark);
  transform: scaleX(0);
  transition: transform 200ms var(--ease);
}
.nav-link:hover::after { transform: scaleX(1); }
.nav-link[aria-current="page"] { color: var(--brass-on-dark); }
.nav-link[aria-current="page"]::after { transform: scaleX(1); }

/* Pre-Order is the only crimson item in the nav — it reads as a purchase
   path. Uses the lifted crimson tint for AA on midnight (4.52:1). Reverts
   to a normal brass nav item automatically when JS relabels it "The Book"
   on/after the on-sale date. */
.nav-link--preorder { color: var(--crimson-text); }
.nav-link--preorder::after { background: var(--crimson-text); }
.drawer-link--preorder { color: var(--crimson-text); }

.header-actions { display: flex; align-items: center; gap: 0.75rem; }

.nav-toggle {
  display: none;
  width: 44px; height: 44px; padding: 0;
  background: none; border: 1px solid rgba(155, 117, 54, 0.4); border-radius: 2px;
  color: var(--bone); cursor: pointer;
}
.nav-toggle__bars, .nav-toggle__bars::before, .nav-toggle__bars::after {
  display: block; width: 18px; height: 1.5px;
  background: currentColor;
  transition: transform 250ms var(--ease), opacity 150ms var(--ease);
}
.nav-toggle__bars { margin-inline: auto; position: relative; }
.nav-toggle__bars::before, .nav-toggle__bars::after { content: ""; position: absolute; left: 0; }
.nav-toggle__bars::before { top: -6px; }
.nav-toggle__bars::after  { top: 6px; }
.nav-toggle[aria-expanded="true"] .nav-toggle__bars { background: transparent; }
.nav-toggle[aria-expanded="true"] .nav-toggle__bars::before { transform: translateY(6px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] .nav-toggle__bars::after  { transform: translateY(-6px) rotate(-45deg); }

.nav-drawer {
  position: fixed; inset: 0; z-index: 99;
  background: var(--midnight);
  display: grid; place-items: center;
}
.nav-drawer[hidden] { display: none; }
.nav-drawer__inner {
  display: flex; flex-direction: column; align-items: center; gap: 1.5rem;
  text-align: center;
}
.drawer-link {
  font-family: var(--font-head);
  font-weight: 800; font-size: 1.6rem;
  letter-spacing: -0.01em;
  color: var(--bone); text-decoration: none;
}
.drawer-link[aria-current="page"] { color: var(--brass-on-dark); }
.drawer-cta { margin-top: 1rem; }

@media (max-width: 899px) {
  .header-nav { display: none; }
  .nav-toggle { display: block; }
  .header-cta { display: none; }
}
@media (min-width: 900px) {
  .nav-drawer { display: none !important; }
}

/* ============================================================
   7. VIEW ROUTER — one document, four views, no reloads
   ============================================================ */
.view { display: none; }
.view.is-active { display: block; }

/* #party is the default route, so it is visible before a single line of
   JS runs. Without this the router is a single point of failure: if
   main.js throws or never loads, nothing carries .is-active and the
   whole site renders blank. The router overrides this the moment it
   activates any other view. */
#view-party { display: block; }
#view-party:not(.is-active):has(~ .view.is-active) { display: none; }

/* ============================================================
   8. HERO
   ============================================================ */
.hero {
  display: grid;
  grid-template-columns: 1fr 1fr;
  min-height: calc(100svh - var(--header-h));
  background: var(--midnight);
}

/* Centred panel — this is an invitation, so it reads on one vertical
   axis rather than ranged left. */
.hero__content {
  position: relative;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  min-height: 0;                      /* grid child must be allowed to shrink */
  overflow: hidden;
  padding-block: clamp(2rem, 5vw, 4rem);
  padding-inline: clamp(2.5rem, 5vw, 5rem);   /* equal both sides — balanced breathing room */
  border-right: none;                 /* no brass seam between columns */
  text-align: center;
}
.hero__media { border-left: none; }
.hero__stack {
  position: relative; z-index: 1;
  width: 100%; max-width: 30rem;
  margin-inline: auto;
}

/* Desktop: keep the invite centred in its column (margin-inline: auto,
   inherited) with symmetric padding, so the space to the left of the text
   and the gap to the image read as a balanced pair rather than the block
   crowding the seam. The image is centred in its own column (below), which
   opens the ~90–130px breathing room the layout wants. */

/* ---- ambient confetti — ON THE LEFT MIDNIGHT TEXT PANEL, BEHIND TEXT ----
   Clean classic confetti (strips, squares, slivers) built by
   initHeroConfetti(). Sits behind the text (z-index 0). Each .confetti-bit
   carries its own lane, sway, rotation, scale, opacity and optional depth
   blur as custom properties; one keyframe drives them all. */
.hero__confetti {
  position: absolute; inset: 0;
  overflow: hidden; z-index: 0;   /* behind the text */
  pointer-events: none;
}
/* Every other child of the content panel sits above the particles. */
.hero__content > *:not(.hero__confetti) { position: relative; z-index: 1; }
.confetti-bit {
  position: absolute;
  top: -12vh; left: var(--lx);
  opacity: var(--op);
  filter: blur(var(--blur, 0));   /* ~1/3 of particles get a soft blur for depth */
  will-change: transform;
  /* NOTE: no var(--delay) in this shorthand. JS sets the animation-delay
     LONGHAND per particle; an empty var(--delay) here would make the whole
     shorthand invalid and compute animation-name to none — which is exactly
     why the confetti was frozen off-screen and never fell. */
  animation: confettiFall var(--dur, 24s) linear infinite;
}
.confetti-bit svg { display: block; }
/* A gentle sine sway (±--sway) around the lane while falling, so nothing
   drops in a straight line. Rotation accumulates to --rot over the fall. */
@keyframes confettiFall {
  0%   { transform: translate3d(0, 0, 0)      rotate(0)                     scale(var(--sc)); }
  25%  { transform: translate3d(var(--sway), 34vh, 0)  rotate(calc(var(--rot) * 0.25)) scale(var(--sc)); }
  50%  { transform: translate3d(calc(var(--sway) * -1), 68vh, 0) rotate(calc(var(--rot) * 0.5)) scale(var(--sc)); }
  75%  { transform: translate3d(var(--sway), 102vh, 0) rotate(calc(var(--rot) * 0.75)) scale(var(--sc)); }
  100% { transform: translate3d(0, 138vh, 0)  rotate(var(--rot))            scale(var(--sc)); }
}

/* The picture is taken out of flow so the photo's intrinsic height can't
   drive the grid row — a 4:5 image half the page wide would otherwise
   make the hero ~950px tall and push the CTA below the fold. */
/* The photo is composed to be shown WHOLE (4:5), so it is contained, not
   cropped. Any letterbox space is midnight and vanishes into the design.
   The image is absolutely positioned so its intrinsic height can never
   drive the grid row past the viewport. */
.hero__media {
  position: relative; overflow: hidden; min-height: 0;
  background: var(--midnight);
  display: flex; align-items: center; justify-content: center;
}
.hero__media img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: contain;
  /* Centre the contained image in its column so the pillarbox splits evenly.
     This reopens balanced breathing room between the text stack and the
     photo (~90–130px at 1440) instead of hugging the seam. */
  object-position: center center;
}

/* ---- vertical rhythm, per spec ---- */
.hero__eyebrow    { margin: 0 0 1.25rem; }
.hero__lockup     { margin: 0; }
.hero__banner     { margin: 0.5rem 0 0; }
.hero__tagline    { margin: 1.25rem auto 0; }
.hero__dress      { margin: 0.75rem 0 0; }
.cta-drip-wrap    { margin: 1.75rem 0 0; }
.hero__chips      { margin: 1.5rem 0 0; }
.countdown-label  { margin: 1rem 0 0.35rem; }
.countdown        { margin: 0; }

/* Dress code (1 of 8) — the loudest small line in the hero stack. The
   lifted crimson tint clears AA on midnight at 11px; #8C1A1B is 2.07:1. */
.hero__dress {
  font-family: var(--font-mono);
  font-weight: 700; font-size: 0.72rem;
  letter-spacing: 0.2em; text-transform: uppercase;
  color: var(--crimson-text);
}

/* ---- eyebrow ---- */
.hero__eyebrow { font-family: var(--font-mono); }
.hero__invite {
  display: block;
  font-weight: 700; font-size: 0.7rem;
  letter-spacing: 0.3em; text-transform: uppercase;
  color: var(--brass-on-dark);
}
/* ---- lockup ---- */
.hero__lockup {
  font-family: var(--font-display);
  font-size: clamp(2.75rem, 5vw, 4.5rem);
  line-height: 0.85;
  letter-spacing: -0.03em;
  text-transform: uppercase;
}
.hero__plot  { display: block; color: var(--bone); }
.hero__twist { display: block; color: var(--crimson-bright); }

/* ---- brass banner — five white lines: name, launch, rule, when, where.
   White on brass (#9B7536) is ~4:1 = AA-Large, so every line is ≥14px at
   weight 700 (or the display face). No white text smaller than that. ---- */
.hero__banner {
  display: block; width: 100%;
  padding: 1rem 1.5rem;
  /* Deeper gold than the brand brass so WHITE text clears 4.5:1 (AA normal),
     not just the 3:1 large-text floor — the small logistics lines are 14px,
     which WCAG treats as normal text (bold-large starts at 18.66px). */
  background: var(--brass-banner);
  color: #FFFFFF;
  border-radius: 0;
  transform-origin: center;
  text-align: center;
}
.hero__banner-loud {
  display: block;
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 2.6vw, 2.1rem);
  line-height: 1;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #FFFFFF;
}
.hero__banner-quiet {
  display: block; margin-top: 0.3rem;
  font-family: var(--font-mono);
  /* 14px @700 is the AA-Large floor for white on brass (~4:1). The spec's
     .68rem (10.9px) would fail AA, so it's lifted to .875rem. */
  font-weight: 700; font-size: 0.875rem;
  letter-spacing: 0.14em; text-transform: uppercase;
  color: #FFFFFF;
}
.hero__banner-rule {
  display: block; width: 40%; height: 3px;   /* deliberate rule, not a full-width border */
  margin: 0.75rem auto;
  background: rgba(255, 255, 255, 0.45);
}
.hero__banner-when {
  display: block;
  font-family: var(--font-mono);
  font-weight: 700; font-size: 0.95rem;   /* the loudest logistics line */
  letter-spacing: 0.1em; text-transform: uppercase;
  color: #FFFFFF;
}
.hero__banner-where {
  display: block; margin-top: 0.35rem;
  font-family: var(--font-mono);
  /* Solid white on --brass-banner = 5.0:1 (AA normal). */
  font-weight: 700; font-size: 0.875rem;
  letter-spacing: 0.1em; text-transform: uppercase;
  color: #FFFFFF;
}

/* Only the venue name is a link; it reads as an external link via a trailing
   ↗ glyph (the external-link convention, distinct from the internal → arrows).
   No persistent underline — the arrow carries the signal, and an underline
   appears only on hover/focus. Kept as one unit so the arrow never splits from
   the text; the line wraps at the interpunct before CHICAGO instead. */
.venue-link {
  color: inherit;
  text-decoration: none;
  white-space: nowrap;
}
.venue-arrow {
  display: inline-block;
  font-size: 0.8em;
  margin-left: 0.25em;
  transition: transform 150ms ease;
}
.venue-link:hover,
.venue-link:focus-visible {
  text-decoration: underline;
  text-decoration-color: #FFFFFF;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}
.venue-link:hover .venue-arrow,
.venue-link:focus-visible .venue-arrow {
  transform: translate(1px, -1px);   /* up and to the right by 1px */
}
.venue-link:focus-visible {
  outline: 2px solid #FFFFFF;
  outline-offset: 3px;
}
/* Guarantee a ≥44px tap target on mobile without enlarging the font or
   disturbing the banner's tight fit: an absolutely-positioned overlay inside
   the anchor expands the hit area but takes up no layout space. */
@media (max-width: 768px) {
  .venue-link { position: relative; display: inline-block; }
  .venue-link::before {
    content: "";
    position: absolute;
    left: 0; right: 0; top: 50%;
    transform: translateY(-50%);
    height: 44px;
  }
}

.hero__tagline {
  max-width: 34ch;
  font-size: 1rem;
  color: rgba(232, 223, 204, 0.75);
}

/* ---- amenity chips ---- */
.hero__chips {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: 0.5rem;
  list-style: none; padding: 0;
}
.hero__chips li {
  padding: 0.4rem 0.75rem;
  border: 1px solid rgba(155, 117, 54, 0.45);
  border-radius: 2px;
  font-family: var(--font-mono);
  font-weight: 400; font-size: 0.65rem;
  letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--bone);
}
/* Scarcity should not read like an amenity. The border keeps the brand
   crimson; the 10px text uses the lifted tint, because #8C1A1B is only
   2.07:1 on midnight. */
.hero__chips .chip-scarce {
  border-color: var(--crimson-bright);
  color: var(--crimson-text);
}
/* Loudest tier — the one thing that runs out. Solid crimson fill defines the
   edge (border kept transparent so box geometry stays identical to the other
   chips); bone on #8C1A1B is ~6.96:1, comfortably AA. */
.hero__chips .chip-critical {
  background: var(--crimson);
  border-color: transparent;
  color: var(--bone);
}

.countdown-label {
  font-family: var(--font-mono);
  font-weight: 400; font-size: 0.6rem;
  letter-spacing: 0.25em; text-transform: uppercase;
  /* 0.7 alpha puts this at 4.31:1; 0.8 clears AA at 5.28:1 */
  color: rgba(200, 159, 82, 0.8);
}

.countdown {
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: center;
  gap: 0.5rem;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--brass-on-dark);
}
.countdown-unit { display: inline-flex; align-items: baseline; gap: 0.3rem; }
.countdown b {
  display: inline-block;
  font-family: var(--font-display);
  font-weight: 400; font-size: 1.35rem;
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
}
.countdown-sep { opacity: 0.5; }
.countdown.is-past { color: var(--bone); }

/* Under 14 days the DAYS figure turns crimson and breathes. The class is
   set by main.js; the threshold logic is live now and trips on its own. */
.countdown b.is-urgent {
  color: var(--crimson-text);
  animation: urgentPulse 3.5s ease-in-out infinite;
}
@keyframes urgentPulse { 0%, 100% { opacity: 0.75; } 50% { opacity: 1; } }

/* Digit change — the changed figure only, nothing else moves. */
@keyframes digitTick {
  from { transform: translateY(-6px); opacity: 0.4; }
  to   { transform: translateY(0);    opacity: 1; }
}
.countdown b.is-ticking { animation: digitTick 300ms var(--ease-out); }

@media (max-width: 900px) {
  .hero { grid-template-columns: 1fr; min-height: auto; }
  /* Image on top, everything tightened so image → eyebrow → lockup → the
     five banner lines → tagline → dress line → GET TICKETS all clear the
     fold at 390×844 and 360×800. Chips + countdown may fall below it. */
  .hero__media { height: 24vh; max-height: 200px; order: -1; }
  .hero__content {
    border-right: none;
    border-top: none;                 /* no horizontal seam on mobile either */
    padding: 1rem 1.25rem 1.5rem;
  }
  .hero__eyebrow { margin: 0 0 0.4rem; }
  .hero__invite  { font-size: 0.62rem; }
  .hero__lockup  { font-size: clamp(1.85rem, 9vw, 2.4rem); line-height: 0.85; margin: 0 0 0.3rem; }
  .hero__banner  { padding: 0.6rem 0.9rem; margin-bottom: 0.6rem; }
  .hero__banner-loud  { font-size: 1.2rem; }
  .hero__banner-quiet { font-size: 0.55rem; }
  .hero__banner-rule  { margin: 0.5rem auto; }
  .hero__banner-when  { font-size: 0.8rem; }
  .hero__banner-where { font-size: 0.55rem; }
  .hero__tagline   { font-size: 0.88rem; line-height: 1.35; margin: 0 auto 0.5rem; }
  .hero__dress     { font-size: 0.65rem; margin: 0 0 0.9rem; }
  .cta-drip-wrap   { margin: 0.9rem 0 0; }         /* tighten CTA up to the dress line */
  .cta-drip-wrap .btn { padding: 0.85rem 2rem; font-size: 0.85rem; }
  /* Chips + countdown intentionally below the fold. */
  .hero__chips     { margin-top: 1rem; }
  .countdown-label { margin-top: 0.75rem; }
}

/* Short desktop windows: shrink the lockup before anything else, so the
   CTA stays above the fold at every height. */
/* Short desktop windows: the split hero is meant to sit in one screen, so
   cap it at the viewport and hide any spill (desktop only — the mobile
   hero stacks and must never be clipped). The compression below tightens
   the rhythm and shrinks the lockup so the countdown still lands inside
   the fold rather than being clipped. */
@media (min-width: 900px) {
  .hero { max-height: calc(100svh - var(--header-h)); overflow: hidden; }
  /* Tighten the text column's right padding so the stack sits closer to
     the image, shrinking the gap between the two panels. */
  .hero__content { padding-inline-end: 1.5rem; }
}
@media (min-width: 900px) and (max-height: 940px) {
  /* The whole stack must fit inside calc(100svh - header); tight padding +
     smaller lockup + reduced gaps keep the content column ≤ the capped
     hero height, so nothing clips top or bottom and the image (contain)
     is never overflowed. Triggers at ≤940px tall so 900-tall laptops are
     covered too. */
  .hero__content   { padding-block: 1rem; }
  .hero__lockup    { font-size: clamp(2.25rem, 4vw, 3rem); }
  .hero__eyebrow   { margin: 0 0 0.75rem; }
  .hero__banner    { margin: 0.4rem 0 0; padding: 0.7rem 1.25rem; }
  .hero__banner-loud { font-size: clamp(1.2rem, 2vw, 1.6rem); }
  .hero__banner-rule { margin: 0.5rem auto; }
  .hero__tagline   { margin: 0.75rem auto 0; }
  .hero__dress     { margin: 0.5rem 0 0; }
  .cta-drip-wrap   { margin: 1rem 0 0; }
  .hero__chips     { margin: 0.8rem 0 0; }
  .countdown-label { margin: 0.7rem 0 0.25rem; }
}

/* ============================================================
   9. BLOOD DRIP — hero CTA only. Never the sticky bar, never a
   tier button, never a brass button.
   ============================================================ */
.cta-drip-wrap {
  position: relative;
  /* inline-block so the box shrink-wraps to the button rather than
     stretching to the column — the drip strip has to be exactly the
     button's width for the clipping to line up. */
  display: inline-block;
}
.cta-drip-wrap .btn { position: relative; z-index: 1; }

.cta-drips {
  position: absolute; left: 0; top: 100%;
  width: 100%; height: 22px;
  overflow: hidden;
  pointer-events: none;
  z-index: 0;
}
.drip { position: absolute; top: 0; margin-left: -6px; line-height: 0; }

/* No static beads. The edge is clean except for the moment a droplet
   forms, slips and vanishes. */
.drip-drop {
  fill: var(--crimson);
  transform-origin: center top;
  opacity: 0;
  transition: filter 400ms var(--ease);
  animation: dripFall var(--dur) cubic-bezier(0.55, 0, 1, 0.45) var(--delay) infinite;
}
@keyframes dripFall {
  0%, 84% { transform: translateY(0)    scaleY(0.3); opacity: 0; }
  87%     { transform: translateY(0)    scaleY(0.9); opacity: 0.95; }
  90%     { transform: translateY(4px)  scaleY(1.4); opacity: 0.95; }
  97%     { transform: translateY(13px) scaleY(1.9); opacity: 0.4; }
  100%    { transform: translateY(17px) scaleY(2.1); opacity: 0; }
}
.cta-drip-wrap:hover .drip-drop,
.cta-drip-wrap:focus-within .drip-drop {
  animation-duration: calc(var(--dur) * 0.55);
  filter: brightness(1.15);
}

/* ---- periodic CTA shimmer — hero + tier 3 only ---- */
.btn-shimmer::after {
  content: "";
  position: absolute; inset: 0 auto 0 0;
  width: 40%;
  background: linear-gradient(100deg, transparent, rgba(255, 255, 255, 0.16), transparent);
  transform: translateX(-160%);
  pointer-events: none;
  animation: ctaShimmer 7s linear infinite;
}
/* The sweep occupies the first 13% of a 7s timeline — ~900ms of travel,
   then six seconds of nothing. */
@keyframes ctaShimmer {
  0%   { transform: translateX(-160%); }
  13%  { transform: translateX(360%); }
  100% { transform: translateX(360%); }
}
.btn-shimmer:hover::after { animation-play-state: paused; }

/* ============================================================
   10. PARTY PROOF STRIP
   ============================================================ */
/* .section.proof-strip, not .proof-strip — it has to out-specify the
   seam trim above and hold its spec'd 3rem on both sides. */
.section.proof-strip { padding-block: 3rem; }
.proof-grid {
  display: grid; grid-template-columns: repeat(2, 1fr);
  gap: 1.75rem;
  list-style: none; margin: 0; padding: 0;
}
@media (min-width: 900px) { .proof-grid { grid-template-columns: repeat(4, 1fr); } }

.proof-item { text-align: left; }
.proof-icon { display: block; margin-bottom: 0.6rem; color: var(--brass-deep); }
.proof-label {
  margin: 0 0 0.3rem;
  font-family: var(--font-head);
  font-weight: 800; font-size: 1.1rem;
  line-height: 1.25; letter-spacing: -0.01em;
  color: var(--ink);
}
.proof-line { margin: 0; font-size: 0.92rem; line-height: 1.5; color: var(--fg-soft); }

/* ============================================================
   11. PITCH
   ============================================================ */
/* Lead — two sentences, contrasting colour; the party half in brass with
   an italic "lit". Both Archivo 800. Stacks on mobile. */
.pitch-lead {
  margin: 1.25rem 0 1.5rem;
  font-family: var(--font-head);
  font-weight: 800;
  font-size: clamp(1.25rem, 2.2vw, 1.6rem);
  line-height: 1.3; letter-spacing: -0.01em;
}
/* Pitch headline — font-relative max-width so it wraps to two lines (not one,
   not three) across breakpoints. */
#pitch-h { max-width: 15em; margin-left: auto; margin-right: auto; }
.lead-a { color: var(--bone); opacity: 0.65; white-space: nowrap; }
/* The crimson beat always takes its own line so "The party is lit." never
   breaks mid-clause after the first two beats. nowrap keeps it intact. */
.lead-b { color: var(--accent); display: block; white-space: nowrap; }  /* brighter brass #C89F52, 7.75:1 on midnight */
.lead-b em { font-style: italic; font-weight: 800; }
/* Recurring gradient shimmer on "The party is lit." — base #C89F52 → a ~35%-
   lighter gold highlight (narrow ~20% band, gradient-stop only, NOT a token) →
   base #C89F52. The solid --accent above is the fallback if background-clip
   is unsupported. The sweep occupies the first 20% of a 7s cycle (~1.4s) then
   holds at the end position for the remaining 80%; because the sweep travels
   exactly one background period, the hold state equals the start state, so the
   loop is seamless (no visible jump). Skipped entirely under reduced motion. */
@media (prefers-reduced-motion: no-preference) {
  .lead-b.is-shimmer {
    background-image: linear-gradient(100deg,
      #C89F52 0%, #C89F52 40%,
      #DBC18F 50%,
      #C89F52 60%, #C89F52 100%);
    background-size: 200% 100%;
    -webkit-background-clip: text; background-clip: text;
    -webkit-text-fill-color: transparent; color: transparent;
    animation: leadShimmer 7s ease-in-out infinite both;
  }
}
@keyframes leadShimmer {
  0%   { background-position: 100% 0; }
  20%  { background-position: -100% 0; }   /* one full period → identical to 0% */
  100% { background-position: -100% 0; }   /* hold for the remaining 80% */
}
@media (max-width: 768px) {
  .lead-a, .lead-b { display: block; }
}

/* First paragraph — body copy. 1.5rem below to group the block. */
.pitch-open { margin: 0 0 1.5rem; color: var(--fg-soft); }

/* Closing line — the section's visual anchor. 2rem above it separates it
   cleanly from the paragraph now that the middle connective line is gone. */
.pitch-close {
  margin-top: 2rem;
  font-family: var(--font-head);
  font-weight: 800;
  font-size: clamp(1.35rem, 2.4vw, 1.75rem);
  line-height: 1.25;
  color: var(--bone);
}

/* Hand-drawn brush underline beneath "Stay for the vibes." — a FILLED,
   variable-width crimson stroke (thick through the middle, tapered to
   points at both ends, a small upward hook overshooting the last word).
   A stroked path can't render that pressure taper, so the mark is a filled
   shape and the draw is a left→right clip-path wipe — the same observable
   left-to-right reveal the brief's stroke-dashoffset would give. */
.vibes-underline { position: relative; display: inline-block; }
.vibes-stroke {
  position: absolute;
  left: -4px; bottom: -10px;
  width: calc(100% + 8px); height: 12px;
  color: var(--crimson-bright);       /* raw --crimson is near-invisible on midnight */
  overflow: visible; pointer-events: none;
}
.js-motion .vibes-underline:not(.is-drawn) .vibes-stroke { clip-path: inset(0 100% 0 0); }
.vibes-underline.is-drawn .vibes-stroke { animation: vibesDraw 700ms cubic-bezier(0.16, 1, 0.3, 1) both; }
@keyframes vibesDraw {
  from { clip-path: inset(0 100% 0 0); }
  to   { clip-path: inset(0 0 0 0); }
}

/* ============================================================
   12. WHERE'S YOUR LINE
   ============================================================ */
.line-aside { opacity: 0.75; font-style: italic; }

/* Lead line above the header — the retired book pull quote, repurposed as
   the thematic opener for the widget. */
.line-lead {
  /* Archivo 600, non-italic — same display register as the rest of the
     site; weight 600 keeps it subordinate to the 800 H2 below it. */
  font-family: var(--font-head);
  font-weight: 600; font-style: normal;
  font-size: clamp(1.15rem, 1.9vw, 1.5rem);
  line-height: 1.35; letter-spacing: -0.01em;
  color: var(--brass-deep);
  max-width: 34ch; margin: 0 auto 2rem;
  text-align: center; text-wrap: balance;
}
/* Three beats, breaking after "shield." and "trigger."; the question is
   heavier. On narrow columns they run together and wrap naturally. */
.line-lead__beat, .line-lead__ask { display: block; }
.line-lead__ask { font-weight: 700; }
@media (max-width: 599px) {
  .line-lead__beat, .line-lead__ask { display: inline; }
  .line-lead__beat::after { content: " "; }
}

.line-widget { max-width: var(--text-max); margin: 2rem auto 0; }

/* ---- drag cues: grab cursor, idle nudge, pulsing ring, chevrons, hint.
   All centred on the thumb's start (50%) and all killed permanently once
   .has-interacted lands on the widget. ---- */
.line-track-wrap { position: relative; }
.line-range { cursor: grab; }
.line-range:active { cursor: grabbing; }

.line-cues {
  position: absolute; left: 50%; top: 12px;   /* track vertical centre */
  pointer-events: none;
}
.line-ring {
  position: absolute; left: 0; top: 0;
  width: 26px; height: 26px; border-radius: 50%;
  border: 1px solid var(--crimson);
  transform: translate(-50%, -50%);
  animation: thumbRing 2.8s ease-out infinite;
}
@keyframes thumbRing {
  0%   { transform: translate(-50%, -50%) scale(0.7); opacity: 0.8; }
  100% { transform: translate(-50%, -50%) scale(1.6); opacity: 0; }
}
/* Chevrons flank the thumb and slide outward on the same 2.8s loop. */
.line-chevron {
  position: absolute; top: 0;
  width: 8px; height: 8px;
  border-top: 1.5px solid var(--crimson);
  border-right: 1.5px solid var(--crimson);
}
.line-chevron--r { left: 10px;  transform: translateY(-50%) rotate(45deg);  animation: chevronOut 2.8s ease-out infinite; }
.line-chevron--l { left: -10px; transform: translateY(-50%) rotate(-135deg); animation: chevronOutL 2.8s ease-out infinite; }
@keyframes chevronOut  { 0% { transform: translateY(-50%) rotate(45deg)  translateX(0); opacity: 0; } 30% { opacity: 0.9; } 100% { transform: translateY(-50%) rotate(45deg)  translateX(10px); opacity: 0; } }
@keyframes chevronOutL { 0% { transform: translateY(-50%) rotate(-135deg) translateX(0); opacity: 0; } 30% { opacity: 0.9; } 100% { transform: translateY(-50%) rotate(-135deg) translateX(10px); opacity: 0; } }

.line-hint {
  margin: 0.6rem 0 0; text-align: center;
  font-family: var(--font-mono);
  font-weight: 400; font-size: 0.68rem;
  letter-spacing: 0.18em; text-transform: uppercase;
  /* Full crimson (7.09:1); the 2.8s pulse animates opacity, and the
     resting/reduced-motion state sits at full strength — a flat 70% alpha
     would have failed AA at 3.93. */
  color: var(--crimson);
  animation: hintPulse 2.8s ease-in-out infinite;
  transition: opacity 400ms var(--ease);
}
@keyframes hintPulse { 0%, 100% { opacity: 0.5; } 50% { opacity: 1; } }

/* First interaction stops every cue for good. */
.line-widget.has-interacted .line-ring,
.line-widget.has-interacted .line-chevron { animation: none; opacity: 0; }
.line-widget.has-interacted .line-hint { opacity: 0; animation: none; }

.line-ends {
  display: flex; justify-content: space-between; gap: 1rem;
  margin-bottom: 0.85rem;
  font-family: var(--font-mono);
  font-weight: 700; font-size: 0.72rem;
  letter-spacing: 0.16em; text-transform: uppercase;
}
/* Protection → obsession as a red that darkens across the axis. */
.line-ends span:first-child { color: var(--crimson-light); }
.line-ends span:last-child  { color: var(--crimson-deep); }

.line-range {
  -webkit-appearance: none; appearance: none;
  display: block; width: 100%; height: 24px; margin: 0;
  background: transparent; cursor: grab;   /* draggable affordance */
}
.line-range:active { cursor: grabbing; }
.line-range:focus-visible { outline: 3px solid var(--accent); outline-offset: 6px; border-radius: 4px; }

/* The track is a fixed red gradient — light (protection) to deep
   (obsession) — so the fill visibly deepens as the thumb travels right. */
.line-range::-webkit-slider-runnable-track { height: 2px; border-radius: 2px; background: linear-gradient(90deg, var(--crimson-light), var(--crimson-deep)); }
.line-range::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 14px; height: 14px; margin-top: -6px;
  background: var(--crimson); border: 1px solid var(--crimson-deep);
  clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
  transition: transform 150ms var(--ease);
}
.line-range::-moz-range-track { height: 2px; border-radius: 2px; background: linear-gradient(90deg, var(--crimson-light), var(--crimson-deep)); }
.line-range::-moz-range-thumb {
  width: 14px; height: 14px; background: var(--crimson); border: 1px solid var(--crimson-deep);
  clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
  transition: transform 150ms var(--ease);
}
.line-range.is-dragging::-webkit-slider-thumb { transform: scale(1.15); }
.line-range.is-dragging::-moz-range-thumb     { transform: scale(1.15); }

/* Idle nudge on the thumb — a small ±7px sway, killed on first interaction.
   It never changes the value; it's a pure transform on the thumb glyph. */
@keyframes thumbNudge {
  0%, 100% { transform: translateX(0); }
  25%      { transform: translateX(-7px); }
  75%      { transform: translateX(7px); }
}
.line-widget:not(.has-interacted) .line-range::-webkit-slider-thumb { animation: thumbNudge 2.8s ease-in-out infinite; }
.line-widget:not(.has-interacted) .line-range::-moz-range-thumb     { animation: thumbNudge 2.8s ease-in-out infinite; }

/* Reserve exactly what the tallest zone label needs, so the layout can't
   jump on a zone change — and not a pixel more. */
.line-readout {
  min-height: 7rem; margin-top: 1.25rem; text-align: center;
  transition: opacity 150ms var(--ease);
}
@media (max-width: 599px) { .line-readout { min-height: 6.5rem; } }
.line-readout.is-swapping { opacity: 0; }

.line-name {
  margin: 0 0 0.3rem;
  font-family: var(--font-head);
  font-weight: 800; font-size: clamp(1.5rem, 1.25rem + 1.2vw, 2rem);
  letter-spacing: -0.01em; text-transform: uppercase;
  color: var(--crimson);                 /* brand crimson — 6.6:1 on vellum */
}
.line-desc { margin: 0 auto; max-width: 46ch; font-style: italic; color: var(--fg-soft); }

.line-close { margin-top: 1.75rem; }
.line-quote {
  margin: 0 0 1rem;
  font-family: var(--font-body);
  font-weight: 500; font-style: italic;
  font-size: clamp(1.15rem, 1rem + 0.9vw, 1.45rem);
  line-height: 1.4;
  color: var(--fg);
}
.line-close .small-print { margin-bottom: 1rem; }
.line-close p:last-child { margin-bottom: 0; }

/* #line and #tickets are both vellum, so they read as one block — the
   doubled section padding at that seam was pure dead space. */
#line    { padding-bottom: 2.25rem; }
#tickets { padding-top: 2.25rem; }

/* The one conversion path out of the widget — give it a pulse so it
   doesn't sit there looking like body copy. */
.link-breathe { animation: linkBreathe 3.2s ease-in-out infinite; }
.link-breathe .link-arrow { animation: linkArrow 3.2s ease-in-out infinite; }
@keyframes linkBreathe {
  0%, 100% { opacity: 0.72; text-shadow: none; }
  50%      { opacity: 1; text-shadow: 0 0 12px rgba(155, 117, 54, 0.45); }
}
@keyframes linkArrow {
  0%, 100% { transform: translateX(0); }
  50%      { transform: translateX(3px); }
}
.link-breathe:hover, .link-breathe:focus-visible {
  animation-play-state: paused;
  opacity: 1;
  /* No text-decoration here — the single hover line is the inherited
     .text-link border-bottom (which changes colour on hover). */
}
.link-breathe:hover .link-arrow { animation-play-state: paused; }

/* ============================================================
   13. TICKETS + VALUE STACK + CONFETTI BURST
   ============================================================ */
.tickets-h2  { margin: 0 0 1rem; }
.tickets-sub { margin: 0; }
/* §5 — pointer to the Briefing beneath the doors line. Brass, mono. On this
   vellum ground bone (the brief's hover target) is illegible, so hover
   strengthens to crimson — the site's interactive-emphasis colour on light. */
.tickets-kbyg { margin: 20px 0 32px; text-align: center; }
.kbyg-link {
  font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.12em;
  color: var(--accent); text-decoration: none;
  transition: color 150ms ease;
}
.kbyg-link:hover, .kbyg-link:focus-visible { color: var(--crimson); }
.kbyg-link .link-arrow { transition: transform 150ms ease; }
.kbyg-link:hover .link-arrow,
.kbyg-link:focus-visible .link-arrow { transform: translateX(2px); }

/* Dress code (5 of 8) — full-width bar between the sub and the cards.
   Crimson top/bottom rules on midnight; nobody buys without passing it. */
.dress-bar {
  margin: 1.5rem 0 0;
  padding: 1rem var(--gutter);
  background: var(--midnight);
  border-top: 1px solid var(--crimson);
  border-bottom: 1px solid var(--crimson);
  text-align: center;
  font-family: var(--font-mono);
  font-weight: 700; font-size: 0.8rem;
  letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--bone);
}

/* Dress code (6 of 8) — reminder beneath the scarcity line. On vellum the
   brand crimson clears AA (6.6:1); the on-dark tint would fail at 3.24:1. */
.dress-reminder {
  margin: 0.6rem 0 0;
  text-align: center;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  color: var(--crimson);
}
/* No-refunds fine print under the ticket cards — muted, not a loud statement. */
.ticket-refunds {
  margin: 0.5rem 0 0;
  text-align: center;
  font-family: var(--font-mono);
  font-size: 0.68rem; letter-spacing: 0.04em;
  color: var(--fg-soft);
}

.tier-grid {
  display: grid; gap: 1.5rem;
  /* Bar → cards is 2.5rem; the bar sits full-bleed above the grid. */
  margin-top: 2.5rem;
  /* MUST NOT be overflow:hidden — it would clip every ribbon. */
  overflow: visible;
}
@media (min-width: 900px) {
  .tier-grid {
    grid-template-columns: repeat(3, 1fr);
    align-items: stretch;          /* all cards equal height, tops aligned */
  }
}

/* Outer card is the positioning shell: it anchors the ribbon and never
   scales, so the ribbon centreline is identical on every card. All visual
   styling lives on .tier-card__inner, which is what scales on card 3. */
.tier-card {
  position: relative;              /* REQUIRED — the ribbon anchors here */
  overflow: visible;               /* REQUIRED — or the ribbon clips */
  display: flex; flex-direction: column;
  height: 100%;
  margin-top: 0;                   /* no card may offset itself */
  transform: none;                 /* no card scales — all render at scale 1 */
}
.tier-card__inner {
  display: flex; flex-direction: column;
  flex: 1;                         /* fill the stretched card height */
  padding: clamp(1.5rem, 3vw, 2rem);
  background: rgba(255, 253, 246, 0.55);
  border: 1px solid var(--hairline); border-radius: 2px;
}

/* The ribbon is a direct child of the card and owns its own centring.
   Nothing else may set right/float/margin-left/align-self/justify-self/
   order/position on it. */
.tier-ribbon {
  position: absolute;
  top: 0; left: 50%;
  transform: translate(-50%, -50%);
  white-space: nowrap;
  z-index: 2;
  padding: 0.35rem 0.9rem;
  border-radius: 2px;
  font-family: var(--font-mono);
  font-weight: 700; font-size: 0.62rem;
  letter-spacing: 0.16em; text-transform: uppercase;
}
.tier-ribbon--brass   { background: var(--brass);   color: var(--midnight); }
.tier-ribbon--crimson { background: var(--crimson); color: var(--bone); }

.tier-card__head { text-align: center; }
.tier-card__name {
  margin: 0 0 0.35rem;
  font-family: var(--font-head);
  font-weight: 800; font-size: 1.35rem;
  letter-spacing: -0.01em;
  color: var(--ink);
}
.tier-card__price {
  margin: 0 0 0.35rem;
  font-family: var(--font-display);
  font-weight: 400; font-size: 2.4rem;
  line-height: 1; letter-spacing: -0.02em;
  color: var(--ink);
}
.tier-card__descriptor {
  margin: 0 0 0.6rem;
  font-family: var(--font-mono);
  font-weight: 700; font-size: 0.66rem;
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--brass-deep);
}
.tier-card__hook { margin: 0 0 1.25rem; font-style: italic; color: var(--fg-soft); }

.tier-card__list {
  text-align: left;                /* bullets stay left for scanning */
  flex: 1;
  margin: 0 0 1.5rem; padding: 0;
  list-style: none;
}
.tier-card__list li {
  position: relative;
  margin-bottom: 0.6rem; padding-left: 1.2rem;
  font-size: 0.95rem; line-height: 1.5;
}
.tier-card__list li::before {
  content: ""; position: absolute;
  left: 0; top: 0.62em;
  width: 5px; height: 5px;
  background: var(--brass-deep);
  transform: rotate(45deg);
}
.tier-card__cta {
  width: 100%; margin-top: auto;   /* CTAs bottom-align */
  min-height: 58px;                /* shared CTA height so ledgers above align (brass btn was 50, shimmer 58) */
  display: inline-flex; align-items: center; justify-content: center;
}

/* ---- tier 3 dominance — border/bg on the inner, scale on the inner ---- */
.tier-card--popular .tier-card__inner { border-color: var(--brass); }
.tier-card--value .tier-card__inner {
  border: 2px solid var(--crimson);
  background: var(--vellum-lift);
  box-shadow: 0 24px 48px rgba(0, 0, 0, 0.28);
  z-index: 1;                      /* its deeper shadow layers over neighbours */
}
@media (min-width: 1024px) {
  .tier-card__inner { transition: transform 250ms var(--ease), box-shadow 250ms var(--ease); transform-origin: center; }
  /* All three cards render at scale 1. Card 3's emphasis is its crimson
     border, --vellum-lift ground and deeper shadow — never size. The hover
     lift is identical on every card (translateY only, no scale). */
  .tier-card:hover .tier-card__inner { transform: translateY(-6px); box-shadow: 0 18px 36px rgba(0, 0, 0, 0.18); }
  .tier-card--value:hover .tier-card__inner { box-shadow: 0 30px 60px rgba(0, 0, 0, 0.34); }
}
/* Below 768px card 3 leads the order. (No card scales at any breakpoint.) */
@media (max-width: 768px) {
  .tier-card--value { order: -1; }
  .tier-grid { display: flex; flex-direction: column; }
}

/* ---- tier-2 upgrade nudge ----
   A single brass line directly above the CTA. Card 2 carries no value
   stack; this is the only cross-sell on it. */
/* Both Kit references — the tier-3 bullet and the tier-2 upgrade nudge —
   read as interactive: crimson, underlined. On vellum the brand crimson
   clears AA. */
.kit-link {
  color: var(--crimson);
  font-weight: 600;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  transition: text-decoration-thickness 150ms var(--ease);
}
.kit-link:hover { text-decoration-thickness: 2px; }

.upgrade-line { margin: 0 0 1rem; text-align: center; }
/* Crimson (via .kit-link) but no underline, and kept on one line so it
   never breaks with a wrapping rule. */
.upgrade-link {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.08em;
  white-space: nowrap;
  text-decoration: none;
}
.upgrade-link:hover { text-decoration: none; opacity: 0.8; }

/* The pulse the Kit links trigger on card 3's inner — border only. The
   border lives on the inner now, so the animation must target it. */
@keyframes cardPulse {
  0%, 100% { border-color: var(--crimson); }
  25%, 75% { border-color: rgba(140, 26, 27, 0.35); }
  50%      { border-color: var(--crimson); }
}
.tier-card--value.is-pulsing .tier-card__inner { animation: cardPulse 900ms var(--ease) 1; }

/* ---- the value stack ---- */
.value-stack {
  /* §9 — identical on both cards: shared width + min-height (set by Fully,
     which carries the extra savings line). The bullet lists (flex:1) absorb
     the difference so both ledgers land on a matched baseline; only the CTA
     sits below each ledger. */
  margin: 0 auto 1.5rem; padding: 1rem 1.1rem;
  max-width: 300px; min-height: 258px;
  border: 1px solid rgba(140, 26, 27, 0.35); border-radius: 2px;
  background: rgba(255, 255, 255, 0.4);
  font-family: var(--font-mono);
  font-size: 0.72rem;
}
/* §5 — Standard Security carries no ledger. This flat variant fills the same
   panel footprint (so all three cards' panels share one size/baseline) with a
   single centred line, keeping the card from reading unfinished.
   NOTE: brief specified "bone at 70%", but --bone is a near-white cream that is
   illegible on this light panel; using the ground's muted foreground keeps the
   intent (secondary, ~70%) while staying legible and on-palette. */
.value-stack--flat {
  display: flex; align-items: center; justify-content: center;
  text-align: center;
}
.vs-flatline {
  margin: 0; color: var(--fg-soft);
  font-family: var(--font-mono); font-size: 0.8rem; line-height: 1.5;
  letter-spacing: 0.01em;
}
.vs-row {
  display: flex; align-items: baseline; gap: 0.5rem;
  margin: 0 0 0.5rem;
  color: var(--ink);
}
.vs-lead {
  flex: 1;
  border-bottom: 1px dotted rgba(26, 26, 26, 0.35);
  transform: translateY(-0.2em);
}
.vs-fig { font-variant-numeric: tabular-nums; white-space: nowrap; }

.vs-rule {
  height: 1px; margin: 0.75rem 0;
  background: rgba(140, 26, 27, 0.45);
  transform-origin: left center;
}

.vs-total {
  display: flex; align-items: baseline; justify-content: space-between;
  flex-wrap: wrap; gap: 0.5rem;
  margin: 0;
}
/* Three beats. The value stack sits on the CREAM card, so §3's dark-ground
   colours are mapped to the light ground (approved): worth = --fg-soft,
   pay = --crimson, save = --brass-deep, biggest = ink-muted. */
.vs-worth {
  position: relative;
  color: var(--fg-soft);           /* dark, ~75% on cream */
  font-size: 1.25rem;
  letter-spacing: 0.02em;
  text-decoration: none;
  font-variant-numeric: tabular-nums;
}
/* Strike: a 1px rule at 52% (just below optical centre, clearing the
   crossbars on 4/7/9), --brass-deep at 55% so the numerals stay legible.
   It's an absolutely-positioned element (§3b's reliable-thickness path),
   drawn on scroll. */
.vs-strike {
  position: absolute; left: -2px; right: -2px; top: 52%;
  height: 1px; background: rgba(122, 91, 40, 0.55);   /* --brass-deep @ 55% */
  transform: scaleX(0); transform-origin: left center;
}
.vs-pay {
  font-family: var(--font-body);
  font-weight: 500; font-size: 1.75rem;
  letter-spacing: -0.01em;
  color: var(--crimson);
  font-variant-numeric: tabular-nums;
}
/* Savings — brass (not crimson: two red figures would compete). Full ledger
   width, centred, 1px brass-40% hairline above. */
.vs-save {
  margin: 12px 0 0; padding-top: 8px;
  border-top: 1px solid rgba(122, 91, 40, 0.4);   /* --brass-deep @ 40% */
  text-align: center;
  font-family: var(--font-mono); text-transform: uppercase;
  font-size: 0.8rem; letter-spacing: 0.14em;
  color: var(--brass-deep);
  font-variant-numeric: tabular-nums;
}
.vs-save:last-child { margin-bottom: 10px; }   /* Insider — savings is the last row */
/* Top-tier advantage — Fully Protected only. ink-muted (§3f "fg-soft at 60%";
   held at the AA-safe --fg-soft rather than a literal 60% on cream). */
.vs-biggest {
  margin: 6px 0 0; text-align: center;
  font-family: var(--font-mono); text-transform: uppercase;
  font-size: 0.62rem; letter-spacing: 0.12em;
  color: var(--fg-soft);
}
/* Fully Protected's differentiator label — brass (--brass-deep passes AA on
   the cream panel; #9B7536 does not). Weight 500 requested; Space Mono only
   ships 400/700, so the brass colour carries the emphasis. */
.vs-item--pop { color: var(--brass-deep); font-weight: 500; }

/* ============================================================
   6.45 THE SIGNATURE POURS  (midnight ground)
   ============================================================ */
.sig__eyebrow { letter-spacing: 0.18em; }
.sig__headline {
  font-family: var(--font-display);
  color: var(--bone);
  font-size: clamp(1.9rem, 4.2vw, 3rem);
  line-height: 1.02; letter-spacing: -0.01em;
  margin: 0.5rem auto 0;
  /* Two lines via an explicit <br>: "Two drinks" / "worth the upgrade."
     The accent is fully nbsp-joined, so line 2 never splits to a third. */
}
.sig__accent { color: var(--crimson-bright); }   /* raw --crimson is too dark on midnight */

/* Tier names in PROSE read as tickets, not categories. Space Mono, title
   case, brass. Colour is ground-adaptive var(--accent) (brass-on-dark on
   midnight/aubergine, brass-deep on vellum) so it clears AA on every ground
   — raw #9B7536 fails on vellum and aubergine at prose sizes. */
.tier-name {
  font-family: var(--font-mono);
  font-size: 1.05em;             /* Space Mono has a smaller x-height — optically matched */
  letter-spacing: 0.04em;
  color: var(--accent);
  white-space: nowrap;
}
.sig__deck {
  font-family: var(--font-body);
  color: rgba(232, 223, 204, 0.8);               /* bone 80% via alpha — reveal fill can't reset it */
  max-width: 580px; margin: 1.1rem auto 0; line-height: 1.6;
}
.sig__deck--short { display: none; }
@media (max-width: 779px) {
  .sig__deck--long  { display: none; }
  .sig__deck--short { display: block; }
}

/* ---- desktop annotated lineup (>=780) ---- */
.sig-lineup { margin: 2.5rem auto 0; max-width: 1100px; }
.sig-lineup__frame { position: relative; border: 1px solid var(--brass); line-height: 0; }
.sig-lineup__frame img { width: 100%; height: auto; display: block; }

/* ---- mobile crop + caption strip (<780) ---- */
.sig-mobile { display: none; }
@media (max-width: 779px) {
  .sig-lineup { display: none; }
  .sig-mobile { display: block; margin: 2rem auto 0; max-width: 520px; }
  .sig-mobile img { width: 100%; height: auto; display: block; border: 1px solid var(--brass); }
  .sig-capstrip {
    display: grid; grid-template-columns: 1fr 1fr; gap: 8px;
    border-top: 1px solid var(--brass); border-bottom: 1px solid var(--brass);
    padding: 0.5rem 0;
  }
  .sig-capstrip__name { font-family: var(--font-mono); text-transform: uppercase; font-size: 0.62rem; letter-spacing: 0.12em; color: var(--bone); }
  .sig-capstrip__desc { font-family: var(--font-body); font-style: italic; font-size: 0.68rem; color: rgba(232, 223, 204, 0.55); }
  .sig-capstrip__name--l { grid-column: 1; grid-row: 1; text-align: left; }
  .sig-capstrip__name--r { grid-column: 2; grid-row: 1; text-align: right; }
  .sig-capstrip__desc--l { grid-column: 1; grid-row: 2; text-align: left; }
  .sig-capstrip__desc--r { grid-column: 2; grid-row: 2; text-align: right; }
  .sig-mobile__unlock { font-family: var(--font-mono); text-transform: uppercase; font-size: 0.6rem; letter-spacing: 0.12em; color: var(--brass-on-dark); text-align: center; margin: 0.6rem 0 0; }
}

/* ---- the two drink cards (aubergine) ---- */
.sig-cards { display: grid; gap: 1.25rem; max-width: 1100px; margin: 2.5rem auto 0; }
@media (min-width: 780px) { .sig-cards { grid-template-columns: 1fr 1fr; } }
.sig-card { display: flex; flex-direction: column; background: var(--aubergine); border-top: 1px solid var(--brass); padding: 24px; }
.sig-card__label { order: 1; margin: 0; font-family: var(--font-mono); text-transform: uppercase; font-weight: 700; font-size: 0.8rem; letter-spacing: 0.12em; color: var(--brass-on-dark); }
.sig-card__desc  { order: 2; margin: 0.3rem 0 0; font-family: var(--font-body); font-style: italic; color: rgba(232, 223, 204, 0.6); }
.sig-card__body  { order: 3; margin: 0.9rem 0 0; font-family: var(--font-body); color: var(--bone); line-height: 1.6; }
.sig-card__ingredients { order: 4; margin: 1rem 0 0; padding-top: 0.75rem; border-top: 1px solid var(--brass); font-family: var(--font-mono); font-size: 0.62rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--brass-on-dark); line-height: 1.7; }
.sig-card__body--short { display: none; }
/* Desktop order: label → desc → body → ingredients (1/2/3/4 above). */
@media (max-width: 779px) {
  /* Mobile order: label → desc → ingredient strip → body. §3: tighten the
     strip so "MELON LIQUEUR" doesn't orphan to its own line. */
  .sig-card__ingredients { order: 3; font-size: 0.57rem; letter-spacing: 0.06em; line-height: 1.5; }
  .sig-card__body { order: 4; }
  .sig-card__body--long  { display: none; }
  .sig-card__body--short { display: block; }
}

/* ---- access block ---- */
.sig-access { margin: 2.5rem auto 0; max-width: 640px; }
.sig-access__h { margin: 0; font-family: var(--font-mono); text-transform: uppercase; font-size: 0.75rem; letter-spacing: 0.14em; color: var(--brass-on-dark); }
.sig-access__prose { margin: 0.75rem auto 0; max-width: 560px; font-family: var(--font-body); color: var(--bone); line-height: 1.6; }

/* ---- CTA row (both scroll forward) ---- */
.sig-ctarow { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; margin: 2rem auto 0; }
.sig-ctarow__btn { min-height: 48px; display: inline-flex; align-items: center; justify-content: center; }
@media (max-width: 779px) {
  .sig-ctarow { flex-direction: column; }
  .sig-ctarow__btn { width: 100%; }
}

/* ---- confetti burst container — same mixed particle set as ambient ---- */
.burst-wrap { position: relative; }
.burst {
  position: absolute; left: 0; right: 0; top: 0; height: 1px;
  pointer-events: none;
  z-index: 0;                      /* below the button label */
}
.burst .confetti-bit {
  position: absolute; top: 0; left: var(--lx, 50%);
  opacity: 0;
  will-change: transform, opacity;
}
/* Launch up and out on an arc (--peak up, then --by down past the edge)
   while spinning to --rot and fading. */
@keyframes burstFly {
  0%   { transform: translate(0, 0) rotate(0) scale(0.4);                       opacity: var(--op); }
  30%  { transform: translate(calc(var(--bx) * 0.5), var(--peak)) rotate(calc(var(--rot) * 0.3)) scale(var(--sc)); opacity: var(--op); }
  100% { transform: translate(var(--bx), var(--by)) rotate(var(--rot)) scale(var(--sc));        opacity: 0; }
}

.capacity-note {
  margin: 2rem 0 0;
  text-align: center;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--fg-soft);
}

/* ============================================================
   14. KIT
   ============================================================ */
.kit-stack { max-width: 900px; margin-inline: auto; }
.kit-image {
  width: 100%; max-width: 560px;
  margin: 1.75rem auto;
  filter: drop-shadow(0 20px 36px rgba(0, 0, 0, 0.28));
}
.kit-image--lg { max-width: 620px; }
.kit-sub {
  font-family: var(--font-body);
  font-weight: 500; font-style: italic;
  font-size: 1.1rem;
  color: var(--fg-soft);
}
.kit-body { max-width: 60ch; margin-inline: auto; }

/* Two-column grid at every breakpoint — six items become three rows of two
   on desktop and mobile alike. Colour comes from the ground tokens (--accent
   / --fg / --hairline) so the SAME markup passes AA on both the aubergine
   (#party) and vellum (#book) grounds. */
.senses-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 1.25rem 1.5rem;
  margin: 1.75rem 0 0; padding: 0;
  text-align: left;
}
/* Collapse to one column only when two would crush the text. */
@media (max-width: 360px) { .senses-grid { grid-template-columns: 1fr; } }

.sense-item {
  display: flex;
  gap: 0.6rem;
  align-items: flex-start;
  border-left: 1px solid var(--hairline);
  padding-left: 0.75rem;
}
.sense-icon {
  flex: 0 0 22px;
  color: var(--accent);
  margin-top: 0.1rem;              /* optical align with the label */
}
.sense-icon svg { width: 22px; height: 22px; display: block; }
.sense-text { min-width: 0; }
.sense-label {
  margin: 0;
  font-family: var(--font-mono);
  font-weight: 700; font-size: 0.7rem;
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--accent);
}
.sense-body {
  margin: 0.25rem 0 0;
  font-family: var(--font-body);
  font-size: 0.85rem; line-height: 1.4;
  color: var(--fg); opacity: 0.82;
}

.kit-price {
  margin-top: 1.5rem;
  font-family: var(--font-mono);
  font-size: 0.9rem;
  color: var(--fg);
}
.kit-cta { margin: 1.5rem 0 0; }

/* ---- Kit SHOWCASE (aubergine) — the most distinctive block on the page.
   Everything below is scoped to .kit-section so the #book-tab Kit detail
   (still on vellum) is untouched. -------------------------------------- */
.kit-section {
  position: relative;
  /* Framed insert: a brass hairline across the top and bottom edges. */
  border-top: 1px solid rgba(155, 117, 54, 0.35);
  border-bottom: 1px solid rgba(155, 117, 54, 0.35);
}
/* A warm brass glow pooled behind the product photo, fading to nothing. */
.kit-section::before {
  content: ""; position: absolute; inset: 0;
  background: radial-gradient(60% 45% at 50% 42%, rgba(155, 117, 54, 0.08), transparent 70%);
  pointer-events: none;
}
.kit-section .shell { position: relative; z-index: 1; }

/* Value flag — rotated brass banner off the top-right corner. */
.kit-flag {
  position: absolute; top: 1.25rem; right: 0; z-index: 2;
  padding: 0.4rem 1rem;
  background: #FFFFFF; color: var(--midnight);   /* pops hard on aubergine */
  font-family: var(--font-mono);
  font-weight: 700; font-size: 0.7rem;
  letter-spacing: 0.12em; text-transform: uppercase;
  transform: rotate(-3deg);
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.35);
}
/* Mobile: the absolute top-right badge would overlap the eyebrow and run
   off-screen, so it becomes a centred in-flow badge ABOVE the eyebrow. */
@media (max-width: 768px) {
  .kit-flag {
    position: static;
    transform: rotate(-2deg);
    display: block;
    width: fit-content;
    margin: 0 auto 1.25rem;
    padding: 0.35rem 0.75rem;
    font-size: 0.58rem;
    letter-spacing: 0.1em;
    white-space: nowrap;
  }
}

/* Framed image: the frame is an inset overlay 12px in from the edges, so
   it reads as a bordered box around the photo, not a border on it. */
.kit-figure {
  position: relative;
  width: 100%; max-width: 560px;
  margin: 1.75rem auto;
}
.kit-figure .kit-image {
  width: 100%; margin: 0;
  filter: drop-shadow(0 28px 56px rgba(0, 0, 0, 0.55));
}
.kit-figure::after {
  content: ""; position: absolute; inset: 12px;
  border: 1px solid rgba(155, 117, 54, 0.55);
  pointer-events: none;
}
@media (min-width: 1024px) {
  .kit-figure .kit-image { transition: transform 400ms var(--ease); }
  .kit-figure:hover .kit-image { transform: scale(1.02); }
}


/* Dollar figures in the closing line pick up brass; the rest stays bone. */

/* The section-border pulse fires on the top/bottom hairlines. */
@keyframes kitPulseBorder {
  0%, 100% { border-color: rgba(155, 117, 54, 0.35); }
  50%      { border-color: var(--brass); }
}
.kit-section.is-pulsing { animation: kitPulseBorder 900ms var(--ease) 1; }

.callout {
  max-width: 46ch; margin: 2.5rem auto 0;
  padding: 1.25rem 1.5rem;
  border: 1px solid var(--hairline); border-radius: 2px;
  text-align: center;
}
.callout p { margin: 0 0 0.75rem; }

/* Section 15 (run-of-show timeline) intentionally does not exist — see
   the note in index.html. Guests find out when they arrive. */

/* ============================================================
   16. BOOK CARD + BOOK VIEW
   ============================================================ */
.book-card {
  display: flex; align-items: center; gap: 2.5rem;
  max-width: 680px; margin-inline: auto;
  padding: 1.5rem 2rem;              /* keeps total height ~256px ≤ ~260 */
  border: 2px solid var(--brass); border-radius: 2px;
  box-sizing: border-box;
}
/* HARD CAP the cover. As a flex item the img defaults to min-width:auto,
   which let its 560px intrinsic width blow past flex-basis and overflow
   the card. width + max-width + min-width:0 pin it to 130px. */
.book-card__cover {
  flex: 0 0 130px;
  width: 130px; max-width: 130px; min-width: 0;
  height: auto; display: block;
  filter: drop-shadow(0 12px 24px rgba(0, 0, 0, 0.5));
}
.book-card__body { flex: 1; min-width: 0; text-align: left; }
.book-card__body h3 {
  margin: 0 0 0.75rem;
  font-size: 1.5rem; line-height: 1.15;
}
/* Mobile (≤768px): stacks — cover centred on top at 110px, copy centred. */
@media (max-width: 768px) {
  .book-card { flex-direction: column; text-align: center; gap: 1.5rem; }
  .book-card__cover { flex: 0 0 auto; width: 110px; max-width: 110px; }
  .book-card__body { flex: 0 0 auto; text-align: center; }
}

.book-hero-cover {
  width: 100%; max-width: 380px; height: auto;
  margin: 1.75rem auto;
  filter: drop-shadow(0 20px 40px rgba(0, 0, 0, 0.5));
}

/* Synopsis hook line — the loud all-caps lead above the body copy. */
.book-tagline {
  margin: 0 auto 1.25rem;
  font-family: var(--font-head);
  font-weight: 800; font-size: clamp(1rem, 1.6vw, 1.25rem);
  letter-spacing: 0.04em; text-transform: uppercase;
  line-height: 1.3; color: var(--bone);
  text-wrap: balance;
}

/* ---- the pull quote, both places it appears ----
   Each beat is its own block so the line breaks after "shield." and
   "trigger." on desktop; below 600px they run inline and wrap naturally.
   The closing question carries slightly more weight than the two beats
   above it — Inter 600 against 500. */
.book-pull {
  margin: 0 0 1rem;
  font-family: var(--font-body);
  font-weight: 500; font-style: italic;
  font-size: 1.15rem;
  line-height: 1.4;
  color: var(--fg);
  text-wrap: balance;
}
.book-pull__beat,
.book-pull__ask { display: block; }
.book-pull__ask { font-weight: 600; }

.book-pull--lg {
  max-width: 24ch; margin: 1.5rem auto;
  font-size: clamp(1.2rem, 1rem + 1vw, 1.6rem);
  line-height: 1.35;
}

@media (max-width: 599px) {
  /* Let the three beats reflow as one paragraph on a narrow column. */
  .book-pull__beat,
  .book-pull__ask { display: inline; }
  .book-pull__beat::after { content: " "; }
  .book-pull--lg { max-width: 30ch; }
}

.book-cta { margin-top: 1.75rem; }

/* ---- #book pre-order cards (on midnight) ---- */
.preorder-grid {
  display: grid; gap: 1.25rem;
  max-width: var(--text-max); margin: 2rem auto 0;
  text-align: left;
}
@media (min-width: 760px) { .preorder-grid { grid-template-columns: 1fr 1fr; align-items: stretch; } }
.preorder-card {
  position: relative;
  display: flex; flex-direction: column;
  padding: 1.5rem;
  border: 1px solid var(--hairline); border-radius: 2px;
  background: rgba(255, 255, 255, 0.03);
}
.preorder-card--value { border: 2px solid var(--crimson); }
.preorder-card__badge {
  position: absolute; top: 0; right: 1rem; transform: translateY(-50%);
  padding: 0.3rem 0.7rem; background: var(--crimson); color: var(--bone);
  font-family: var(--font-mono); font-weight: 700; font-size: 0.6rem;
  letter-spacing: 0.14em; text-transform: uppercase;
}
.preorder-card__eyebrow {
  margin: 0 0 0.5rem;
  font-family: var(--font-mono); font-weight: 700; font-size: 0.62rem;
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--accent);
}
.preorder-card__name {
  margin: 0 0 0.35rem;
  font-family: var(--font-head); font-weight: 800; font-size: 1.2rem;
  letter-spacing: -0.01em; color: var(--fg);
}
.preorder-card__price {
  margin: 0 0 0.9rem;
  font-family: var(--font-display); font-weight: 400; font-size: 1.9rem;
  line-height: 1; letter-spacing: -0.02em; color: var(--fg);
}
.preorder-card__list { flex: 1; margin: 0 0 1.25rem; padding: 0; list-style: none; }
.preorder-card__list li {
  position: relative; margin-bottom: 0.5rem; padding-left: 1.1rem;
  font-size: 0.92rem; line-height: 1.5; color: var(--fg-soft);
}
.preorder-card__list li::before {
  content: ""; position: absolute; left: 0; top: 0.6em;
  width: 5px; height: 5px; background: var(--accent); transform: rotate(45deg);
}
.preorder-card__cta { width: 100%; margin-top: auto; }

/* ============================================================
   17. LUXE VIEW + SIGNUP
   ============================================================ */
.luxe-portrait {
  width: 100%; max-width: 340px;
  margin: 1.75rem auto;
  border: 1px solid var(--hairline);
  filter: drop-shadow(0 18px 32px rgba(0, 0, 0, 0.4));
}

/* Two stacked fields + a full-width button, centred. */
.signup {
  display: flex; flex-direction: column; gap: 0.75rem;
  max-width: 480px; margin: 1.5rem auto 0.75rem;
}
.signup-input {
  width: 100%;
  padding: 0.85rem 1rem;
  background: #FFFDF6;
  border: 1px solid var(--hairline); border-radius: 2px;
  color: var(--ink);
  font-family: var(--font-body); font-size: 1rem;
}
.signup-input::placeholder { color: rgba(26, 26, 26, 0.5); }
.signup-status { min-height: 1.4rem; margin: 0; font-family: var(--font-mono); font-size: 0.8rem; color: var(--accent); }
.signup-note { margin: 0.25rem 0 0; font-style: italic; font-size: 0.85rem; color: var(--fg-soft); }

/* Inline error beneath the fields (crimson), and the in-place success
   state that replaces the form. Both fade rather than snap. */
.signup-wrap, .signup-success { transition: opacity 200ms var(--ease); }
.signup-wrap[hidden], .signup-success[hidden] { display: none; }
.signup-wrap.is-fading, .signup-success.is-fading { opacity: 0; }

.signup-error {
  margin: 0.5rem 0 0;
  font-family: var(--font-mono); font-size: 0.8rem;
  color: var(--crimson-text);      /* AA on vellum? this block is on vellum */
}
/* On the vellum #luxe section the brand crimson clears AA (6.6:1). */
.section-vellum .signup-error { color: var(--crimson); }

.signup-success { text-align: center; margin-top: 1.5rem; }
.signup-success__icon { display: inline-block; color: var(--brass-deep); margin-bottom: 0.5rem; }
.signup-success__head {
  margin: 0 0 0.5rem;
  font-family: var(--font-head); font-weight: 800; font-size: 1.4rem;
  color: var(--fg);
}
.signup-success__body {
  max-width: 420px; margin: 0 auto 1rem;
  font-family: var(--font-body); font-size: 1rem; line-height: 1.55;
  color: var(--fg-soft);
}
.signup-retry { font-size: 0.72rem; }

/* ============================================================
   18. DETAILS + FAQ
   ============================================================ */
/* Dress code (8 of 8) — first element in #details, above the list. */
.dress-callout {
  max-width: var(--text-max); margin: 2rem auto 0;
  padding: 1.5rem;
  border: 2px solid var(--crimson); border-radius: 2px;
  background: var(--midnight);
  text-align: center;
}
.dress-callout__eyebrow {
  margin: 0 0 0.5rem;
  font-family: var(--font-mono);
  font-weight: 700; font-size: 0.7rem;
  letter-spacing: 0.2em; text-transform: uppercase;
  color: var(--crimson-text);
}
.dress-callout__head {
  margin: 0;
  font-family: var(--font-head);
  font-weight: 800; font-size: 1.4rem;
  letter-spacing: -0.01em;
  color: var(--bone);
}
.dress-callout__note {
  margin: 0.5rem 0 0;
  font-size: 0.9rem;
  color: rgba(232, 223, 204, 0.78);
}

.details-list {
  display: grid; grid-template-columns: minmax(90px, 150px) 1fr;
  gap: 0 1.5rem;
  max-width: var(--text-max); margin: 2rem auto 0;
  text-align: left;
}
.details-list dt {
  padding: 0.9rem 0;
  border-top: 1px solid var(--hairline);
  font-family: var(--font-mono);
  font-weight: 700; font-size: 0.7rem;
  letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--accent);
}
.details-list dd {
  margin: 0; padding: 0.9rem 0;
  border-top: 1px solid var(--hairline);
  font-family: var(--font-mono);
  font-size: 0.88rem; line-height: 1.6;
  color: var(--fg);
  text-wrap: pretty;   /* modern orphan-avoidance; degrades silently */
}
.details-list dt:last-of-type, .details-list dd:last-of-type { border-bottom: 1px solid var(--hairline); }
/* Below 780px the value column is only ~215px in the two-column grid, which
   forces single-word final lines. Stack label over value at full width so
   every value gets the whole container. Hairline stays a per-row rule. */
@media (max-width: 779px) {
  .details-list { grid-template-columns: 1fr; gap: 0; }
  .details-list dt { padding: 20px 0 0; }
  .details-list dd { padding: 6px 0 0; border-top: none; }
  .details-list dt:last-of-type { border-bottom: none; }
  .details-list dd:last-of-type { padding-bottom: 20px; }
}

/* Fine print beneath the logistics list. */
.fineprint-block { margin-top: 1.75rem; }

.accordion { margin: 2rem auto 0; text-align: left; }
.faq-item { border-top: 1px solid var(--hairline); }
.faq-item:last-child { border-bottom: 1px solid var(--hairline); }
.faq-item h3 { margin: 0; font-size: inherit; }

.faq-q {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  width: 100%; padding: 1.1rem 0;
  background: none; border: 0;
  color: var(--fg);
  font-family: var(--font-head);
  font-weight: 700; font-size: 1rem;
  text-align: left; cursor: pointer;
}
.faq-q:hover { color: var(--accent); }
.faq-icon {
  flex: none; width: 12px; height: 12px;
  border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform 250ms var(--ease);
}
.faq-q[aria-expanded="true"] .faq-icon { transform: rotate(225deg) translate(-2px, -2px); }

/* grid-template-rows animates cleanly and needs no measured height. */
.faq-a {
  display: grid; grid-template-rows: 0fr;
  transition: grid-template-rows 250ms var(--ease);
}
.faq-a.is-open { grid-template-rows: 1fr; }
.faq-a-inner { overflow: hidden; }
.faq-a-inner p { margin: 0 0 1.1rem; color: var(--fg-soft); }

.faq-cta { margin-top: 2.5rem; }

/* ============================================================
   19. FOOTER + MOBILE BAR
   ============================================================ */
.site-footer {
  padding-block: 4rem;
  background: var(--midnight);
  border-top: 1px solid rgba(155, 117, 54, 0.4);
  --fg: var(--bone);
  --fg-soft: rgba(232, 223, 204, 0.72);
  --accent: var(--brass-on-dark);
  color: var(--fg);
}
.site-footer .shell > * { max-width: 760px; margin-inline: auto; }
.footer-wordmark { width: min(210px, 55vw); height: auto; margin: 0 auto 1.25rem; opacity: 0.95; }

.signoff {
  margin: 0 0 0.5rem;
  /* Brand signature face — the ONLY place Dancing Script is used sitewide.
     Fallbacks are cursive-then-sans, never Comic Sans / a `cursive` generic
     (which is what Windows substitutes with Comic Sans). */
  font-family: "Dancing Script", "Brush Script MT", "Segoe Script", sans-serif;
  font-weight: 600; font-style: normal;    /* the face is already cursive */
  font-size: clamp(1.5rem, 2.4vw, 2rem);
  letter-spacing: 0.01em; line-height: 1.3;
  color: var(--brass);
}

/* Dress code (7 of 8) — beneath the sign-off, above the rule. */
.footer-dress {
  margin: 0 0 1.25rem;
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.14em; text-transform: uppercase;
  /* brass at 0.7 alpha reads ~4.3:1; the on-dark tint holds AA */
  color: var(--brass-on-dark); opacity: 0.85;
}

.socials {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: 0.75rem 1rem;
  list-style: none; padding: 0; margin: 0 0 1.5rem;
}
.socials a {
  display: inline-flex; align-items: center; gap: 0.5rem;
  color: var(--brass-on-dark); text-decoration: none;
  font-family: var(--font-mono); font-size: 0.8rem;
}
.socials a:hover { text-decoration: underline; }
.socials-sep { opacity: 0.5; }

.footer-note, .footer-legal, .footer-credit {
  font-family: var(--font-mono);
  font-size: 0.75rem; line-height: 1.7;
  color: var(--fg-soft);
}
/* Bardroom Brand Lab production credit — its own baseline beneath the legal
   lines. The logo is a white-on-black raster keyed to an alpha mask, painted
   brass via mask (recolorable + hoverable). 20px tall (never near the 210px
   LUXE wordmark). Both -webkit-mask and mask so Safari never blanks it. */
.footer-credit-row { margin: 28px 0 32px; }
/* Text first, wordmark second — reads as one sentence. Wraps (text line 1,
   logo line 2, 10px row gap) only if it can't fit; the logo never shrinks. */
.footer-cred {
  display: inline-flex; flex-wrap: wrap;
  align-items: center; justify-content: center;
  gap: 10px 18px;
  text-decoration: none;
  color: var(--accent);   /* neutralize default link-blue on the wrapper (visible text/logo styled separately) */
}
.footer-cred__logo {
  display: block;
  /* Mask trimmed to the ink (viewBox 186 203 550 98 → aspect 5.61:1), so
     `contain` fills the box with the wordmark, not transparent padding.
     30px tall → ~168px wide, comfortably under the ~50px LUXE mark. */
  height: 30px; width: 168px;
  background-color: var(--brass);
  -webkit-mask: url("assets/img/bardroom-brand-lab-mask.svg") no-repeat center;
  mask: url("assets/img/bardroom-brand-lab-mask.svg") no-repeat center;
  -webkit-mask-size: contain; mask-size: contain;
  transition: background-color 200ms ease;
}
.footer-cred__text {
  font-family: var(--font-mono); text-transform: uppercase;
  font-size: 0.7rem; letter-spacing: 0.16em; color: var(--brass);
  transition: color 200ms ease;
}
.footer-cred:hover .footer-cred__logo,
.footer-cred:focus-visible .footer-cred__logo { background-color: var(--bone); }
.footer-cred:hover .footer-cred__text,
.footer-cred:focus-visible .footer-cred__text { color: var(--bone); }

.mobile-bar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 98;
  padding: 0.75rem var(--gutter) calc(0.75rem + env(safe-area-inset-bottom));
  background: rgba(11, 16, 24, 0.94);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  border-top: 1px solid rgba(155, 117, 54, 0.4);
  transform: translateY(110%);
  visibility: hidden;
  transition: transform 350ms var(--ease), visibility 350ms var(--ease);
}
.mobile-bar.is-visible { transform: translateY(0); visibility: visible; }
@media (min-width: 600px) { .mobile-bar { display: none !important; } }
@media (max-width: 599px) { body.has-mobile-bar .site-footer { padding-bottom: 6rem; } }

/* ============================================================
   20. WATERMARK
   Never the dark asset on a dark ground. The PNG is used as a mask and
   filled with brass at 6%, so it reads as a texture rather than an
   image. One hole, ~180px, bleeding off an edge — never floating in the
   content area, never where text sits within 100px.
   ============================================================ */
.watermark {
  position: absolute; top: 50%; z-index: 0;
  width: 180px; height: 180px;
  background: var(--brass);
  opacity: 0.06;
  filter: blur(1px);
  pointer-events: none;
  -webkit-mask-image: url("assets/watermark-bullets.png");
          mask-image: url("assets/watermark-bullets.png");
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-size: contain;     mask-size: contain;
}
.watermark-left  { left: -70px;  transform: translateY(-50%); }
.watermark-right { right: -70px; transform: translateY(-50%) scaleX(-1); }
/* Below the wide breakpoint the columns close in on the bleed, so the
   mark would land under the text. It comes out entirely. */
@media (max-width: 1100px) { .watermark { display: none; } }

/* ============================================================
   21. MOTION
   Tier 1 loud · Tier 2 ambient · Tier 3 functional · Tier 0 none.
   Transform and opacity only, everywhere.
   ============================================================ */

/* ---- 9.1 hero entrance, once on load ----
   Deliberately pure CSS with no .js-motion gate and no JS trigger. The
   animations start on load and `fill-mode: both` supplies the hidden
   from-state, so there is no flash — and if main.js never runs, or
   requestAnimationFrame is throttled (background tab, prerender, an
   embedded webview), the hero still resolves to fully visible. Nothing
   above the fold may depend on a script to become readable. */
.hero [data-hero] {
  animation: heroRise 600ms var(--ease-out) var(--hd, 0ms) both;
}
[data-hero="1"]  { --hd: 0ms; }
[data-hero="2"]  { --hd: 120ms; }
[data-hero="3"]  { --hd: 240ms; }
[data-hero="4"]  { --hd: 360ms; }
[data-hero="6"]  { --hd: 700ms; }    /* tagline */
[data-hero="10"] { --hd: 800ms; }    /* dress line (1 of 8) */
[data-hero="8"]  { --hd: 1000ms; }   /* venue line */
[data-hero="9"]  { --hd: 1240ms; }   /* countdown */

@keyframes heroRise {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* The banner unfurls rather than sliding. */
.hero [data-hero="5"] {
  animation: bannerUnfurl 700ms var(--ease-out) 520ms both;
}
@keyframes bannerUnfurl {
  from { opacity: 0; transform: scaleX(0.85); }
  to   { opacity: 1; transform: scaleX(1); }
}

/* The CTA settles rather than rising — and stays clickable throughout,
   because nothing here touches pointer-events. */
.hero [data-hero="7"] {
  animation: ctaSettle 500ms var(--ease-out) 920ms both;
}
@keyframes ctaSettle {
  from { opacity: 0; transform: scale(0.94); }
  to   { opacity: 1; transform: scale(1); }
}

/* Chips stagger left to right from 1040ms. */
.hero [data-hero-chip] {
  animation: heroRise 500ms var(--ease-out) var(--hd, 0ms) both;
}
.hero [data-hero-chip]:nth-child(1) { --hd: 1040ms; }
.hero [data-hero-chip]:nth-child(2) { --hd: 1100ms; }
.hero [data-hero-chip]:nth-child(3) { --hd: 1160ms; }
.hero [data-hero-chip]:nth-child(4) { --hd: 1220ms; }

/* Hero photograph, from 0ms. */
.hero__media img {
  animation: heroPhoto 1400ms var(--ease-out) both;
}
/* Ends at scale(1): the image is object-fit:contain, so any residual
   scale > 1 overflows the box and overflow:hidden crops it — which was
   clipping the bottom of the book. A gentle settle to 1 keeps it whole. */
@keyframes heroPhoto {
  from { opacity: 0; transform: scale(1.03); }
  to   { opacity: 1; transform: scale(1); }
}

/* ---- 9.10 scroll reveals ---- */
.js-motion [data-reveal],
.js-motion [data-proof],
.js-motion [data-sense] { opacity: 0; }

[data-reveal].is-in,
[data-proof].is-in,
[data-sense].is-in {
  animation: revealRise 550ms var(--ease-out) both;
  animation-delay: var(--rd, 0ms);
}
@keyframes revealRise {
  from { opacity: 0; transform: translateY(20px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ---- 9.2 ticket cascade ----
   The card animates translateY + opacity only. It must NOT carry the
   scale, or the fill state would leave a residual transform on the card
   and shift the ribbon. Card 3's scale-in lives on the inner. */
.js-motion .tier-card { opacity: 0; }
.tier-card.is-in { animation: cardRise 600ms var(--ease-out) both; animation-delay: var(--cd, 0ms); }
@keyframes cardRise {
  from { opacity: 0; transform: translateY(28px); }
  to   { opacity: 1; transform: translateY(0); }
}
/* Card 3 arrives with the same cardRise as the others — no scale entrance,
   so it lands at scale 1 identical in size to cards 1 and 2. */
.js-motion .tier-ribbon { opacity: 0; }
.tier-card.is-in .tier-ribbon { animation: fadeOnly 300ms var(--ease) both; animation-delay: calc(var(--cd, 0ms) + 800ms); }
@keyframes fadeOnly { from { opacity: 0; } to { opacity: 1; } }

/* ---- 9.3 value stack build ---- */
.js-motion [data-vs] { opacity: 0; }
.value-stack.is-in [data-vs] { animation: vsIn 420ms var(--ease-out) both; animation-delay: var(--vd, 0ms); }
@keyframes vsIn {
  from { opacity: 0; transform: translateX(-8px); }
  /* Lands at --vs-op (1 for every row; the worth figure sets it to .85 so
     its spec'd opacity survives the animation's `both` fill). */
  to   { opacity: var(--vs-op, 1); transform: translateX(0); }
}
/* Three line items now: rows at 0 / 300 / 600, rule 900, worth 1150,
   strike 1550, pay 1750. */
.value-stack.is-in [data-vs="1"] { --vd: 0ms; }
.value-stack.is-in [data-vs="2"] { --vd: 300ms; }
.value-stack.is-in [data-vs="3"] { --vd: 600ms; }
.value-stack.is-in [data-vs="5"] { --vd: 1150ms; }
/* Savings + biggest-savings land after the pay figure. */
.value-stack.is-in [data-vs="7"] { --vd: 1900ms; }
.value-stack.is-in [data-vs="8"] { --vd: 2050ms; }

.value-stack.is-in [data-vs="4"] { animation: vsRule 400ms var(--ease-out) 900ms both; }
@keyframes vsRule { from { opacity: 1; transform: scaleX(0); } to { opacity: 1; transform: scaleX(1); } }

/* The strikethrough draws at 1550ms. This is the payoff. */
.value-stack.is-in .vs-strike { animation: vsStrike 350ms var(--ease-out) 1550ms both; }
@keyframes vsStrike { from { transform: scaleX(0); } to { transform: scaleX(1); } }

.value-stack.is-in [data-vs="6"] { animation: vsPay 500ms var(--ease-out) 1750ms both; }
@keyframes vsPay {
  from { opacity: 0; transform: scale(0.9); }
  to   { opacity: 1; transform: scale(1); }
}


/* ---- 9.11 tab switch ---- */
.view.is-entering { animation: viewIn 220ms var(--ease-out) both; }
@keyframes viewIn {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ============================================================
   22. REDUCED MOTION
   Every animation above resolves instantly to its final state.
   Elements appear; they do not move. No exceptions.
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-delay: 0ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }

  /* Anything the motion layer pre-hides is simply visible. The hero and
     the photograph aren't listed: they animate with fill-mode: both and
     no .js-motion gate, so the instant duration above already lands them
     on their end state. */
  .js-motion [data-reveal],
  .js-motion [data-proof],
  .js-motion [data-sense],
  .js-motion [data-vs],
  .js-motion .tier-card,
  .js-motion .tier-ribbon { opacity: 1 !important; transform: none !important; }

  /* Final states that are a transform, not a fade. */
  .vs-rule        { transform: scaleX(1) !important; }
  .vs-strike      { transform: scaleX(1) !important; }   /* the strike still reads */
  /* Card 3 renders at scale 1 like the others; its dominance is border,
     background and shadow — no transform to preserve here. */

  .btn:hover { transform: none !important; }

  /* Drips render nothing at all — no beads, no droplets. */
  .cta-drips { display: none; }
  /* No sweep. */
  .btn-shimmer::after { display: none; }
  /* Confetti sits static and scattered at .5 opacity — no movement. Each
     bit holds its --sy scatter position, rotation and scale. */
  .confetti-bit { animation: none !important; top: auto; opacity: 0.5 !important; transform: translate3d(0, var(--sy, 40vh), 0) rotate(var(--rot)) scale(var(--sc)); }
  .burst .confetti-bit { display: none; }   /* the burst simply never fires */
  /* The link stops breathing and sits at full strength. */
  .link-breathe, .link-breathe .link-arrow { opacity: 1; transform: none; }

  /* Slider cues: no nudge, no ring/chevron motion. Keep a static brass
     ring, static chevrons at .6, and the hint at full opacity. */
  .line-range::-webkit-slider-thumb { animation: none !important; }
  .line-range::-moz-range-thumb { animation: none !important; }
  .line-ring { animation: none; transform: translate(-50%, -50%) scale(1); opacity: 0.6; }
  .line-chevron { animation: none !important; opacity: 0.6; }
  .line-hint { animation: none; opacity: 1; }

  .mobile-bar { transition: none; }
}
