@import url("fonts.css");

/* A registered custom property is a real colour to the animation engine, not an
   unknown token, which is what lets the whole page cross-fade from one fruit to
   the next instead of snapping. Where @property is unsupported it simply snaps,
   which is the behaviour this had before. */
@property --accent {
  syntax: "<color>";
  inherits: true;
  initial-value: #c0453c;
}

/* ==========================================================================
   Local Liquor — design system, brand manual 1.0 (2026)

   One rule carries the whole thing: everything is ink on paper, and the accent
   hue is the only variable. Layout, typography and structure never change —
   a new fruit takes a free hue off the ladder and nothing else moves.

   The accent is allowed in five places and no others: the rule under the
   wordmark, the dot after LIQUOR, the fruit name, the divider on the back
   label, and the web address. Everything else is ink, secondary or a rule.
   ========================================================================== */

:root {
  /* --- neutrals --------------------------------------------------------- */
  --ink: #17150f;
  --paper: #f7f5f1;
  --ink-2: #6e6a62;   /* secondary text — small sizes only */
  --ink-3: #9a958c;   /* light text */
  --rule: #d5d2cb;
  --rule-soft: #e2dfd9;

  /* --- accent ----------------------------------------------------------- */
  /* Overridden per wine. oklch(0.58 0.14 H): same lightness and chroma every
     time, only the hue moves, so no fruit shouts louder than another. */
  --accent: #c0453c;

  /* --- type ------------------------------------------------------------- */
  --sans: "Archivo", system-ui, -apple-system, "Segoe UI", Helvetica, sans-serif;
  --mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, "Courier New", monospace;

  /* --- rhythm ----------------------------------------------------------- */
  --gutter: clamp(1.25rem, 5vw, 4.5rem);
  --section-y: clamp(4.5rem, 10vw, 9rem);
  --max: 1480px;
  --max-content: 1160px;

  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);

  transition: --accent 0.75s var(--ease);
}

/* ==========================================================================
   Reset
   ========================================================================== */

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

html {
  /* Fixed light design: without this, form controls and the browser canvas
     follow the reader's dark preference. */
  color-scheme: light;
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: 6rem;
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: clamp(0.9375rem, 0.25vw + 0.9rem, 1.0625rem);
  font-weight: 400;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

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

/* Archivo 800 is for display only — never body copy. */
h1, h2, h3, h4 {
  margin: 0;
  font-weight: 800;
  letter-spacing: -0.05em;
  line-height: 0.92;
  text-wrap: balance;
}

p { margin: 0 0 1.1em; }
p:last-child { margin-bottom: 0; }

a { color: inherit; }
button { font: inherit; color: inherit; }
ul, ol { margin: 0; padding: 0; list-style: none; }

/* No italics anywhere: the manual forbids them outright. */
i, em, cite, address { font-style: normal; }

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

::selection { background: var(--ink); color: var(--paper); }

/* ==========================================================================
   Primitives
   ========================================================================== */

.shell {
  width: 100%;
  max-width: var(--max);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.shell--content { max-width: var(--max-content); }

.section { padding-block: var(--section-y); }

.prose { max-width: 56ch; color: var(--ink-2); }

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

.skip-link {
  position: fixed;
  top: 0.75rem; left: 0.75rem;
  z-index: 200;
  padding: 0.65rem 1.1rem;
  background: var(--ink);
  color: var(--paper);
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  text-decoration: none;
  transform: translateY(-200%);
  transition: transform 0.2s var(--ease);
}
.skip-link:focus-visible { transform: none; }

/* --- data: numbers, measures and labels. Mono, caps, +0.2em, never prose --- */
.data {
  font-family: var(--mono);
  font-size: 0.6875rem;
  font-weight: 400;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-2);
}

.data--ink { color: var(--ink); }
.data--light { color: var(--ink-3); }

/* --- hairlines, the structural device of the whole system ---------------- */
.rule {
  height: 1px;
  border: 0;
  margin: 0;
  background: var(--rule);
}
.rule--ink { background: var(--ink); height: 2px; }
.rule--accent { background: var(--accent); height: 2px; }

/* ==========================================================================
   Wordmark

   Live type rather than an image, so the accent follows whichever wine is on
   screen. Proportions come from local-liquor-primary.svg: rules span the block,
   tracking −5%, the dot's diameter is half the cap height and it sits on the
   right edge, and the accent rule sits a quarter of a cap height below LIQUOR.
   Everything is in em, so one font-size scales the lockup.
   ========================================================================== */

.wordmark {
  display: block;
  /* The rules run wider than the words — 100 mm of rule to 15 mm of type in the
     artwork — and it is that block, not the text, that the dot sits at the edge
     of. Everything below is em, so one font-size scales the whole lockup. */
  width: 6.67em;
  font-size: 1rem;
  line-height: 1;
  text-decoration: none;
  color: var(--ink);
}

.wordmark__rule {
  display: block;
  height: 0.08em;
  background: var(--ink);
}

.wordmark__lines {
  position: relative;
  display: block;
  margin: 0.3em 0 0;
  font-family: var(--sans);
  font-weight: 800;
  font-size: 1em;
  line-height: 1;
  letter-spacing: -0.05em;
  text-transform: uppercase;
}

.wordmark__lines span { display: block; }

/* Diameter is half the cap height, and it sits on the right edge of the block. */
.wordmark__dot {
  position: absolute;
  right: 0.06em;
  bottom: 0.3em;
  width: 0.347em;
  height: 0.347em;
  border-radius: 50%;
  background: var(--accent);
}

.wordmark__underline {
  display: block;
  height: 0.08em;
  margin-top: 0.16em;
  background: var(--accent);
}

.wordmark__foot {
  display: flex;
  justify-content: space-between;
  gap: 1em;
  margin-top: 0.3em;
  font-family: var(--mono);
  font-size: 0.173em;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-2);
}

