/* ==========================================================================
   ROOK - STAGE

   The layer that makes the site feel like the brand rather than like a shop
   template. Loaded last, so it overrides rook.css without editing it and can
   be pulled out in one line if any of it ever gets in the way.

   Three things live here:

     1. The opener. A full-viewport black hero carrying the WebGL smoke, with
        the headline pushing up out of a mask.
     2. The turn into light. The page does not cut from black to paper, it
        dissolves, which is the whole gesture of the brand in one scroll.
     3. Motion. Scroll-driven where the browser supports it, class-driven
        where it does not, and absent entirely for anyone who asked for less.

   The rule inherited from rook.css still holds and is worth repeating:
   NOTHING IS HIDDEN BY DEFAULT. Every entrance sits inside a
   prefers-reduced-motion: no-preference block, so a browser that cannot run
   it shows finished content instead of an empty page.
   ========================================================================== */

:root {
  --ease-out:  cubic-bezier(.16, .84, .44, 1);
  --ease-mask: cubic-bezier(.22, 1, .36, 1);
}

/* --------------------------------------------------------------------------
   THE OPENER
   -------------------------------------------------------------------------- */

.hero--smoke {
  position: relative;
  min-height: 100svh;              /* svh, so the mobile URL bar cannot cause a jump */
  display: flex;
  align-items: flex-end;
  /* Pulled up under the sticky header so the smoke runs behind it and the top
     of the page reads as one unbroken black field. The matching padding keeps
     the content clear of the bar. */
  margin-top: calc(var(--header-h) * -1);
  padding-block: var(--header-h) clamp(3.5rem, 9vh, 7rem);
  background: #08080A;
  color: var(--paper);
  overflow: hidden;
  isolation: isolate;
}

/* Painted underneath the canvas and never removed. If WebGL is missing, the
   context is lost, or the shader fails to compile, this is what the visitor
   sees, and it is a finished picture on its own rather than a black hole. */
.hero--smoke::before {
  content: "";
  position: absolute; inset: 0; z-index: 0;
  background:
    radial-gradient(120% 80% at 50% 108%, rgba(184,152,90,.50) 0%, rgba(184,152,90,.16) 34%, rgba(184,152,90,0) 66%),
    radial-gradient(80% 55% at 78% 88%, rgba(228,201,138,.16) 0%, rgba(228,201,138,0) 60%),
    #08080A;
}

.hero__smoke {
  position: absolute; inset: 0; z-index: 1;
  width: 100%; height: 100%;
  display: block;
  opacity: 0;
  transition: opacity 1.2s var(--ease-out);
}
/* The script adds this once the first frame is on screen, so the canvas
   cross-fades over the gradient instead of popping in. */
.hero__smoke.is-live { opacity: 1; }

/* Floor. Grounds the smoke into the page and guarantees the headline a dark
   base whatever the shader happens to be doing that second.

   It has to stay light. An earlier version ran to 94% black over the bottom
   62% and quietly erased the smoke exactly where the shader makes it densest,
   which read as "the canvas is broken" rather than "the gradient is too
   strong". The shader now holds its density in the middle of the frame
   instead, so this only has to cover the last stretch behind the type. */
.hero--smoke::after {
  content: "";
  position: absolute; left: 0; right: 0; bottom: 0; height: 52%;
  z-index: 2; pointer-events: none;
  background: linear-gradient(180deg, rgba(8,8,10,0) 0%, rgba(8,8,10,.42) 58%, rgba(8,8,10,.82) 100%);
}

.hero--smoke .hero__in {
  position: relative; z-index: 3;
  width: 100%;
  padding-block: 0;
}

.hero__eyebrow {
  display: inline-flex; align-items: center; gap: var(--s3);
  font-family: var(--display); font-size: var(--t-micro); font-weight: 600;
  text-transform: uppercase; letter-spacing: var(--track);
  color: var(--copper-2);
}
.hero__eyebrow::before {
  content: ""; width: 28px; height: 1px; background: var(--copper);
  transform-origin: 0 50%;
}

.hero--smoke .hero__title {
  margin-block: var(--s5) var(--s5);
  max-width: 14ch;
  font-size: clamp(3rem, 10.5vw, 8.5rem);
  font-weight: 700;
  line-height: .92;
  letter-spacing: -.005em;
  text-transform: uppercase;
  text-shadow: none;
}

/* Each word gets a mask so it can be pushed up from underneath. Inline-block
   on the outer span keeps the words wrapping like normal text. */
.kw { display: inline-block; overflow: hidden; vertical-align: bottom; padding-bottom: .06em; }
.kw > span { display: inline-block; will-change: transform; }

.hero--smoke .hero__lede {
  max-width: 46ch;
  font-size: clamp(.9375rem, 1.4vw, 1.0625rem);
  line-height: 1.7;
  color: #CFC6BC;
  text-shadow: none;
}

.hero--smoke .hero__cta { margin-top: var(--s7); }

/* Scroll cue. A hairline that fills top to bottom on a loop. */
.hero__cue {
  position: absolute; left: 50%; bottom: var(--s5); z-index: 4;
  transform: translateX(-50%);
  display: none; flex-direction: column; align-items: center; gap: var(--s3);
  font-family: var(--display); font-size: .5625rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: var(--track);
  color: #8A8078;
}
.hero__cue span { display: block; width: 1px; height: 46px; background: rgba(255,255,255,.16); overflow: hidden; }
.hero__cue span::after { content: ""; display: block; width: 100%; height: 100%; background: var(--copper); transform: translateY(-100%); }
@media (min-width: 860px) { .hero__cue { display: flex; } }