/* The wordmark is never set in the accent, never letterspaced, never rotated. */

/* ==========================================================================
   Headings
   ========================================================================== */

.display {
  font-size: clamp(2.75rem, min(9vw, 13vh), 8rem);
  letter-spacing: -0.055em;
  line-height: 0.9;
}

.h-section {
  font-size: clamp(1.75rem, 4vw, 3.25rem);
  letter-spacing: -0.05em;
}

.h-card {
  font-size: clamp(1.25rem, 1.8vw, 1.6rem);
  letter-spacing: -0.04em;
}

.lede {
  max-width: 46ch;
  font-size: clamp(1rem, 0.5vw + 0.9rem, 1.1875rem);
  font-weight: 400;
  line-height: 1.5;
  color: var(--ink-2);
}

/* ==========================================================================
   Buttons and links
   ========================================================================== */

.btn {
  --btn-bg: transparent;
  --btn-fg: var(--ink);
  --btn-line: var(--ink);
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.85rem 1.4rem;
  background: var(--btn-bg);
  color: var(--btn-fg);
  border: 1px solid var(--btn-line);
  border-radius: 0;
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
  transition: background 0.3s var(--ease), color 0.3s var(--ease),
              border-color 0.3s var(--ease);
}

.btn--solid { --btn-bg: var(--ink); --btn-fg: var(--paper); }
.btn--solid:hover { --btn-bg: transparent; --btn-fg: var(--ink); }
.btn--ghost:hover { --btn-bg: var(--ink); --btn-fg: var(--paper); }
.btn--small { padding: 0.6rem 1rem; font-size: 0.625rem; }

/* The solid button, inverted, for the one block that is ink instead of paper. */
.btn--paper { --btn-bg: var(--paper); --btn-fg: var(--ink); --btn-line: var(--paper); }
.btn--paper:hover { --btn-bg: transparent; --btn-fg: var(--paper); }

.btn__arrow { transition: transform 0.3s var(--ease); }
.btn:hover .btn__arrow { transform: translateX(4px); }

.link {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  text-decoration: none;
  padding-bottom: 0.3rem;
  border-bottom: 1px solid var(--rule);
  transition: border-color 0.3s var(--ease);
}
.link:hover { border-color: var(--ink); }

/* ==========================================================================
   Header
   ========================================================================== */

.site-header {
  position: fixed;
  inset: 0 0 auto;
  z-index: 100;
}
.site-header::after {
  content: "";
  position: absolute;
  inset: 0;
  /* Solid, not frosted. The headline is 8rem of Archivo 800 and it passes
     directly under this: a translucent bar turns it into a grey smear, which
     is the one thing a page built out of hard rules cannot afford. */
  background: var(--paper);
  border-bottom: 1px solid var(--rule-soft);
  opacity: 0;
  transition: opacity 0.35s var(--ease);
  pointer-events: none;
}
.site-header.is-stuck::after { opacity: 1; }

.site-header__inner {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 2rem;
  height: 5.5rem;
}

.site-header .wordmark { font-size: clamp(1.25rem, 1.6vw, 1.55rem); }

.nav { display: flex; align-items: center; gap: clamp(1rem, 2.2vw, 2.25rem); }

.nav__link {
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--ink-2);
  transition: color 0.3s var(--ease);
}
.nav__link:hover { color: var(--ink); }

.header__end { display: flex; align-items: center; gap: 1.25rem; }

.lang { display: flex; align-items: center; gap: 0.3rem; margin: 0; }
.lang__btn {
  padding: 0.25rem 0.1rem;
  background: none;
  border: 0;
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-3);
  cursor: pointer;
  transition: color 0.3s var(--ease);
}
.lang__btn:hover,
.lang__btn[aria-current="true"] { color: var(--ink); }
.lang__sep { color: var(--rule); font-size: 0.6875rem; }

.nav-toggle {
  display: none;
  width: 2.5rem; height: 2.5rem;
  align-items: center; justify-content: center;
  background: none;
  border: 1px solid var(--rule);
  cursor: pointer;
}
.nav-toggle span {
  display: block;
  width: 1rem; height: 1px;
  background: var(--ink);
  position: relative;
  transition: background 0.2s var(--ease);
}
.nav-toggle span::before,
.nav-toggle span::after {
  content: "";
  position: absolute;
  left: 0;
  width: 100%; height: 1px;
  background: var(--ink);
  transition: transform 0.3s var(--ease);
}
.nav-toggle span::before { top: -5px; }
.nav-toggle span::after { top: 5px; }
[data-nav-open] .nav-toggle span { background: transparent; }
[data-nav-open] .nav-toggle span::before { transform: translateY(5px) rotate(45deg); }
[data-nav-open] .nav-toggle span::after { transform: translateY(-5px) rotate(-45deg); }

/* ==========================================================================
   Hero
   ========================================================================== */

.hero {
  position: relative;
  min-height: 100svh;
  display: flex;
  align-items: center;
  padding-top: 5.5rem;
  overflow: hidden;
}

.hero__stage {
  position: absolute;
  inset: 0;
  z-index: 0;
  /* Read by stage.js: how far to slide the rendered frame so the bottles clear
     the copy. Zeroed below, where the hero stacks and there is no copy beside
     them to clear. */
  --stage-shift: 0.19;
}
.hero__stage canvas { width: 100%; height: 100%; }

.hero__inner {
  width: 100%;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: center;
  gap: 2rem;
  pointer-events: none;
}
.hero__inner > * { pointer-events: auto; }

.hero__copy,
.hero__picker { position: relative; z-index: 1; }

.hero__copy,
.hero__picker { max-width: 32rem; }

/* --- the opening move ---------------------------------------------------
   Each line of the headline sits in its own clipped box and rises into it,
   one after the next. It costs two transforms and is the difference between
   a page that fades in and a page that arrives. `.is-lit` is set on the next
   frame after load, so nothing moves until the type is really there. */

.hero__eyebrow { margin: 0 0 1.75rem; }
/* Scoped to .js-reveal for the same reason [data-reveal] is: hidden from CSS
   alone, this is the whole hero gone for a reader without JavaScript. */
.js-reveal .hero__eyebrow { overflow: hidden; }
.js-reveal :is(.hero__eyebrow, .hero__title .line) > span {
  display: block;
  transform: translateY(115%);
  transition: transform 1.15s var(--ease-out);
  transition-delay: calc(var(--line, 0) * 110ms);
}
.js-reveal .hero__eyebrow > span { transition-duration: 0.8s; }

.hero__title { margin-bottom: 1.75rem; }
.hero__title .line {
  display: block;
  /* Scroll pulls the two lines apart as the hero leaves. The clipped box is
     what moves, so the text travels with its own mask and nothing is shaved. */
  transform: translateX(calc(var(--hero-out, 0) * var(--line-dir, 1) * 7vw));
  will-change: transform;
  /* Clips the line on its way in. Padding keeps descenders and the -0.055em
     tracking from being shaved off once it has arrived. */
  overflow: hidden;
  padding-bottom: 0.08em;
  margin-bottom: -0.08em;
}
.hero__title .line:nth-child(1) { --line-dir: -1; }
.hero__title .line:nth-child(2) { --line-dir: 1; }

/* The lede and the buttons drift the other way, half as far, so the block
   comes apart rather than sliding as one slab. */
.hero__body { transform: translateX(calc(var(--hero-out, 0) * -3vw)); }

.js-reveal .hero__title .line > span { transition-delay: calc(120ms + var(--line, 0) * 110ms); }

.js-reveal .hero.is-lit :is(.hero__eyebrow, .hero__title .line) > span { transform: none; }

/* The rest of the hero follows the type in. */
.js-reveal :is(.hero__body, .hero__picker) {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 0.8s var(--ease-out), transform 0.8s var(--ease-out);
  transition-delay: 480ms;
}
.js-reveal .hero__picker { transition-delay: 580ms; }
.js-reveal .hero.is-lit .hero__picker { opacity: 1; transform: none; }
.js-reveal .hero.is-lit .hero__body {
  opacity: 1;
  transform: translateX(calc(var(--hero-out, 0) * -3vw));
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-top: 2.25rem;
}

/* --- the picker: a list of fruit, one accent dot each --------------------
   In the copy column, not the right-hand one. It used to sit at the right edge
   under the bottles, which works right up until the window is narrow enough for
   the bottle to be wider than the gap — and then the fruit names are printed
   over the label. Nothing but the bottle lives on the right now. */
.hero__picker {
  grid-column: 1;
  justify-self: start;
  align-self: start;
  margin-top: clamp(1.25rem, 3vh, 2.5rem);
}

.picker__hint { display: block; margin-bottom: 0.8rem; }