/* The turn into light. A tall band that carries black up into the paper
   ground so the two never meet at a hard edge. */
.dawn {
  height: clamp(90px, 14vh, 190px);
  margin-top: -1px;
  background: linear-gradient(180deg, #08080A 0%, #171613 38%, #6E6960 72%, var(--paper) 100%);
}

/* --------------------------------------------------------------------------
   HEADER OVER THE OPENER

   Transparent while the smoke is behind it, then a blurred dark bar once it
   is over content. rook.css paints it solid; this is the override.
   -------------------------------------------------------------------------- */

.hdr--over {
  background: transparent;
  border-bottom-color: transparent;
  transition: background-color .4s var(--ease-out), border-color .4s var(--ease-out), backdrop-filter .4s var(--ease-out);
}
.hdr--over.is-stuck {
  background: rgba(10,10,10,.86);
  border-bottom-color: var(--line-dark);
  box-shadow: none;
}
@supports (backdrop-filter: blur(1px)) {
  .hdr--over.is-stuck { background: rgba(10,10,10,.74); backdrop-filter: blur(16px) saturate(1.2); }
}

/* --------------------------------------------------------------------------
   PAGE TRANSITIONS

   Cross-document view transitions. The site is multi-page and server
   rendered, and this is what buys it the feel of an app without shipping a
   router, a bundler, or a kilobyte of JavaScript. Browsers without it simply
   navigate, which is the behaviour we already had.
   -------------------------------------------------------------------------- */

@view-transition { navigation: auto; }

@media (prefers-reduced-motion: no-preference) {
  ::view-transition-old(root) { animation: vt-out .22s cubic-bezier(.4,0,1,1) both; }
  ::view-transition-new(root) { animation: vt-in  .34s var(--ease-out) both; }

  @keyframes vt-out { to { opacity: 0; transform: translateY(-6px); } }
  @keyframes vt-in  { from { opacity: 0; transform: translateY(10px); } }

  /* The mark is the one thing on every page, so it stays put while the rest
     of the page changes around it.

     Scoped to the header on purpose. A view-transition-name has to be unique
     in the document, and .logo__mark also appears in the footer and in the
     age gate. Naming all three aborts the transition and can leave a stale
     snapshot layer painted over the live page - which looks, very
     confusingly, like the content below the fold has vanished. */
  .hdr .logo__mark { view-transition-name: rook-mark; }
  ::view-transition-group(rook-mark) { animation-duration: .4s; }
}

/* --------------------------------------------------------------------------
   MOTION
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: no-preference) {

  /* --- Opener entrance ------------------------------------------------- */

  @keyframes rk-mask-up { from { transform: translateY(108%); } to { transform: none; } }
  @keyframes rk-fade-up { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
  @keyframes rk-line     { from { transform: scaleX(0); } to { transform: scaleX(1); } }

  .hero--smoke .kw > span {
    animation: rk-mask-up 1.05s var(--ease-mask) both;
    animation-delay: calc(var(--i, 0) * 70ms + 120ms);
  }

  .hero__eyebrow { animation: rk-fade-up .8s var(--ease-out) both; }
  .hero__eyebrow::before { animation: rk-line .9s var(--ease-out) .25s both; }

  .hero--smoke .hero__lede { animation: rk-fade-up .9s var(--ease-out) .52s both; }
  .hero--smoke .hero__cta  { animation: rk-fade-up .9s var(--ease-out) .64s both; }

  .hero__cue { animation: rk-fade-up .9s var(--ease-out) 1s both; }
  .hero__cue span::after { animation: rk-cue 2.6s cubic-bezier(.7,0,.3,1) 1.4s infinite; }
  @keyframes rk-cue {
    0%   { transform: translateY(-100%); }
    45%  { transform: translateY(0); }
    55%  { transform: translateY(0); }
    100% { transform: translateY(100%); }
  }

  /* --- Scroll-driven reveals -------------------------------------------

     Where the browser can drive an animation from scroll position, it does,
     and the JavaScript observer becomes dead weight that simply never
     matters. Where it cannot, the existing .is-in path in rook.css still
     runs. Both end in the same place. */

  @supports (animation-timeline: view()) {
    .rk-js .rv { opacity: 1; transform: none; }
    .rv {
      animation: rk-fade-up linear both;
      animation-timeline: view();
      animation-range: entry 4% entry 46%;
    }
  }

  /* --- Section rules draw themselves ------------------------------------ */

  .shead { position: relative; }
  .shead::after {
    content: ""; position: absolute; left: 0; right: 0; bottom: calc(var(--s4) * -1);
    height: 1px; background: var(--line);
    transform: scaleX(0); transform-origin: 0 50%;
  }
  @supports (animation-timeline: view()) {
    .shead::after { animation: rk-line linear both; animation-timeline: view(); animation-range: entry 10% entry 60%; }
  }

  /* --- Cards ------------------------------------------------------------

     A small lift and a gold edge. The tilt is deliberately tiny: at this
     size anything more reads as a gimmick rather than as depth. */

  .card {
    transition: transform .45s var(--ease-out), border-color .35s var(--ease-out), box-shadow .45s var(--ease-out);
  }
  .card:hover {
    transform: translateY(-6px);
    border-color: var(--copper);
    box-shadow: 0 18px 44px -18px rgba(8,8,8,.28), 0 2px 10px -4px rgba(184,152,90,.3);
  }

  /* --- Buttons ----------------------------------------------------------

     Gold wipes in from the left rather than the whole surface switching
     colour on a timer. */

  .btn { position: relative; overflow: hidden; isolation: isolate; }
  .btn::before {
    content: ""; position: absolute; inset: 0; z-index: -1;
    background: var(--copper-btn);
    transform: scaleX(0); transform-origin: 0 50%;
    transition: transform .42s var(--ease-out);
  }
  .btn:hover::before, .btn:focus-visible::before { transform: scaleX(1); }
  .btn:hover, .btn:focus-visible { color: var(--ink); border-color: var(--copper-btn); }
  .btn--copper:hover, .btn--copper:focus-visible { color: var(--ink); }
  .btn--copper::before { background: var(--copper-btn-h); }
}

/* --------------------------------------------------------------------------
   ENTRANCE SAFETY NET

   Set by script three seconds after load. The opener's type starts at opacity
   0 so it can rise out of its mask, and an animation that never starts would
   otherwise leave the headline invisible for good. A backgrounded tab or a
   window that is not painting will do exactly that.

   The scroll cue's own loop is deliberately left alone: it is ambient, not an
   entrance, and pinning it would freeze it mid-travel.
   -------------------------------------------------------------------------- */

.hero--smoke.is-settled .kw > span,
.hero--smoke.is-settled .hero__eyebrow,
.hero--smoke.is-settled .hero__eyebrow::before,
.hero--smoke.is-settled .hero__lede,
.hero--smoke.is-settled .hero__cta,
.hero--smoke.is-settled .hero__cue {
  animation: none !important;
  opacity: 1 !important;
  transform: none !important;
}

/* --------------------------------------------------------------------------
   REDUCED MOTION

   Belt and braces, matching rook.css. Everything is pinned to its finished
   state explicitly, because a stylesheet can be overridden and a class can be
   set by script.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .kw > span,
  .hero__eyebrow,
  .hero__eyebrow::before,
  .hero--smoke .hero__lede,
  .hero--smoke .hero__cta,
  .hero__cue,
  .hero__cue span::after,
  .shead::after,
  .rv {
    animation: none !important;
    opacity: 1 !important;
    transform: none !important;
  }
  .hero__smoke { transition: none; }
  .shead::after { transform: scaleX(1) !important; }
  @view-transition { navigation: none; }
}

@media print {
  .hero__smoke, .hero__cue, .dawn { display: none !important; }
  .hero--smoke { min-height: 0; color: #000; background: #fff; }
}

/* --------------------------------------------------------------------------
   AGE GATE

   Every visitor sees this before they see anything else, so it is the real
   first impression - not the hero. It used to be a cream box, which meant the
   site opened light, cut to black, then went light again. Now it is the same
   black-and-smoke language as the opener, so the gate reads as the front door
   to the hero rather than a different site bolted in front of it.

   rook.css paints .gate light. Everything below overrides that for .gate--dark
   only, so the light version still exists if we ever want it back.
   -------------------------------------------------------------------------- */

.gate--dark {
  background: #08080A;
  color: var(--paper);
  isolation: isolate;
}

/* Same gradient fallback as the hero: a finished picture on its own if WebGL
   is unavailable or the shader fails. */
.gate--dark::before {
  content: "";
  position: absolute; inset: 0; z-index: 0;
  background:
    radial-gradient(120% 78% at 50% 112%, rgba(184,152,90,.44) 0%, rgba(184,152,90,.13) 36%, rgba(184,152,90,0) 68%),
    #08080A;
}

.gate__smoke {
  position: absolute; inset: 0; z-index: 1;
  width: 100%; height: 100%;
  display: block;
  opacity: 0;
  transition: opacity 1.2s var(--ease-out);
}
.gate__smoke.is-live { opacity: 1; }

/* Keeps the form legible over whatever the shader is doing. */
.gate--dark::after {
  content: "";
  position: absolute; inset: 0; z-index: 2; pointer-events: none;
  background: radial-gradient(68% 52% at 50% 46%, rgba(8,8,10,.86) 0%, rgba(8,8,10,.62) 55%, rgba(8,8,10,.34) 100%);
}

.gate--dark .gate__in { position: relative; z-index: 3; max-width: 440px; }

.gate--dark .gate__logo { color: var(--paper); }
.gate--dark .gate__logo .logo__mark { color: var(--copper); }
.gate--dark .logo__tag { color: var(--copper-2); }

.gate--dark .gate__title {
  font-family: var(--display);
  font-size: clamp(1.75rem, 4.6vw, 2.75rem);
  font-weight: 700;
  line-height: 1.05;
  letter-spacing: .01em;
  text-transform: uppercase;
  margin-bottom: var(--s4);
}

.gate--dark .gate__copy { color: #C6BDB3; }

.gate--dark .field__label { color: var(--copper-2); }

.gate--dark .input {
  background: rgba(255,255,255,.05);
  border-color: rgba(255,255,255,.2);
  color: var(--paper);
  font-size: 1.125rem;
  letter-spacing: .08em;
  padding-block: var(--s3);
}
.gate--dark .input::placeholder { color: #8F857C; }
.gate--dark .input:focus { border-color: var(--copper); background: rgba(255,255,255,.08); }

.gate--dark .btn {
  background: var(--copper-btn);
  border-color: var(--copper-btn);
  color: var(--ink);
  min-height: 50px;
}

.gate--dark .gate__legal { border-top-color: rgba(255,255,255,.12); color: #8F857C; }
.gate--dark .gate__legal a { color: #C6BDB3; border-bottom-color: rgba(255,255,255,.26); }
.gate--dark .gate__legal a:hover { color: var(--copper-2); border-bottom-color: var(--copper); }

.gate--dark .notice--error {
  background: rgba(192,73,47,.14);
  border-color: rgba(192,73,47,.4);
  border-left-color: var(--sale);
  color: #F0D9D2;
}

@media (prefers-reduced-motion: no-preference) {
  .gate--dark .kw > span {
    animation: rk-mask-up .95s var(--ease-mask) both;
    animation-delay: calc(var(--i, 0) * 60ms + 80ms);
  }
  .gate--dark .gate__logo { animation: rk-fade-up .7s var(--ease-out) both; }
  .gate--dark .gate__copy { animation: rk-fade-up .8s var(--ease-out) .3s both; }
  .gate--dark .gate__form { animation: rk-fade-up .8s var(--ease-out) .42s both; }
  .gate--dark .gate__legal { animation: rk-fade-up .8s var(--ease-out) .54s both; }
}

/* Same safety net as the opener: script pins the finished state after three
   seconds, in case the animation timeline never advances. */
.gate--dark.is-settled .kw > span,
.gate--dark.is-settled .gate__logo,
.gate--dark.is-settled .gate__copy,
.gate--dark.is-settled .gate__form,
.gate--dark.is-settled .gate__legal {
  animation: none !important;
  opacity: 1 !important;
  transform: none !important;
}

@media (prefers-reduced-motion: reduce) {
  .gate--dark .kw > span,
  .gate--dark .gate__logo,
  .gate--dark .gate__copy,
  .gate--dark .gate__form,
  .gate--dark .gate__legal {
    animation: none !important; opacity: 1 !important; transform: none !important;
  }
  .gate__smoke { transition: none; }
}

/* ==========================================================================
   SITE COMPONENTS

   The vocabulary the interior pages are built from: the numbered range
   index, flavour plates, spec rows, the ledger and the section rhythm.
   Merged from the per-page passes and harmonised here, which is why it
   reads as one system rather than five.
   ========================================================================== */

/* --- home ------------------------------------------------------------ */

/* ==========================================================================
   HOMEPAGE ADDITIONS (Agent B)

   Everything the homepage needs that Part 4 does not already hand to
   stage.css. No new colour values: every one of these resolves to an existing
   custom property. Append after the Part 4 component block, since a few of
   these deliberately outrank it.
   ========================================================================== */

/* --------------------------------------------------------------------------
   THE GUIDE BAND
   The same band as a flavour, with the flavour taken out. It carries no wash,
   no facts and no number, so the rail gets two hairlines instead and the side
   column holds nothing but the cue.
   -------------------------------------------------------------------------- */

.index--tail { border-top: 0; }

.plate--guide .plate__name { max-width: 18ch; }
.plate--guide .plate__body {
  margin-top: var(--s4);
  max-width: 46ch;
  font-size: var(--t-small);
  color: var(--ink-2);
}
.plate--guide .plate__side { display: flex; align-items: flex-start; }

/* Two hairlines where a numeral would be. A drawn mark rather than a
   punctuation character, so it cannot violate L11 and it cannot be read out. */
.ord--rule { display: inline-flex; align-items: center; gap: 5px; }
.ord--rule::before,
.ord--rule::after {
  content: "";
  display: block;
  width: 9px; height: 1px;
  background: var(--line-2);
}

/* --------------------------------------------------------------------------
   THE NUMBERED FAQ
   Same .acc and data-acc hook as before, set as an index.

   Two live bugs are fixed here. rook.css paints .acc__panel display:grid with
   grid-template-rows:0fr, which (a) outranks the user agent's [hidden] rule so
   a closed panel is not actually hidden, and (b) collapses the row to zero
   height with nothing clipping it, so the answer overflows onto the question
   below. The panels are declared block, [hidden] is honoured explicitly, and
   the markup now ships them open so the answers survive with JavaScript off.
   -------------------------------------------------------------------------- */

.acc--index .acc__btn {
  align-items: baseline;
  gap: var(--s5);
  padding-block: var(--s5);
  font-weight: 600;
}
.acc--index .acc__q {
  display: flex;
  align-items: baseline;
  gap: var(--s4);
  min-width: 0;
}
.acc--index .acc__q .ord { flex: 0 0 2.25rem; }
.acc--index .acc__btn:hover .ord,
.acc--index .acc__btn:focus-visible .ord { color: var(--copper-ink); }

.acc--index .acc__btn svg {
  flex: 0 0 auto;
  color: var(--ink-3);
  transition: transform .3s var(--ease), color .3s var(--ease);
}
.acc--index .acc__btn[aria-expanded="true"] svg { transform: rotate(180deg); }
.acc--index .acc__btn:hover svg { color: var(--copper-ink); }

.acc--index .acc__panel { display: block; }
.acc--index .acc__panel[hidden] { display: none; }
/* Answers hang under the question, not under the ordinal. */
.acc--index .acc__panel p { padding-left: calc(2.25rem + var(--s4)); }
@media (max-width: 620px) {
  .acc--index .acc__panel p { padding-left: 0; }
}

/* --------------------------------------------------------------------------
   SECTION HEADS ON BLACK

   4.2 and 4.16 between them leave .sect__eyebrow at var(--ink-3) and its .ord
   at var(--copper-ink) inside a dark band. Both are near enough to 3:1 on
   #0A0A0A to fail, and Part 9 is explicit that no --ink value and no
   --copper-ink may sit on a dark ground.
   -------------------------------------------------------------------------- */

.bay--dark .sect__eyebrow { color: var(--ink-dark-2); }
.bay--dark .sect__eyebrow .ord { color: var(--copper-2); }

/* --------------------------------------------------------------------------
   THE RANGE STATEMENT

   .sect__statement is capped at 22ch, which is right for the three or four
   word statements it was drawn for. The homepage one carries a full sentence
   and sets as six shouted lines at that measure. Scoped to this one instance,
   so the component is untouched. Type, weight and case stay exactly as
   specified.
   -------------------------------------------------------------------------- */

#range .sect__statement { max-width: 38ch; }

/* --------------------------------------------------------------------------
   THE FLASH BANNER

   The opener pulls itself up under the sticky header by --header-h, which
   slides it straight over a flash banner sitting above it in the flow. The
   banner is the confirmation someone sees after signing up without
   JavaScript, so it has to stay on top. One line, no layout change.
   -------------------------------------------------------------------------- */

.flash { position: relative; z-index: 5; }

/* --------------------------------------------------------------------------
   MOTION

   Nothing new animates on entrance, so there is nothing new to add to the
   Part 7 reduce block. The disclosure chevron is a state change rather than
   an entrance; it is pinned anyway.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .acc--index .acc__btn svg { transition: none; }
}

/* Every answer prints, whatever the script left open. */
@media print {
  .acc--index .acc__panel[hidden] { display: block; }
}
/* --- shop ------------------------------------------------------------ */

/* ==========================================================================
   SHOP / THE INDEX - page-level joins only.

   Everything the bands themselves need (.index, .plate and its parts,
   .phead--index, .sect__eyebrow, .ord, .chip, .stamp, .grid12) is Part 4 of
   the spec and belongs to Agent A's stage.css. The three rules below are the
   only things the shop page needs on top of that, and they exist because this
   is the one page where a .shead sits directly on top of an .index.

   No animation here, so nothing needs a reduced-motion mirror. Every value
   resolves to an existing custom property.
   ========================================================================== */

/* On /shop/ the .shead carries an eyebrow instead of a heading, so it sits in
   the shead's baseline row next to the tabs. Its own bottom margin (which is
   correct when an eyebrow sits above a section statement) would push the two
   sides out of alignment. */
.shead .sect__eyebrow { margin-bottom: 0; }

/* The slash between the ordinal and the eyebrow label is punctuation, not
   content, and reads as noise at full ink weight. */
.sect__eyebrow > span[aria-hidden="true"] { color: var(--line-2); }

/* The .shead already draws its own hairline (G2, .shead::after). Without this
   the index's border-top lands a second hairline var(--s5) below the first,
   which reads as a mistake rather than as a rule. The drawn rule becomes the
   top line of the index and the first band hangs off it. */
.shead + .index { border-top: 0; margin-top: var(--s3); }
/* --- product ------------------------------------------------------------ */

/* ==========================================================================
   PRODUCT PAGE

   Everything here is scoped to .pdp*, .plinth--pdp or .strip__*, so it cannot
   collide with the shared component layer. It DEPENDS on that layer landing
   first: the :root tokens (--t-name, --num, --wash-dark, --ink-dark-2),
   .plate__wash (including the .pdp__plate 52% / --wash-dark override),
   .plate__rail, .plate__soon, .plate__notes, .chip, .stamp, .lrow, .meter,
   .plinth, .strip, .dusk, .dawn--sm, the .smoke sizing fix, and .pdp__plate in
   the is-settled safety net.

   Two rules below override app.css rather than adding to it: .pdp (which sets
   5fr 6fr at 860px) and .pdp__name (which sets its own type). stage.css loads
   last, so they win. The 860 to 900 window is closed explicitly, because a
   two-column split at 880px squeezes the plate to nothing.
   ========================================================================== */

/* --- The crumb row and the two columns ---------------------------------- */

.pdp__crumbs { padding-block: var(--s6); }
.pdp__bay    { padding-block: var(--s5) var(--bay); }

.pdp { display: grid; gap: var(--s7); align-items: start; }
@media (max-width: 899px) { .pdp { grid-template-columns: 1fr; } }
@media (min-width: 900px) { .pdp { grid-template-columns: 5fr 7fr; gap: var(--s9); } }

/* --- The plate ----------------------------------------------------------
   The one black box the spec allows, because the h1 lives inside it. It is
   sticky only above 1100px: below that the right column is short enough that
   a pinned plate just leaves a hole. */

.pdp__plate {
  position: relative;
  display: flex;
  min-height: clamp(26rem, 58vh, 40rem);
  padding: var(--s7) var(--s6);
  background: #08080A;
  color: var(--paper);
  overflow: hidden;
  isolation: isolate;
}
@media (min-width: 900px)  { .pdp__plate { padding: var(--s8) var(--s7); } }
@media (min-width: 1100px) { .pdp__plate { position: sticky; top: calc(var(--header-h) + var(--s6)); } }

/* The frost crown. Opacity comes straight off the cooling meter, so this is
   the product describing itself rather than decoration: Black Ice wears a
   visible one, Pina Colada wears none. */
.pdp__plate::before {
  content: "";
  position: absolute; inset: 0 0 auto; height: 34%;
  z-index: 1; pointer-events: none;
  background: linear-gradient(180deg, rgba(242,239,232,calc(.14 * var(--f-cold, 0))) 0%, transparent 100%);
}

/* One positioned child above the wash, the smoke and the crown, so the type
   needs no z-index of its own and the sticky column stays predictable. */
.pdp__plate-in {
  position: relative; z-index: 3;
  display: flex; flex-direction: column;
  width: 100%;
}
.pdp__plate .plate__rail { margin-bottom: auto; }

.pdp__name {
  margin-block: var(--s6) var(--s4);
  font-family: var(--display);
  font-size: var(--t-name);
  font-weight: 700;
  line-height: .92;
  letter-spacing: -.01em;
  text-transform: uppercase;
  color: var(--paper);
}

.pdp__plate .plate__soon { color: var(--copper-2); }
.pdp__plate .stamp { margin-top: var(--s6); }
.pdp__plate .stamp, .pdp__plate .stamp svg { color: var(--ink-dark-2); }

/* Gold on a tinted dark ground is not reliably visible, so the ring turns
   paper inside both dark surfaces on this page. */
.pdp__plate :focus-visible,
.plinth--pdp :focus-visible { outline-color: var(--paper); outline-offset: 2px; }

/* --- The right column ---------------------------------------------------- */

.pdp__hr {
  display: block; width: 48px; height: 1px;
  margin-top: var(--s5);
  background: var(--copper);
}

.pdp__info .plate__notes { margin-top: var(--s4); }

.pdp__short {
  margin-top: var(--s5);
  max-width: 46ch;
  font-size: 1.0625rem;
  line-height: 1.7;
  color: var(--ink-2);
}

/* The section heads on the right are caps labels, not display type. They are
   signposts on a spec sheet; the only thing set at size on this page is the
   name on the plate. */
.pdp__h {
  margin-top: var(--s8);
  font-family: var(--display);
  font-size: var(--t-micro);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: var(--track);
  color: var(--ink-3);
}
.pdp__h--lg {
  margin-top: 0; margin-bottom: var(--s5);
  font-size: var(--t-h2);
  letter-spacing: .02em;
  color: var(--ink);
}

.pdp__meters { display: grid; gap: var(--s4); margin-top: var(--s5); max-width: 26rem; }
.pdp__lrows  { margin-top: var(--s5); max-width: 28rem; }

.lrow__note {
  margin-left: var(--s3);
  font-family: var(--display);
  font-size: var(--t-micro);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: var(--track);
  color: var(--copper-ink);
}

/* The bottle's single appearance on the site, at diagram scale beside its own
   caption so it reads as a measurement, not as a product shot. */
.pdp__diagram { display: flex; align-items: center; gap: var(--s5); margin-top: var(--s8); }
.pdp__diagram .bottle { flex: 0 0 auto; width: 90px; max-width: 90px; margin-inline: 0; }
.pdp__cap { max-width: 16ch; color: var(--ink-3); }

.pdp__ing { margin-top: var(--s6); max-width: 52ch; line-height: 1.7; color: var(--ink-3); }

/* --- The closing band ---------------------------------------------------- */

.plinth--pdp .plinth__t { max-width: 20ch; }

/* A9.7: the signup is the page's primary control, so its label is visible
   rather than screen-reader only. Better fixed in signupForm() itself once
   src/lib/ is open; this is the CSS-layer version of the same change. */
.plinth--pdp .signup .sr {
  position: static;
  display: block;
  width: auto; height: auto;
  margin: 0 0 var(--s3);
  overflow: visible;
  clip-path: none;
  font-family: var(--display);
  font-size: var(--t-micro);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: var(--track);
  color: var(--copper-2);
}
.plinth--pdp .signup__row { flex-wrap: wrap; }

/* --- The cross-sell strip ------------------------------------------------ */

.strip__n  { flex: 1 1 auto; }
.strip__go { flex: 0 0 auto; color: var(--ink-3); transition: color .3s var(--ease), transform .3s var(--ease); }
.strip__it:hover .strip__go,
.strip__it:focus-visible .strip__go { color: var(--copper-ink); transform: translateX(4px); }

/* --- Motion --------------------------------------------------------------
   G1, above the fold tier. Duplicate of the rule Part 7 assigns to stage.css:
   DEDUPE ON MERGE if Agent A has already landed it. The is-settled net that
   makes it safe is Agent A's and must include .pdp__plate. Nothing else on
   this page animates on entrance. */

@media (prefers-reduced-motion: no-preference) {
  .pdp__plate .kw > span {
    animation: rk-mask-up 1s var(--ease-mask) both;
    animation-delay: calc(var(--i, 0) * 70ms + 100ms);
  }
}

@media (prefers-reduced-motion: reduce) {
  .pdp__plate .kw > span { animation: none !important; opacity: 1 !important; transform: none !important; }
  .strip__go { transition: none; }
}

@media print {
  .pdp__plate { min-height: 0; padding: 0; background: #fff; color: #000; }
  .pdp__plate::before { display: none !important; }
  .pdp__name { color: #000; }
}
/* --- page ------------------------------------------------------------ */

/* ==========================================================================
   INTERIOR PAGES (page.js: about, help, wholesale, legal, flavours, contact)

   Everything Part 4 specifies verbatim is Agent A's and is NOT repeated here.
   This block depends on .grid12 (4.1), .ord (4.2), .chip (4.3), .phead--index
   (4.9), .sect / .sect__eyebrow / .sect__statement (4.16), .matrix and
   .tablewrap (4.12) and .meter (4.8) already being in stage.css.

   Only the five things those sections leave open are below.
   ========================================================================== */

/* --- Stacked bays ---------------------------------------------------------

   Two .bay blocks in a row pay the padding twice and the page opens a hole
   between them. The second one keeps the first one's bottom padding and drops
   its own top, so an interior page reads as one column of copy rather than as
   three separately padded slabs. */

.bay--flush { padding-top: 0; }

/* --- The decision matrix ---------------------------------------------------

   4.12 gives the table its structure. These are the two things it leaves to
   the page: the row link and the closing cell. Gold interfaces, so the hover
   is --copper-ink and nothing here ever picks up a flavour tint (L5). */

.matrix tbody th a { color: var(--ink); }
.matrix tbody tr:hover th a { color: var(--copper-ink); }

/* Gold, not grey. Muted until hover looks tidy on a desktop mock and fails
   in practice: it is the row's only call to action, it sits at 11px, and on a
   touch screen there is no hover to reveal it. --ink-3 on paper is also the
   weakest contrast on the page for the element asking to be clicked. */
.matrix__cta {
  font-family: var(--display); font-size: var(--t-micro); font-weight: 600;
  text-transform: uppercase; letter-spacing: var(--track);
  color: var(--copper-ink); white-space: nowrap;
}
.matrix tbody tr:hover .matrix__cta { color: var(--copper-ink); }

/* The table is the one horizontal scroller on the site, so the wrapper takes a
   tab stop. A tab stop with no visible ring is worse than no tab stop. */
.tablewrap:focus-visible { outline: 2px solid var(--copper); outline-offset: 4px; }

/* --- Numbered FAQ rows -----------------------------------------------------

   The /help/ questions set as an index rather than as more prose: the ordinal
   in a left rail, the question hanging off it. */

.acc--index .acc__q { display: flex; align-items: baseline; gap: var(--s4); min-width: 0; }
.acc--index .acc__q .ord { flex: 0 0 2.25rem; }
.acc--index .acc__btn { align-items: baseline; }
.acc--index .acc__btn:hover .ord,
.acc--index .acc__btn:focus-visible .ord { color: var(--copper-ink); }

/* rook.css collapses .acc__panel to a 0fr grid row and expects an inner
   wrapper this markup does not have. Here the answers are open on load and the
   script closes them with [hidden], so the panel is a plain block. The :not()
   matters: without it this rule would out-specify the [hidden] default and the
   toggle would stop working. */
.acc--index .acc__panel:not([hidden]) { display: block; }
.acc--index .acc__panel p { padding-bottom: var(--s5); }

@media (max-width: 620px) {
  .acc--index .acc__q { gap: var(--s3); }
  .acc--index .acc__q .ord { flex-basis: 1.75rem; }
}

/* --- Contact form on the 12 column grid ------------------------------------

   app.css caps .cform at 560px, which fights the grid. Everything spans the
   full measure except the name and email pair. Below 900px .grid12 drops to
   six columns, so 7/span 6 would run off the end: every field goes full width
   there instead. */

.cform { max-width: none; }
.cform > * { grid-column: 1 / -1; }
.cform .field { margin-bottom: 0; }
.cform .field--half { grid-column: 1 / span 6; }
.cform .field--half ~ .field--half { grid-column: 7 / span 6; }
.cform__note { margin-bottom: var(--s3); }
.cform__go { margin-top: var(--s3); }

@media (max-width: 900px) {
  .cform .field--half,
  .cform .field--half ~ .field--half { grid-column: 1 / -1; }
}
/* --- journal ------------------------------------------------------------ */

/* ==========================================================================
   JOURNAL  (Agent F)

   Additions only. Everything in Part 4.9 (.phead--index) and Part 4.11
   (.ledger) is assumed present from Agent A and is not re-declared here.
   Two of these blocks exist to neutralise live rook.css rules that would
   otherwise fight the new markup, rather than editing rook.css itself.
   ========================================================================== */

/* --- Position in the set --------------------------------------------------
   The journal data carries no date and no author, so the only honest piece of
   metadata an article has is where it sits in the set. It is aria-hidden in
   the markup because the same information is one click away on the index, and
   read aloud it would be noise between the crumbs and the headline. It sits
   tight above the h1, so the clamp(3.5rem, 8vh, 6rem) of air the spec puts
   under the crumbs is preserved and still does its job. */

.phead--index .phead__ord {
  grid-column: 1 / -1;
  display: flex; align-items: baseline; gap: var(--s2);
  margin-bottom: var(--s5);
}
.phead--index .phead__ord .ord { color: var(--copper-ink); }
.phead--index .phead__ord-s {
  font-family: var(--num); font-size: var(--t-micro); font-weight: 600;
  color: var(--line-2);
}

/* --- Ledger rail ----------------------------------------------------------
   Inline on a phone so the row opens with its number, stacked on the desktop
   grid where the rail has var(--rail) to itself. */

.ledger__rail { display: flex; align-items: baseline; gap: var(--s3); }
@media (min-width: 900px) {
  .ledger__rail { flex-direction: column; gap: var(--s2); }
}

/* Only the previous and next rows carry this. Direction is real information,
   so it is readable text rather than an arrow pointing left. */
.ledger__dir {
  font-family: var(--display); font-size: var(--t-micro); font-weight: 600;
  text-transform: uppercase; letter-spacing: var(--track);
  color: var(--ink-3);
  transition: color .3s var(--ease);
}
.ledger__row:hover .ledger__dir,
.ledger__row:focus-within .ledger__dir { color: var(--copper-ink); }

/* --- Focus mirrors hover (A9.6) -------------------------------------------
   The hit area is a stretched pseudo-element, so without this the row gives no
   sign that the link inside it has keyboard focus. */

.ledger__row:focus-within { border-bottom-color: var(--copper); }
.ledger__row:focus-within .ord { color: var(--copper-ink); }
.ledger__row:focus-within .ledger__go { color: var(--copper-ink); transform: translateX(4px); }

/* --- Ledger below the grid breakpoint ------------------------------------
   Once the row stacks to one column, an arrow pinned to the right edge floats
   on its own with nothing to align to, so it sits under the excerpt as a read
   on mark instead. */

@media (max-width: 899px) {
  .ledger__go { justify-self: start; margin-top: var(--s2); }
  .ledger__ex { max-width: 62ch; }
}

/* --- Post navigation ------------------------------------------------------
   rook.css around line 808 still paints .postnav as a two column grid with its
   own top rule, and sets .postnav p in display type at var(--t-h3). Both are
   wrong now that the foot of an article is two ledger rows: the grid would put
   the heading in one column and the rows in the other, and the p rule would
   hit .ledger__ex. This resets them where they land rather than editing
   rook.css out from under Agent A. */

.postnav {
  display: block;
  gap: 0;
  margin-top: var(--s9);
  padding-top: 0;
  border-top: 0;
}
@media (min-width: 640px) { .postnav { grid-template-columns: none; } }

.postnav__h {
  font-family: var(--display); font-size: var(--t-micro); font-weight: 600;
  text-transform: uppercase; letter-spacing: var(--track);
  color: var(--ink-3);
  margin-bottom: var(--s5);
}
.postnav .ledger__ex {
  margin-top: 0;
  font-family: var(--body); font-size: var(--t-small); font-weight: 400;
  color: var(--ink-2);
}

/* --- Long form measure ----------------------------------------------------
   rook.css sets .prose to 72ch, which is right for a terms page read in
   chunks and too wide for something read start to finish. The opening
   paragraph steps up to the standfirst size so the article has one clear way
   in, then settles into body size for the rest. */

.prose--long { max-width: 62ch; }
.prose--long p { line-height: 1.75; }
.prose--long > p:first-child { font-size: 1.0625rem; color: var(--ink); }

/* --- Reduced motion -------------------------------------------------------
   Nothing here animates on entrance, so there is nothing to pin. The one
   moving part is the hover arrow, which is stilled. */

@media (prefers-reduced-motion: reduce) {
  .ledger__go, .ledger__dir { transition: none !important; }
}
/* --------------------------------------------------------------------------
   LAST-RESORT REVEAL

   rook.css hides .rv the moment the inline head script adds .rk-js, and only
   rook.js ever takes that back. If rook.js fails to load, 404s or throws, the
   reveal never happens and the page keeps its primary content at opacity 0
   permanently. Interior pages now carry real content in .rv, so that failure
   would empty them.

   The head script sets .rk-shown on a three second timer of its own, inline
   and independent of any external file, so recovery does not depend on the
   thing that might be missing. It is deliberately blunt: if we have got this
   far, being visible beats being animated.
   -------------------------------------------------------------------------- */

.rk-shown .rv { opacity: 1 !important; transform: none !important; }

@media (prefers-reduced-motion: reduce) {
  .rk-shown .rv { transition: none !important; }
}

/* --------------------------------------------------------------------------
   SECTION STATEMENT

   This class was given a measure and never a type treatment, so it rendered
   as a bare h2: 22.5px sentence case, the only heading on the whole site not
   set in the display face. It read as an unstyled leftover, because it was.
   -------------------------------------------------------------------------- */

.sect__statement {
  font-family: var(--display);
  font-size: var(--t-h2);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .06em;
  line-height: 1.15;
  color: var(--ink);
}