.picker__list {
  display: flex;
  flex-wrap: wrap;
  gap: 0 clamp(1.25rem, 2.5vw, 2rem);
}

.picker__btn {
  display: flex;
  align-items: center;
  gap: 0.8rem;
  padding: 0.45rem 0;
  background: none;
  border: 0;
  cursor: pointer;
  font-family: var(--sans);
  font-size: clamp(1.05rem, 1.4vw, 1.35rem);
  font-weight: 600;
  letter-spacing: -0.03em;
  color: var(--ink-3);
  transition: color 0.3s var(--ease);
}
.picker__btn:hover,
.picker__btn[aria-current="true"] { color: var(--ink); }

.picker__dot {
  order: 1;
  width: 0.5rem; height: 0.5rem;
  flex: none;
  border-radius: 50%;
  background: var(--wine-accent, var(--accent));
  opacity: 0.35;
  transform: scale(0.7);
  transition: opacity 0.3s var(--ease), transform 0.3s var(--ease);
}
.picker__btn[aria-current="true"] .picker__dot { opacity: 1; transform: none; }

/* shown until the 3D scene reports ready; also the no-WebGL fallback */
.hero__fallback {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  padding: 3rem 0;
  transition: opacity 0.5s var(--ease);
}
.hero__fallback :is(img, canvas) {
  width: auto;
  max-width: min(40%, 240px);
  height: min(64%, 30rem);
  object-fit: contain;
}
.stage-ready .hero__fallback {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.5s var(--ease), visibility 0s linear 0.5s;
}

/* The copy column now carries the picker as well, so on a laptop the hero can
   run past the fold and cut the fruit names in half. Tighten the rhythm rather
   than shrink the headline — the headline is the point. */
@media (max-height: 880px) and (min-width: 1101px) {
  .hero__eyebrow { margin-bottom: 1rem; }
  .hero__title { margin-bottom: 1.1rem; }
  .hero__actions { margin-top: 1.5rem; }
  .hero { padding-top: 4.5rem; }
}

/* ==========================================================================
   Ticker

   A marquee of the same words the label carries. Two identical runs inside the
   track, so translating the track by exactly -50% lands run two where run one
   started and the loop has no seam.
   ========================================================================== */

.ticker {
  border-block: 1px solid var(--ink);
  padding-block: 0.85rem;
  overflow: hidden;
}

.ticker__track {
  display: flex;
  width: max-content;
  animation: ticker-run 46s linear infinite;
}
/* No pause on hover. The band runs the full width of the page, so simply
   scrolling puts the pointer on it and the marquee stopped dead — which reads
   as broken, not as considerate. */

.ticker__run { display: flex; }

.ticker__item {
  display: block;
  flex: none;
  padding-inline: 1.6rem;
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--ink-2);
}
.ticker__item::after {
  content: "\2022";
  margin-left: 1.6rem;
  color: var(--rule);
}
/* The fruit names are the one place the accent is allowed to travel through
   the ticker, each carrying its own hue rather than the page accent. */
.ticker__item--accent { color: var(--accent); }

@keyframes ticker-run {
  to { transform: translateX(-50%); }
}

/* ==========================================================================
   Figures strip
   ========================================================================== */

.stats {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  border-top: 1px solid var(--ink);
  border-bottom: 1px solid var(--rule);
}

.stat {
  padding: clamp(1.5rem, 3vw, 2.5rem) 1.25rem clamp(1.5rem, 3vw, 2.5rem) 0;
  border-left: 1px solid var(--rule-soft);
  padding-left: clamp(1rem, 2vw, 1.75rem);
}
.stat:first-child { border-left: 0; padding-left: 0; }

.stat__value {
  display: block;
  font-size: clamp(1.5rem, 2.6vw, 2.25rem);
  font-weight: 800;
  letter-spacing: -0.05em;
  line-height: 1;
}

.stat__label { display: block; margin-top: 0.7rem; }

/* ==========================================================================
   Index

   Both the range and the market calendar are the same object: a full-bleed
   list of rows, each one a rule, a number, a name and its facts. The rows are
   deliberately tall — this is the page's second act, and a list of links set
   at body size is what made it read as an afterthought.
   ========================================================================== */

.section--index { padding-block: var(--section-y) 0; }

.index__head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(2rem, 5vw, 5rem);
  align-items: end;
  margin-bottom: clamp(2.5rem, 5vw, 4rem);
}

.index { border-top: 1px solid var(--ink); }
.index__item { border-bottom: 1px solid var(--rule); }

.index__row {
  display: block;
  position: relative;
  text-decoration: none;
  color: inherit;
  /* The label rises out of the bottom of the row, so the row has to clip it. */
  overflow: hidden;
  transition: background 0.5s var(--ease);
}

/* A wash of the wine's own accent, at the strength a rule is drawn at. The
   accent is allowed on the fruit name; this is the same hue carrying the
   hover state rather than a second colour. */
.index__row:not(.index__row--static)::before {
  content: "";
  position: absolute;
  inset: 0;
  background: color-mix(in srgb, var(--accent) 7%, transparent);
  opacity: 0;
  transition: opacity 0.45s var(--ease);
}
.index__row:is(:hover, :focus-visible)::before { opacity: 1; }

.index__inner {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: 4rem minmax(0, 1fr) auto clamp(6.5rem, 10vw, 11rem) 3rem;
  align-items: center;
  gap: clamp(1rem, 2.5vw, 2.5rem);
  min-height: clamp(9rem, 15vw, 14rem);
  padding-block: 1.5rem;
}

.index__no {
  align-self: start;
  padding-top: 0.5rem;
  color: var(--ink-3);
  transition: color 0.45s var(--ease);
}
.index__row:is(:hover, :focus-visible) .index__no { color: var(--accent); }

.index__name {
  display: block;
  font-size: clamp(2rem, 5vw, 4.25rem);
  font-weight: 800;
  letter-spacing: -0.05em;
  line-height: 0.92;
  transition: color 0.45s var(--ease);
}
.index__row:is(:hover, :focus-visible) .index__name { color: var(--accent); }
.index__name--sm { font-size: clamp(1.5rem, 2.6vw, 2.25rem); }

.index__sub { display: block; margin-top: 0.7rem; }

.index__meta {
  display: grid;
  justify-items: end;
  gap: 0.6rem;
  text-align: right;
}

/* The slot is always there; only its contents move, so hovering a row never
   shifts the row next to it. At rest the label stands about a third out of the
   bottom rule — enough that the row has something in it, and enough left to
   give hover somewhere to go. Hidden entirely, the row was mostly white space. */
.index__art {
  display: block;
  aspect-ratio: 67.7 / 99.1;
  align-self: end;
  /* Sits on the bottom rule, so the label reads as tucked into the row. */
  margin-bottom: -1.5rem;
  background: #fff;
  border: 1px solid var(--rule-soft);
  border-bottom: 0;
  transform: translateY(62%);
  transition: transform 0.6s var(--ease-out);
}
.index__art canvas { width: 100%; height: auto; }
.index__row:is(:hover, :focus-visible) .index__art { transform: none; }

.index__go {
  justify-self: end;
  font-size: 1.25rem;
  color: var(--ink-3);
  transition: transform 0.45s var(--ease-out), color 0.45s var(--ease);
}
.index__row:is(:hover, :focus-visible) .index__go {
  transform: translateX(0.4rem);
  color: var(--ink);
}

.index__row.is-sold-out .index__name { color: var(--ink-3); }

/* Without hover there is nothing to reveal, so the label just sits open. */
@media (hover: none) {
  .index__art { transform: none; }
}

.index__empty {
  display: grid;
  justify-items: start;
  gap: 2rem;
  padding-block: clamp(1rem, 3vw, 2rem) clamp(3rem, 7vw, 6rem);
  border-top: 1px solid var(--ink);
}

.index__empty-line {
  margin: 0;
  max-width: 20ch;
  font-size: clamp(1.75rem, 4vw, 3.25rem);
  font-weight: 800;
  letter-spacing: -0.05em;
  line-height: 0.95;
  text-wrap: balance;
}

/* ==========================================================================
   Contact

   The one inverted block on the site. Same ink, same paper, swapped over —
   which is why it can be this loud without stepping outside the manual.
   ========================================================================== */

.contact {
  margin-top: clamp(4rem, 9vw, 8rem);
  background: var(--ink);
  color: var(--paper);
}

.contact__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(2.5rem, 7vw, 7rem);
  align-items: start;
}

.contact__eyebrow { color: color-mix(in srgb, var(--paper) 55%, transparent); }

.contact__title {
  margin-top: 1.25rem;
  font-size: clamp(2rem, 4.5vw, 3.75rem);
  letter-spacing: -0.05em;
}

.contact__lede {
  max-width: 42ch;
  margin-top: 1.75rem;
  font-size: 1.0625rem;
  line-height: 1.55;
  color: color-mix(in srgb, var(--paper) 78%, transparent);
}

.contact__mail {
  display: inline-block;
  margin-top: clamp(2rem, 4vw, 3rem);
  padding-bottom: 0.12em;
  font-size: clamp(1.05rem, 1.7vw, 1.4rem);
  font-weight: 600;
  letter-spacing: -0.02em;
  text-decoration: none;
  /* A permanent hairline with a full-weight rule that wipes across it. */
  background-image:
    linear-gradient(var(--paper), var(--paper)),
    linear-gradient(color-mix(in srgb, var(--paper) 25%, transparent),
                    color-mix(in srgb, var(--paper) 25%, transparent));
  background-size: 0% 1px, 100% 1px;
  background-position: 0 100%, 0 100%;
  background-repeat: no-repeat;
  transition: background-size 0.5s var(--ease-out);
  overflow-wrap: break-word;
}
.contact__mail:is(:hover, :focus-visible) { background-size: 100% 1px, 100% 1px; }

/* The focus ring is drawn in the accent, which on ink needs to be paper
   instead — the accent hues are mid-lightness and vanish against it. */
.contact :focus-visible { outline-color: var(--paper); }

/* --- the form ----------------------------------------------------------- */

.cform { display: grid; gap: 1.75rem; }

.cfield { display: block; }

.cfield__label {
  display: block;
  margin-bottom: 0.7rem;
  color: color-mix(in srgb, var(--paper) 50%, transparent);
}

/* Underlines, not boxes. A boxed input in the middle of a page built out of
   rules reads as something pasted in from another site. */
.cfield__input {
  display: block;
  width: 100%;
  padding: 0.55rem 0 0.7rem;
  background: none;
  border: 0;
  border-bottom: 1px solid color-mix(in srgb, var(--paper) 30%, transparent);
  border-radius: 0;
  font-family: var(--sans);
  font-size: 1.0625rem;
  color: var(--paper);
  transition: border-color 0.3s var(--ease);
}
.cfield__input::placeholder { color: color-mix(in srgb, var(--paper) 35%, transparent); }
.cfield__input:hover { border-bottom-color: color-mix(in srgb, var(--paper) 55%, transparent); }
.cfield__input:focus {
  outline: 0;
  border-bottom-color: var(--paper);
}
textarea.cfield__input { resize: vertical; line-height: 1.5; }

/* Chrome paints its own blue over a dark field on autofill. */
.cfield__input:-webkit-autofill {
  -webkit-text-fill-color: var(--paper);
  -webkit-box-shadow: 0 0 0 100px var(--ink) inset;
}

.cfield__error,
.cform__errors {
  display: block;
  margin-top: 0.6rem;
  font-size: 0.875rem;
  color: color-mix(in srgb, var(--paper) 92%, transparent);
}
.cfield__error:empty,
.cform__errors:empty { display: none; }
.cform__errors ul { padding-left: 1.1rem; list-style: disc; }

/* Off-screen rather than display:none — a bot that skips hidden fields still
   fills this one in, which is the whole point. */
.cform__trap {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.cform__send { justify-self: start; margin-top: 0.5rem; }

.cform__note { color: color-mix(in srgb, var(--paper) 45%, transparent); }

.contact__sent {
  padding: clamp(1.75rem, 4vw, 2.5rem);
  border: 1px solid color-mix(in srgb, var(--paper) 25%, transparent);
}
.contact__sent-line {
  margin: 0;
  font-size: clamp(1.25rem, 2.2vw, 1.75rem);
  font-weight: 800;
  letter-spacing: -0.04em;
}
.contact__sent .contact__lede { margin-top: 1rem; }

/* ==========================================================================
   Footer
   ========================================================================== */

.site-footer {
  padding-block: clamp(3rem, 6vw, 5rem) 2.25rem;
  border-top: 1px solid var(--ink);
}

.footer__top {
  display: grid;
  grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr);
  gap: clamp(2rem, 5vw, 5rem);
  align-items: start;
  padding-bottom: clamp(2.5rem, 5vw, 4rem);
}

.site-footer .wordmark { font-size: clamp(1.75rem, 2.4vw, 2.25rem); }

.footer__tagline { margin-top: 1.5rem; max-width: 30ch; color: var(--ink-2); font-size: 0.9375rem; }

.footer__nav {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 2rem;
  align-content: start;
}
.footer__nav nav { display: grid; gap: 0.8rem; align-content: start; }
.footer__nav-title { margin: 0 0 0.3rem; }
.footer__nav a {
  font-size: 0.9375rem;
  text-decoration: none;
  color: var(--ink-2);
  transition: color 0.3s var(--ease);
}
.footer__nav a:hover { color: var(--ink); }

.footer__bottom {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1rem 2rem;
  padding-top: 1.75rem;
  border-top: 1px solid var(--rule-soft);
}

.age-note { display: flex; align-items: center; gap: 0.7rem; }
.age-note::before {
  content: "18+";
  flex: none;
  display: grid;
  place-items: center;
  width: 2rem; height: 2rem;
  border: 1px solid var(--ink);
  border-radius: 50%;
  font-family: var(--mono);
  font-size: 0.55rem;
  letter-spacing: 0;
  color: var(--ink);
}

/* ==========================================================================
   Product page
   ========================================================================== */

.product { position: relative; padding-top: calc(5.5rem + clamp(2rem, 5vw, 4rem)); overflow: hidden; }

.product__stage { position: absolute; inset: 0; z-index: 0; --stage-shift: -0.16; }
.product__stage canvas { width: 100%; height: 100%; }

.product__fallback {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  transition: opacity 0.5s var(--ease);
}
.product__fallback :is(img, canvas) {
  width: auto;
  max-width: min(46%, 260px);
  height: min(62%, 30rem);
  object-fit: contain;
}
.stage-ready .product__fallback {
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.5s var(--ease), visibility 0s linear 0.5s;
}

.product__grid {
  position: relative;
  z-index: 1;
  /* The grid sits over the stage, so without this it swallows every drag meant
     for the bottle and you can only turn it by grabbing the margins. */
  pointer-events: none;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(2.5rem, 6vw, 6rem);
  align-items: center;
  min-height: min(76vh, 700px);
}
.product__grid > * { pointer-events: auto; }

.product__copy { grid-column: 2; }

.product__title {
  font-size: clamp(2.5rem, 6vw, 4.75rem);
  letter-spacing: -0.055em;
  line-height: 0.9;
  color: var(--accent);
}

.product__sub { display: block; margin-top: 1rem; }

.product__body { margin-top: 1.75rem; max-width: 44ch; color: var(--ink-2); }

.product__actions { display: flex; flex-wrap: wrap; gap: 0.75rem; margin-top: 2rem; }

/* the hint that the bottle can be turned */
.product__turn { margin-top: 1.25rem; }

.spec-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  margin-top: clamp(3rem, 6vw, 4.5rem);
  border-top: 1px solid var(--ink);
}
.spec {
  padding: clamp(1.25rem, 2.4vw, 1.75rem) 1rem clamp(1.25rem, 2.4vw, 1.75rem) 0;
  border-left: 1px solid var(--rule-soft);
  padding-left: clamp(1rem, 2vw, 1.5rem);
}
/* The divider belongs between columns, so whichever cell opens a row loses it
   — otherwise a wrapped grid draws a rule hanging off the left margin. */
.spec:nth-child(4n + 1) { border-left: 0; padding-left: 0; }
.spec__label { display: block; margin-bottom: 0.5rem; }
.spec__value {
  font-family: var(--mono);
  font-size: clamp(0.9375rem, 1.4vw, 1.125rem);
  letter-spacing: 0.06em;
}

.ingredients {
  margin-top: clamp(2.5rem, 5vw, 3.5rem);
  padding-top: 1.5rem;
  border-top: 1px solid var(--rule);
  max-width: 60ch;
}
.ingredients__label { display: block; margin-bottom: 0.6rem; }

/* ==========================================================================
   Stock
   ========================================================================== */

.stock-line { display: flex; gap: 0.6rem; margin: 0 0 1.25rem; }

/* In an index row it is one line of a stack, not a paragraph above a heading. */
.index__meta .stock-line { margin: 0; }

/* ==========================================================================
   Photo strip
   ========================================================================== */

.photo-strip {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: clamp(0.75rem, 1.5vw, 1.25rem);
  margin-top: clamp(2.5rem, 5vw, 4rem);
}
.photo { overflow: hidden; background: var(--rule-soft); }
.photo img {
  width: 100%;
  aspect-ratio: 4 / 5;
  max-height: 26rem;
  object-fit: cover;
  transition: transform 0.7s var(--ease-out);
}
.photo:only-child img { aspect-ratio: 16 / 9; max-height: 30rem; }
.photo:hover img { transform: scale(1.03); }

/* ==========================================================================
   Age gate
   ========================================================================== */

.age-gate {
  position: fixed;
  inset: 0;
  z-index: 300;
  width: 100%; max-width: none;
  height: 100%; max-height: none;
  margin: 0;
  padding: var(--gutter);
  border: 0;
  background: var(--paper);
  color: var(--ink);
  display: grid;
  place-items: center;
  transition: opacity 0.4s var(--ease);
}
.age-gate:not([open]) { display: none; }
.age-gate::backdrop { background: var(--paper); }
.age-gate.is-leaving { opacity: 0; }

.age-gate__inner { width: 100%; max-width: 30rem; }

.age-gate .wordmark {
  font-size: clamp(2rem, 5vw, 2.75rem);
  margin-bottom: clamp(3rem, 8vh, 5rem);
}

.age-gate__title {
  font-size: clamp(1.75rem, 5vw, 2.75rem);
  letter-spacing: -0.05em;
  line-height: 0.95;
}

.age-gate__lede { margin: 1.5rem 0 0; max-width: 38ch; color: var(--ink-2); }

.age-gate__actions { display: flex; flex-wrap: wrap; gap: 0.75rem; margin-top: 2.5rem; }

.age-gate__note { margin: 2rem 0 0; }

/* ==========================================================================
   Scroll reveal
   ========================================================================== */

/* Scoped to .js-reveal, which an inline script in the head sets only when
   there is an IntersectionObserver to take the class off again. Hiding this
   from CSS alone would leave the whole page invisible to a reader without
   JavaScript — which is exactly the reader the flat fallback is for. */
.js-reveal [data-reveal] {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 0.7s var(--ease-out), transform 0.7s var(--ease-out);
  transition-delay: var(--reveal-delay, 0ms);
}
.js-reveal [data-reveal].is-visible { opacity: 1; transform: none; }

/* ==========================================================================
   Responsive
   ========================================================================== */

@media (max-width: 1100px) {
  .index__head { grid-template-columns: minmax(0, 1fr); }
  .footer__top { grid-template-columns: minmax(0, 1fr); }

  /* Drop the arrow and let the label and the facts share what is left. */
  .index__inner {
    grid-template-columns: 3rem minmax(0, 1fr) clamp(5rem, 14vw, 7rem);
    min-height: 0;
  }
  .index__meta {
    grid-column: 2;
    grid-row: 2;
    justify-items: start;
    text-align: left;
    margin-top: 1rem;
  }
  .index__art { grid-column: 3; grid-row: 1 / span 2; }
  .index__go { display: none; }
}

@media (max-width: 560px) {
  .index__inner { grid-template-columns: minmax(0, 1fr) 5rem; gap: 1rem; }
  .index__no { grid-column: 1; align-self: auto; padding-top: 0; }
  .index__main { grid-column: 1; }
  .index__meta { grid-column: 1; grid-row: 3; }
  .index__art { grid-column: 2; grid-row: 1 / span 3; align-self: end; }
}

/* Two conditions, one layout. Width is the obvious one; aspect is the one that
   bit. The stage only moves the bottles out of the copy's column when the frame
   is wider than 1.15 (below that there is nowhere to move them to), so on a tall
   window they come back to the middle — straight over the headline — while the
   grid was still happily two-column. Both now stack on the same condition. */
@media (max-width: 1100px), (max-aspect-ratio: 23 / 20) {
  .hero { min-height: auto; padding-block: 7.5rem 2.5rem; }

  .hero__stage,
  .product__stage { --stage-shift: 0; }

  /* The hero stacks here, so there is no second column for the type to move
     into — drifting it just knocks the copy out of the page margin. Matching
     the specificity of the lit-state rule, which sets the same property. */
  .hero__title .line,
  .js-reveal .hero.is-lit .hero__body,
  .hero__body { transform: none; }

  .hero__inner { grid-template-columns: minmax(0, 1fr); gap: 0; }
  .hero__copy { display: contents; }
  .hero__head { grid-row: 1; }
  .hero__stage {
    position: relative;
    inset: auto;
    grid-row: 2;
    height: min(46svh, 400px);
    margin-block: 1.5rem;
  }
  .hero__body { grid-row: 3; }
  .hero__picker {
    grid-column: 1;
    grid-row: 4;
    justify-self: stretch;
    max-width: none;
    margin-top: 2rem;
  }

  /* Stacked, the two rows no longer need a column-height to match, and
     centring them inside 700px just opens a hole between bottle and copy. */
  .product__grid {
    grid-template-columns: minmax(0, 1fr);
    min-height: 0;
    align-items: start;
    gap: 0;
  }
  .product__copy { grid-column: 1; grid-row: 2; }
  .product__stage {
    position: relative;
    inset: auto;
    grid-row: 1;
    height: min(48svh, 400px);
    margin-bottom: 2rem;
  }
  .spec-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .spec { border-left: 1px solid var(--rule-soft); padding-left: clamp(1rem, 2vw, 1.5rem); }
  .spec:nth-child(2n + 1) { border-left: 0; padding-left: 0; }
}

@media (max-width: 780px) {
  .nav {
    position: fixed;
    inset: 0;
    z-index: 90;
    flex-direction: column;
    align-items: flex-start;
    justify-content: center;
    gap: 1.5rem;
    padding: 6rem var(--gutter) 3rem;
    background: var(--paper);
    transform: translateX(100%);
    visibility: hidden;
    transition: transform 0.45s var(--ease-out), visibility 0.45s;
  }
  [data-nav-open] .nav { transform: none; visibility: visible; }
  .nav__link { font-size: 1.5rem; letter-spacing: 0.06em; color: var(--ink); }
  .nav-toggle { display: inline-flex; position: relative; z-index: 95; }

  .stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .stat:nth-child(3) { border-left: 0; padding-left: 0; }
  .stat:nth-child(n + 3) { border-top: 1px solid var(--rule-soft); }

  .footer__bottom { flex-direction: column; align-items: flex-start; }
}

@media (max-width: 480px) {
  .spec-grid { grid-template-columns: minmax(0, 1fr); }
  .spec { border-left: 0; padding-left: 0; }
  .spec { border-left: 0; padding-left: 0; border-top: 1px solid var(--rule-soft); }
  .spec:first-child { border-top: 0; }
}

/* ==========================================================================
   Motion preferences
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  .js-reveal [data-reveal] { opacity: 1; transform: none; }

  .ticker__track { animation: none; }
  .js-reveal :is(.hero__eyebrow, .hero__title .line) > span { transform: none; }
  .js-reveal :is(.hero__body, .hero__picker) { opacity: 1; transform: none; }
  .hero__title .line { transform: none; }
}
