/* Auroch Canning — design system v4
   Near-black ground, warm kraft "report"/"plate" paper for document-like
   content, a hand-stamped red for attitude and CTAs. Replaces the v3
   "Engineering Print" cyanotype-blueprint world. */

@import url('https://fonts.googleapis.com/css2?family=Staatliches&family=Source+Serif+4:ital,wght@0,400;0,500;0,600;0,700;1,400;1,600&family=Courier+Prime&display=swap');

:root {
  --ink: #121212;
  --ink-deep: #0A0A0A;
  --ink-mid: #35291F;
  --bone: #EDE4D2;
  --bone-dim: #A79A85;
  --report: #E4D9C1;
  --report-dim: #D2C2A0;
  --text: #1C1712;
  --text-soft: #5B4E3D;
  --stamp: #BC311C;
  --stamp-link: #E44E36;
  --stamp-deep: #8A1F12;
  --radius: 1px;
  --grid: 24px;
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--ink);
  background-image: radial-gradient(rgba(237,228,210,0.05) 1px, transparent 1.4px);
  background-size: 10px 10px;
  color: var(--bone);
  font-family: 'Source Serif 4', Georgia, serif;
  font-size: 17px;
  line-height: 1.6;
}

::selection { background: var(--stamp); color: var(--bone); }

h1, h2, h3, h4, .display {
  font-family: 'Staatliches', 'Arial Narrow', sans-serif;
  font-weight: 400;
  color: var(--bone);
  margin: 0;
  letter-spacing: 0.01em;
  line-height: 1.1;
}
h1, h2, .display { text-transform: uppercase; line-height: 1.02; }

.plate h1, .plate h2, .plate h3, .plate h4 { color: var(--text); }
.plate a { color: var(--stamp-deep); }

a { color: var(--stamp-link); text-decoration: none; }
a:hover { text-decoration: underline; }
a:focus-visible, button:focus-visible, input:focus-visible, textarea:focus-visible, select:focus-visible {
  outline: 2px solid var(--stamp);
  outline-offset: 2px;
}

img { max-width: 100%; display: block; }

.wrap { max-width: 1120px; margin: 0 auto; padding: 0 28px; }

.mono {
  font-family: 'Courier Prime', 'Courier New', monospace;
}

/* ---------- Header / Nav ---------- */

.site-header {
  background: var(--ink-deep);
  position: sticky;
  top: 0;
  z-index: 30;
  border-bottom: 1px solid var(--ink-mid);
}

.nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-top: 14px;
  padding-bottom: 14px;
  gap: 24px;
}

.brand { display: flex; align-items: center; gap: 14px; }
.brand img { height: 50px; width: auto; }
.brand .loc {
  font-family: 'Courier Prime', 'Courier New', monospace;
  font-size: 0.68rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--bone-dim);
  border-left: 1px solid var(--ink-mid);
  padding-left: 14px;
}

.nav-links {
  display: flex;
  gap: 30px;
  font-family: 'Source Serif 4', serif;
  font-weight: 600;
  font-size: 0.95rem;
}
.nav-links a { color: var(--bone-dim); }
.nav-links a:hover { color: var(--bone); text-decoration: none; }
.nav-links a[aria-current="page"] { color: var(--stamp-link); }

.nav-cta {
  font-family: 'Courier Prime', 'Courier New', monospace;
  font-size: 0.78rem;
  font-weight: 400;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--bone) !important;
  background-color: var(--stamp);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='90' height='90'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.7' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.28 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-blend-mode: multiply;
  padding: 10px 18px;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  box-sizing: border-box;
  border-radius: var(--radius);
  box-shadow: inset 0 -2px 0 rgba(0,0,0,0.28);
  transform: rotate(-0.6deg);
  transition: transform 120ms cubic-bezier(0.16, 1, 0.3, 1), box-shadow 120ms cubic-bezier(0.16, 1, 0.3, 1), background 150ms ease;
}
.nav-cta:hover { background-color: #A82C1A; text-decoration: none; }
.nav-cta:active { transform: rotate(-0.6deg) translateY(2px); box-shadow: inset 0 -1px 0 rgba(0,0,0,0.3); }
.nav-cta .cta-short { display: none; }

.nav-toggle {
  display: none;
  width: 44px;
  height: 44px;
  background: transparent;
  border: 1px solid var(--ink-mid);
  border-radius: var(--radius);
  padding: 0;
  position: relative;
  cursor: pointer;
}
.nav-toggle span {
  position: absolute;
  left: 9px;
  right: 9px;
  height: 2px;
  background: var(--bone);
  transition: transform 220ms cubic-bezier(0.16, 1, 0.3, 1), opacity 180ms ease;
}
.nav-toggle span:nth-child(1) { top: 16px; }
.nav-toggle span:nth-child(2) { top: 21px; }
.nav-toggle span:nth-child(3) { top: 26px; }
.nav-toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(5px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-5px) rotate(-45deg); }

/* ---------- Hero ---------- */

.hero {
  position: relative;
  overflow: hidden;
  padding: 56px 0 0;
}

.hero--inner .hero-inner { padding-top: 30px; padding-bottom: 32px; }

.hero-inner {
  padding-top: 44px;
  padding-bottom: 56px;
  position: relative;
}

.hero:not(.hero--inner) .hero-inner {
  display: grid;
  /* 1.3fr/0.85fr puts the right column (photo or, per /impeccable
     overdrive, the can stage) at ~40% of the row, inside the 40-45%
     the brief asked for — nudged from the previous 1.05/0.95 split. */
  grid-template-columns: 1.3fr 0.85fr;
  gap: 20px 48px;
  align-items: center;
}

/* Small recurring mark on inner-page heroes. */
.hero-mark--small {
  position: absolute;
  right: 2%;
  top: 4%;
  width: 14%;
  aspect-ratio: 2551 / 3301;
  opacity: 0.22;
  pointer-events: none;
  background-color: var(--bone-dim);
  -webkit-mask-image: url('assets/logo-white.png');
  mask-image: url('assets/logo-white.png');
  -webkit-mask-size: contain;
  mask-size: contain;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center top;
  mask-position: center top;
}

.hero-content { position: relative; max-width: 62ch; }

.hero h1 {
  font-size: clamp(2.7rem, 5.6vw, 4.4rem);
  max-width: 15ch;
}

.hero .lede {
  margin-top: 22px;
  font-size: 1.14rem;
  color: var(--bone-dim);
  max-width: 46ch;
  font-family: 'Source Serif 4', serif;
}
/* Closing statement inside a .lede, called out from the paragraph above it —
   first used on Home's "Now we make beverages for brands like yours." via
   a Live-mode typeset pass. Superseded the plain inline .lede-accent color
   treatment from the round before it.
   Fixed by a /impeccable polish pass, second round: dropping
   text-transform: uppercase from Staatliches (the first fix) didn't
   actually solve the all-caps-body finding — rendered and inspected, the
   sentence still read as full caps, because Staatliches has no true
   lowercase forms; every letter draws at cap height regardless of source
   case or the text-transform property. Switched to bold Source Serif 4
   (the body face, which does have real lowercase) instead: still its own
   line, still larger and accent-colored, still reads as a called-out
   statement, but now as an actual readable sentence. Verified by
   screenshot, not just by the detector going quiet. */
.lede-close {
  display: block;
  margin-top: 10px;
  font-family: 'Source Serif 4', serif;
  font-weight: 700;
  font-size: 1.2rem;
  color: var(--stamp-link);
}

.hero-actions { margin-top: 34px; display: flex; gap: 16px; flex-wrap: wrap; }

/* ---------- Section Index (Capabilities-only jump nav) ----------
   Fixed by an /impeccable layout pass: six equip-blocks stacked in one
   long single column gave no way to see the page's shape or reach a
   specific spec without scrolling past everything above it — working
   directly against the hero's own promise ("so you can tell quickly
   whether we're a fit"). This slim index right after the lede lets a
   visitor scan the full list of what's covered in one glance on arrival,
   or jump straight to the one line they came to check. Deliberately not
   sticky: pinning a second nav bar under the site header permanently
   would be more chrome than this page's length actually calls for — it's
   meant to be read once on arrival, not to shadow the whole scroll. */
.section-index {
  margin-top: 26px;
  display: flex;
  flex-wrap: wrap;
  gap: 8px 0;
}
/* One straight line on desktop, by direct request — moved out of
   .hero-content above so it isn't squeezed into that div's 62ch reading
   measure (fine for the headline/lede, too narrow for a 6-item nav row).
   Matches the site's existing 840px breakpoint rather than inventing a
   new one; below it, items still wrap onto their own lines. */
@media (min-width: 841px) {
  .section-index { flex-wrap: nowrap; }
}
.section-index a {
  font-family: 'Courier Prime', 'Courier New', monospace;
  font-size: 0.78rem;
  letter-spacing: 0.02em;
  color: var(--bone-dim);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  padding: 0 18px 1px 0;
}
.section-index a:hover,
.section-index a:focus-visible {
  color: var(--stamp-link);
  border-bottom-color: var(--stamp-link);
}
.section-index a:not(:first-child) {
  position: relative;
  padding-left: 18px;
}
.section-index a:not(:first-child)::before {
  content: '';
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 1px;
  height: 12px;
  background: var(--ink-mid);
}
/* Offsets anchor-jump landing so the sticky site-header never covers the
   target heading — without this, jumping to #kegging etc. lands the
   block flush with the viewport top, tucked entirely under the header. */
#canning, #shots, #batching, #kegging, #compliance, #fit {
  scroll-margin-top: 90px;
}

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

.btn {
  font-family: 'Source Serif 4', serif;
  font-weight: 600;
  font-size: 0.98rem;
  padding: 14px 26px;
  border-radius: var(--radius);
  display: inline-block;
  border: 1.5px solid transparent;
  transition: transform 120ms cubic-bezier(0.16, 1, 0.3, 1), box-shadow 120ms cubic-bezier(0.16, 1, 0.3, 1), background 150ms ease;
}

.btn-primary {
  background-color: var(--stamp);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='90' height='90'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.7' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.28 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-blend-mode: multiply;
  color: var(--bone);
  box-shadow: inset 0 -3px 0 rgba(0,0,0,0.28), 0 3px 9px rgba(0,0,0,0.35);
  transform: rotate(-0.6deg);
}
.btn-primary:hover { background-color: #A82C1A; text-decoration: none; transform: rotate(-0.6deg) translateY(-1px); }
.btn-primary:active { transform: rotate(-0.6deg) translateY(2px); box-shadow: inset 0 -1px 0 rgba(0,0,0,0.28), 0 1px 4px rgba(0,0,0,0.3); }

.btn-ghost { border-color: var(--bone-dim); color: var(--bone); }
.btn-ghost:hover { background: var(--bone); color: var(--ink-deep); text-decoration: none; border-color: var(--bone); }
.btn-ghost:active { transform: translateY(2px); }

.plate .btn-ghost { border-color: var(--text-soft); color: var(--text); }
.plate .btn-ghost:hover { background: var(--text); color: var(--report); border-color: var(--text); }
/* .plate a (specificity 0,1,1) otherwise beats .btn-primary's color:var(--bone)
   (0,1,0) whenever a primary button sits inside a plate, leaving illegible
   dark stamp-deep text on the red fill — found via /impeccable critique. */
.plate .btn-primary, .plate .btn-primary:hover { color: var(--bone); }

/* ---------- Sections ---------- */

section { padding: 60px 0; }
section + section { border-top: 1px solid var(--ink-mid); }

.section-head { max-width: 60ch; margin-bottom: 34px; }
/* Home's rigor-section only, by direct /impeccable layout request: this is
   the page's one section with no photo/plate/video beside it, so its
   60ch text column left-aligned in the full 1120px .wrap left a large,
   empty gap on the right at wide viewports. Centering it (text itself
   stays left-aligned; only the block is centered) fills that gap without
   touching .section-head's shared left-aligned default on About or
   Capabilities, where it sits in normal reading-flow context. */
.rigor-section .section-head { margin-left: auto; margin-right: auto; }
h2 { font-size: 1.9rem; }
.section-head p { color: var(--bone-dim); margin-top: 14px; font-family: 'Source Serif 4', serif; font-size: 1.04rem; }
/* Deliberate exception to the One-Size Heading Rule (Capabilities'
   "Equipment & Capacity" only): this heading now scopes four equip-blocks
   (Canning, 2oz Shots, Brewing & Batching, Kegging) versus Operational
   Support's two, so it reads as the page's primary heading and Operational
   Support as secondary — the same "let real content weight break uniform
   sizing" logic already used for .equip-block--lead. Don't add a second
   .section-head-lg on this page without an equally real reason. */
.section-head-lg { font-size: 2.3rem; }

/* Closing line for Home's rigor-section: a Live-mode copy edit asked for
   the section's first and last sentences to stand out — the h2 already
   carries the first, this gives the closing line its own bold treatment
   rather than blending into the body paragraph above it. Not a heading
   (a styled <p>), so the site's uppercase-headings rule doesn't bind it.
   Fixed by a /impeccable polish pass, second round: the first fix
   (dropping text-transform: uppercase, keeping Staatliches) still failed
   the all-caps-body check on inspection — Staatliches has no true
   lowercase forms, so a ~90-char sentence set in it reads as full caps
   regardless of the text-transform property or source case. Rebuilt in
   bold Source Serif 4 instead: real lowercase, same "called-out
   statement" weight via size + color + its own line. Verified by
   screenshot, not just by the detector going quiet.
   Fixed again by a /impeccable layout pass: after removing the .plate
   wrap (see Lede Closer & Rigor Closer below), this rule's own base
   specificity (0,1,0) turned out to still lose to .section-head p
   (0,1,1) for every property they share — color, font-family, font-size,
   margin — a third instance of the exact collision already found twice
   for .plate .btn-primary and .plate .rigor-close. `.rigor-close` alone
   was never enough on its own; it needs `.section-head` in front of it
   to out-specify `.section-head p` now that no `.plate` override is
   there to (accidentally) mask the problem. */
.section-head .rigor-close {
  font-family: 'Source Serif 4', serif;
  font-weight: 700;
  font-size: 1.35rem;
  color: var(--stamp-link);
  margin-top: 18px;
  line-height: 1.4;
}
/* Signature reveal for the section's closing promise, by direct
   /impeccable delight request. motion.js now reveals this section's h2,
   body, and closer as three staggered beats instead of one block (see
   its own comment); this gives the closer specifically a heavier landing
   than the quiet fades before it — a bigger rise, a slight scale-and-
   sharpen from soft/dim to full size and full color, settled with the
   same overshoot easing already established for Capabilities' dataplate
   and tank-gauge reveals. The promise doesn't just fade in with its
   neighbors, it visibly arrives and comes into focus, matching what the
   line itself is doing rhetorically (the payoff after the build-up).
   `filter` transitions natively without an @property registration, so
   this needed no new custom-property plumbing. */
.rigor-close.will-animate {
  transform: translateY(22px) scale(0.96);
  filter: brightness(0.7) saturate(0.6) blur(1px);
  transition: opacity 0.5s cubic-bezier(0.16, 1, 0.3, 1),
              transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1),
              filter 0.6s ease-out;
}
.rigor-close.will-animate.in-view {
  transform: none;
  filter: none;
}
@media (prefers-reduced-motion: reduce) {
  .rigor-close.will-animate { transform: none; filter: none; transition: none; }
}

/* Two column prose + plate */

.split { display: grid; grid-template-columns: 1.35fr 1fr; gap: 44px; align-items: start; }
.split > div:not(.plate) > p { color: var(--bone-dim); font-size: 1.04rem; }

/* About's founder/timeline split: the timeline column runs much taller
   than the founder photo + plate beside it, which left a large gap of
   bare ink ground under the shorter column. Sticky keeps the founder
   side in view as the timeline scrolls past it instead. Scoped to the
   two-column width only — .split collapses to one column below 841px,
   where the columns stack in DOM order and sticky has nothing to do. */
@media (min-width: 841px) {
  .founder-side { position: sticky; top: 90px; }
}

/* Plate — a printed sheet resting on the ink ground */

.plate {
  background-color: var(--report);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.65' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 0.28 0 0 0 0 0.21 0 0 0 0 0.12 0 0 0 0.4 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-blend-mode: multiply;
  color: var(--text);
  padding: 26px 28px;
  border-radius: var(--radius);
  box-shadow: 0 10px 22px rgba(0,0,0,0.4), 0 2px 5px rgba(0,0,0,0.3);
  position: relative;
}
.plate::after {
  content: "";
  position: absolute;
  right: 0;
  bottom: 0;
  width: 16px;
  height: 16px;
  background: linear-gradient(135deg, transparent 50%, var(--report-dim) 50%);
  border-bottom-right-radius: var(--radius);
}
.plate h3 { font-size: 1.05rem; margin-bottom: 10px; }
.plate p { font-size: 0.98rem; color: var(--text-soft); margin: 0; font-family: 'Source Serif 4', serif; }

/* Inline spec chip — squared, replaces rounded pill tags */

.spec-chip {
  font-family: 'Courier Prime', 'Courier New', monospace;
  font-weight: 400;
  font-size: 0.74rem;
  letter-spacing: 0.02em;
  color: var(--bone);
  /* --bone-dim, not --ink-mid: a critique pass found the previous
     --ink-mid border measured ~1.35:1 against --ink, well under WCAG
     1.4.11's 3:1 non-text-contrast floor for a UI-component boundary
     (chip text itself was always legible; only the border failed). */
  border: 1px solid var(--bone-dim);
  padding: 3px 9px;
  border-radius: var(--radius);
  display: inline-block;
  vertical-align: middle;
  text-transform: uppercase;
}
.plate .spec-chip { color: var(--text-soft); border-color: var(--report-dim); }

/* ---------- Dataplate (Production Lines only) ----------
   Replaces the earlier liquid-fill Spec Gauge: a riveted equipment
   nameplate reads as "measured and certified" more directly than a
   loading-bar metaphor ever did, and it's the literal object a real
   canning line actually carries bolted to its frame. Deliberately
   exclusive to Production Lines, same as the gauge it replaces — Batching
   Capacity's Tank Compare stays its own device below. */
.dataplate {
  display: inline-flex;
  align-items: center;
  gap: 16px;
  margin-top: 12px;
  padding: 14px 20px;
  background: var(--ink-deep);
  border: 1px solid var(--ink-mid);
  border-radius: var(--radius);
  position: relative;
}
.dataplate-rivet {
  position: absolute;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--bone-dim);
  opacity: 0.5;
}
.dataplate-rivet.tl { top: 6px; left: 6px; }
.dataplate-rivet.tr { top: 6px; right: 6px; }
.dataplate-rivet.bl { bottom: 6px; left: 6px; }
.dataplate-rivet.br { bottom: 6px; right: 6px; }
.dataplate-readout {
  font-family: 'Courier Prime', 'Courier New', monospace;
  font-size: 1.7rem;
  color: var(--stamp-link);
  line-height: 1;
}
.dataplate-meta { display: flex; flex-direction: column; gap: 3px; }
.dataplate-unit {
  font-family: 'Courier Prime', 'Courier New', monospace;
  font-size: 0.72rem;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--bone-dim);
}
/* Static "confirmed" by default (matches the old readout's always-visible
   final value), only ever changing to a color cue on reveal — never the
   sole carrier of the word itself, so a visitor whose JS fails still
   reads a fully confirmed plate, just without the color change. */
.dataplate-status {
  font-family: 'Courier Prime', 'Courier New', monospace;
  font-size: 0.7rem;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--bone-dim);
}
.dataplate.in-view .dataplate-status { color: var(--stamp-link); }
@media (prefers-reduced-motion: reduce) {
  .dataplate-status { color: var(--stamp-link); }
}

/* ---------- Batch Compare (overdrive: Brewing & Batching only) ----------
   A real proportion, not a decorative bar chart: 20 bbl genuinely is
   ~14% of 140 bbl, and 312 cases is genuinely ~16% of 1,930 — this makes
   that 7x range visible at a glance instead of living only in prose.
   Two rows only, no axis or gridlines: the data is two points, and
   building out a fuller chart apparatus around it would read as a
   chart-library template dropped onto a page that otherwise has none.
   Shares the site's material language (squared ink track, --stamp fill)
   without reusing the Dataplate above — this represents a comparison
   between two real values, not a single "confirmed at rate" state, so it
   earns its own device rather than a borrowed one bent to a new meaning. */
/* One grid for both rows, not one grid per row: with each row as its own
   independent grid, the two tracks' 1fr column sized against that row's
   own "auto" readout-text width alone, and "1,930 cases" is wider than
   "312 cases" — the two tracks rendered visibly different widths. A
   single shared grid sizes both bars' columns identically across rows,
   which is the entire point of a comparison device. */
.batch-compare {
  display: grid;
  grid-template-columns: 54px 1fr auto;
  align-items: center;
  gap: 10px 12px;
  margin-top: 14px;
  max-width: 420px;
}
.batch-compare-label {
  font-family: 'Courier Prime', 'Courier New', monospace;
  font-size: 0.72rem;
  color: var(--bone-dim);
  white-space: nowrap;
}
.batch-compare-track {
  height: 8px;
  border: 1px solid var(--ink-mid);
  border-radius: var(--radius);
  background: var(--ink-deep);
  overflow: hidden;
}
/* Defaults to its real authored --pct (not 0) for the same reason every
   other motion-enhanced element on this site defaults to its finished
   state: a visitor whose JS fails still sees the correct proportion,
   just without the grow-in. */
.batch-compare-fill {
  height: 100%;
  width: var(--pct, 100%);
  background: var(--stamp);
}
.batch-compare.in-view .batch-compare-fill {
  animation: batch-compare-fill 900ms cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
}
@keyframes batch-compare-fill {
  from { width: 0; }
  to { width: var(--pct); }
}
.batch-compare-readout {
  font-family: 'Courier Prime', 'Courier New', monospace;
  font-size: 0.74rem;
  color: var(--bone-dim);
  white-space: nowrap;
  text-align: right;
}
.batch-compare-value { color: var(--stamp-link); }

/* Timeline */

.timeline { list-style: none; margin: 0; padding: 0; }
.timeline li {
  display: grid;
  grid-template-columns: 110px 1fr;
  gap: 20px;
  padding: 20px 0;
  border-top: 1px solid var(--ink-mid);
  font-family: 'Source Serif 4', serif;
  color: var(--bone-dim);
}
.timeline li:first-child { border-top: none; }
.timeline .yr {
  font-family: 'Courier Prime', 'Courier New', monospace;
  font-weight: 400;
  font-size: 0.9rem;
  color: var(--stamp-link);
  padding-top: 2px;
}
.timeline strong { color: var(--bone); }

/* Capability spec sheets */

/* Was its own equip-block (heading + bulleted list) with no supporting
   numbers — three content-free noun phrases standing next to six blocks
   of real specs read as filler. Folded to one plain sentence, same true
   facts, no longer presented as a peer section to the numbers-heavy
   blocks above it. */
.operational-support-lede { color: var(--bone-dim); font-family: 'Source Serif 4', serif; font-size: 1.04rem; max-width: 60ch; margin-bottom: 26px; }

/* A Live-mode bolder pass on Batching's description line only — bumped to
   the site's existing .lede scale (1.14rem) rather than a new size, so it
   reads with a touch more confidence without inventing a type step. */
.batching-lede { font-size: 1.14rem; }

.equip-block { padding: 0; }
.equip-block + .equip-block { margin-top: 26px; padding-top: 26px; border-top: 1px solid var(--ink-mid); }
.equip-block h3 {
  font-size: 1.15rem;
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}
/* Canning leads the page's own content, not a framework default: it's the
   line the business itself leads with, so it gets a visibly larger
   heading than its five siblings rather than identical weight for all
   six — a critique finding that the "read like a spec sheet" lede was
   undercut by every category reading as equally important. */
.equip-block--lead h3 { font-size: 1.5rem; }

/* Four content-driven groups (production lines / batching capacity /
   additional services / operational support), not a decorative kicker:
   each labels a cluster of several blocks (or, for Batching Capacity, one
   block being deliberately set apart), never a single headline, so this
   doesn't reintroduce the pre-headline eyebrow pattern this system
   otherwise bans. */
.equip-group-label {
  font-family: 'Courier Prime', 'Courier New', monospace;
  font-size: 0.72rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--bone-dim);
  margin: 0 0 18px;
}
.section-head + .equip-group-label { margin-top: 8px; }
.equip-block + .equip-group-label {
  margin-top: 40px;
  padding-top: 40px;
  border-top: 1px solid var(--ink-mid);
}
/* Production Lines is the business's real bread and butter (Canning, 2oz
   Shots) and asked to read as more prominent than its siblings. Reaching
   for the system's one accent color on the label itself — rather than a
   new box, background tint, or border treatment — keeps the emphasis in
   the existing material vocabulary instead of inventing a second device. */
.equip-group-label--featured { color: var(--stamp-link); }
.equip-list {
  margin: 16px 0 0;
  padding-left: 0;
  list-style: none;
  font-family: 'Source Serif 4', serif;
}
.equip-list--grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: 32px;
}
.equip-list li {
  font-size: 0.96rem;
  color: var(--bone-dim);
  padding: 7px 0 7px 18px;
  position: relative;
}
.plate .equip-list li { color: var(--text-soft); }
.equip-list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 14px;
  width: 9px;
  height: 1px;
  /* --stamp-link, by direct client request to make every equipment list's
     bullet ticks read the same color site-wide. This replaces the earlier
     --bone-dim default plus a separate .equip-list--accent modifier that
     recolored only Brewing & Batching/Kegging's ticks red as a deliberate
     "quieter than Production Lines' gauge, louder than plain bone" middle
     tier — that hierarchy is gone now that every list matches. */
  background: var(--stamp-link);
}
.equip-list li::after {
  content: "";
  position: absolute;
  left: 0;
  top: 11px;
  width: 1px;
  height: 7px;
  background: var(--stamp-link);
}
.plate .equip-list li::before, .plate .equip-list li::after { background: var(--text-soft); }

/* Real equipment photos — a static row, not a carousel. Everything visible
   at once, nothing to click through: a carousel's arrows/dots/autoplay
   would be the site's first real motion pattern anywhere, right where the
   brief asked for quieter, not louder. Three roughly-square-ish portrait
   crops read calmer side by side than one large image ever did. */
.equip-photo-row {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 14px;
  margin-top: 20px;
  max-width: 640px;
}
.equip-photo-row img {
  width: 100%;
  aspect-ratio: 3 / 4;
  object-fit: cover;
  border: 1px solid var(--ink-mid);
  border-radius: var(--radius);
  display: block;
  /* Real photography toned to sit quietly in a near-monochrome, one-accent
     system — desaturated and slightly dimmed so bright stainless steel
     doesn't outshout the page around it. Quiet, not grayscale. */
  filter: saturate(0.5) contrast(0.94) brightness(0.92);
}
.equip-photo-caption {
  margin-top: 8px;
  font-family: 'Courier Prime', 'Courier New', monospace;
  font-size: 0.7rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--bone-dim);
  opacity: 0.85;
}

@media (max-width: 520px) {
  .equip-photo-row { grid-template-columns: 1fr 1fr; max-width: 340px; }
  .equip-photo-row img:nth-child(3) { grid-column: 1 / 3; max-width: 50%; margin: 0 auto; }
}

/* Founder photo (About-only) — a small, undressed portrait ahead of the
   founder plate, same quiet treatment as the Capabilities shot-line
   photos: desaturated, squared, plain-bordered, never given the plate's
   paper-grain treatment even though it sits in the same column as one. */
.founder-photo-block { margin-bottom: 20px; }
.founder-photo {
  width: 160px;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  border: 1px solid var(--ink-mid);
  border-radius: var(--radius);
  display: block;
  filter: saturate(0.5) contrast(0.94) brightness(0.92);
}
.founder-photo-caption {
  margin-top: 8px;
  font-family: 'Courier Prime', 'Courier New', monospace;
  font-size: 0.7rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--bone-dim);
  opacity: 0.85;
}

/* Certification list — plain bordered chips (see .cert-list below), used
   site-wide for compliance/certification facts. The circular ink-stamp
   device this replaced (.stamp/.stamp-sheet) reintroduced the "verdict
   chrome" the client rejected for Home; retired everywhere as of the
   4-page critique pass. */

/* Values row */

.values-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: 26px; }
.value-plate.plate { padding: 24px 24px; }
.value-plate h3 { font-size: 1.05rem; margin-bottom: 8px; text-transform: none; }
.value-plate p { color: var(--text-soft); font-size: 0.98rem; margin: 0; }

/* ---------- Scroll reveal (site's first authored motion, About-only use
   so far) ----------
   Armed by motion.js's armAndObserve(): the .will-animate class (and its
   opacity:0 starting state) is only ever added by script, so content
   stays fully visible by default if JS is disabled or fails — this rule
   can never hide anything on its own. Deliberately even and quiet: no
   single focal moment, the same small fade-and-rise on every section as
   it scrolls into view, matching the client's repeated preference for
   plain and restrained over theatrical. */
.will-animate {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 0.5s cubic-bezier(0.16, 1, 0.3, 1),
              transform 0.5s cubic-bezier(0.16, 1, 0.3, 1);
}
.will-animate.in-view { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .will-animate { opacity: 1; transform: none; transition: none; }
}

/* ---------- Hero photo (homepage hero device) ----------
   Replaces the earlier .capability-plate spec-list card — real facility
   photography now sits beside the hero headline instead of a text preview
   of specs that live in full on Capabilities. No .plate/paper-grain
   treatment: real photography doesn't get document chrome on this site
   (see the Founder Photo and Equipment Photo Row components), so this is
   a plain squared frame, not a card. */
.hero-photo {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border: 1px solid var(--ink-mid);
  border-radius: var(--radius);
  /* Same desaturation as every other real photo on the site — bright
     stainless steel and warehouse lighting would otherwise outshout the
     near-monochrome, one-accent palette. */
  filter: saturate(0.5) contrast(0.94) brightness(0.92);
}
.hero-photo-caption {
  margin-top: 10px;
  font-family: 'Courier Prime', 'Courier New', monospace;
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--bone-dim);
  opacity: 0.85;
}

/* ---------- Hero Can Stage (Home hero, /impeccable overdrive) ----------
   A real WebGL scene (Three.js) replacing the hero photo when the browser
   supports it: a can idea-sketch wireframes in, engineers itself into
   aluminum while rotating, fills with a real beverage and bubbles, gets
   its lid, and takes the Auroch mark — then loops. See hero-can.js for
   the full build; this file only owns the stage frame and the 2D
   technical-drawing overlay (dimension lines, centerlines, callouts) that
   sits over the canvas during Phase 1.
   Progressive enhancement, not a replacement: `hidden` by default, real
   photo stays the default DOM state, hero-can.js only reveals this and
   hides the photo after confirming WebGL support AND that the visitor
   hasn't asked for reduced motion — a visitor on either path sees a
   complete, intentional hero, never a broken one. Same 4:3 frame and
   squared bordered treatment as the photo it can replace, so the layout
   never shifts between the two states. */
.hero-can-stage {
  position: relative;
  width: 100%;
  aspect-ratio: 4 / 3;
  border: 1px solid var(--ink-mid);
  border-radius: var(--radius);
  background: var(--ink-deep);
  overflow: hidden;
}
.hero-can-stage canvas { display: block; width: 100%; height: 100%; }
.hero-can-annotations {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}
.hero-can-annotations line,
.hero-can-annotations path {
  stroke: var(--stamp-link);
  stroke-width: 1;
  fill: none;
  vector-effect: non-scaling-stroke;
}
.hero-can-annotations circle {
  fill: var(--stamp-link);
}
.hero-can-annotations text {
  fill: var(--stamp-link);
  font-family: 'Courier Prime', 'Courier New', monospace;
  font-size: 9px;
  letter-spacing: 0.02em;
}
.hca-centerlines line { stroke-dasharray: 6 3 1 3; opacity: 0.5; }
/* All four annotation groups default hidden; hero-can.js sets
   data-phase="1" on the stage only while the wireframe idea-sketch is on
   screen, matching the brief's own "technical drawing" phase — they fade
   out once the can starts rotating into engineering, same as a real CAD
   viewport clearing its markup once you start moving the model. */
.hca-centerlines, .hca-dims, .hca-callouts, .hca-coord {
  opacity: 0;
  transition: opacity 0.5s ease;
}
.hero-can-stage[data-phase="1"] .hca-centerlines,
.hero-can-stage[data-phase="1"] .hca-dims,
.hero-can-stage[data-phase="1"] .hca-callouts,
.hero-can-stage[data-phase="1"] .hca-coord {
  opacity: 1;
}

/* Live spec HUD — unlike the four annotation groups above, this is not
   Phase-1-only: it tracks the animation the way a real telemetry/status
   readout would, so it stays on screen the whole loop and only its
   Status value changes (schematic -> forming -> filling -> sealed ->
   branded), written by hero-can.js. Styled as the site's own .spec-chip
   convention (Courier Prime, uppercase, bordered) rather than a new
   device, just composited over the dark canvas instead of report paper. */
.hca-hud {
  position: absolute;
  left: 10px;
  top: 10px;
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 6px 9px;
  background: rgba(10, 10, 10, 0.72);
  border: 1px solid var(--ink-mid);
  border-radius: var(--radius);
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.6s ease;
}
.hero-can-stage:not([hidden]) .hca-hud {
  opacity: 1;
}
.hca-hud-row {
  display: flex;
  gap: 8px;
  font-family: 'Courier Prime', 'Courier New', monospace;
  font-size: 0.62rem;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  white-space: nowrap;
}
.hca-hud-label { color: var(--bone-dim); }
.hca-hud-value { color: var(--stamp-link); min-width: 5.6em; }

/* Certification list (homepage compliance preview) — plain bordered chips,
   not the circular ink-stamp device (kept on services.html only). */

.cert-list {
  display: flex;
  flex-wrap: wrap;
  gap: 12px 14px;
  margin-top: 18px;
  /* A real <ul>/<li> now, not bare spans in a div — a screen reader was
     announcing all six compliance facts as one run-on string. */
  padding: 0;
  list-style: none;
}

/* ---------- Exhibit (facility photo/video slot) ----------
   A full-bleed media strip for real facility footage, framed like an
   attached exhibit sheet (hairline top/bottom border) rather than a
   generic gray placeholder box. Previously held a capacity-growth chart
   (25→50→100 CPM) as real, sourced data standing in for footage that
   didn't exist yet; replaced by direct client request once real canning-
   line video was supplied. See the video's own note below for why the
   aspect ratio moved off the originally-planned 21/9. */

.exhibit { padding: 0; }

.exhibit-media {
  width: 100%;
  /* 2/1, thinned down by direct request from the original 16/9 — the video
     asset itself is still delivered at 16/9 (see Canning Line Video), this
     just crops it shorter so the full-bleed strip takes up less vertical
     space on the page. Only trims ~11% off the top/bottom evenly; the
     receding line of cans still reads fine cropped this much. Don't go
     much thinner than this without rechecking the composition — the
     source was never planned to be a razor-thin banner (that's why 21/9
     was rejected outright; this is a much smaller trim by comparison). */
  aspect-ratio: 2 / 1;
  border-top: 1px solid var(--ink-mid);
  border-bottom: 1px solid var(--ink-mid);
  background: var(--ink-deep);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
.exhibit-media video, .exhibit-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
/* Same desaturation treatment as every other real photo/video on the
   site — bright brushed aluminum cans under warehouse fluorescents would
   otherwise outshout the near-monochrome, one-accent palette. */
.exhibit-video {
  filter: saturate(0.5) contrast(0.94) brightness(0.92);
}

/* Story photo (About-only) — a single real photo used as a mid-page
   rhythm break between two dense text sections. Deliberately NOT run
   through .exhibit's full-bleed treatment: that pattern is tuned for
   genuinely wide/cinematic content (facility drone footage at 21/9), and
   blowing a near-square product photo up to full viewport width at its
   own aspect ratio makes it roughly viewport-height tall on a wide
   desktop — oversized and out of place next to the page's normal
   1120px-capped content. Contained and centered instead, sized like a
   single .split column, so it reads as a moment on the page rather than
   a hero banner. Reuses .exhibit-caption below it for the caption type. */
.story-photo-section { padding: 40px 0; }
.story-photo {
  display: block;
  width: 100%;
  max-width: 560px;
  aspect-ratio: 5 / 4;
  object-fit: cover;
  margin: 0 auto;
  border: 1px solid var(--ink-mid);
  border-radius: var(--radius);
}

.exhibit-caption { padding: 22px 28px; }
.exhibit-caption p {
  max-width: 68ch;
  margin: 0 auto;
  text-align: center;
  font-family: 'Source Serif 4', serif;
  font-size: 0.98rem;
  color: var(--bone-dim);
}

@media (max-width: 640px) {
  /* 1/1, thinned down from 4/5 for the same "less vertical space" request
     as desktop's 2/1. Still narrower than 16/9 on mobile, so the crop
     stays horizontal-only (object-fit: cover trims left/right from the
     16:9 source into this taller-than-wide box, never top/bottom) — a
     tighter side-to-side field of view, not a shorter clip. */
  .exhibit-media { aspect-ratio: 1 / 1; }
}

/* ---------- Contact ---------- */

/* Standard visually-hidden pattern: present to assistive tech and the
   document outline, removed from visual flow without display:none
   (which screen readers also skip). Fixed by a /impeccable typeset
   pass — Contact's hero h1 was followed directly by the form plate's
   h3 with no h2 between them, a skipped heading level. A visible h2
   here would be redundant on a two-card page whose job is already
   obvious from the h1 and the form's own heading; this restores the
   correct h1→h2→h3 outline without adding visual clutter. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* align-items: start, not the grid default (stretch): the two cards
   have very different content lengths (a long multi-field form vs. a
   short "email us directly" card), and stretching the shorter one to
   match left a few hundred pixels of bare kraft paper below its content
   — the single biggest thing making this page read as unfinished. */
.contact-grid { display: grid; grid-template-columns: 1fr 1.05fr; gap: 40px; align-items: start; }

.contact-grid .plate { padding: 30px 30px 26px; }

/* Fixed by a critique pass: none of the 9 fields distinguished required
   from optional, so a visitor had to trial-submit to learn the form's
   real scope. A short legend plus an asterisk on the 4 required labels
   is the standard, least noisy pattern for a form this size (fewer
   markers than tagging all 5 optional fields individually). */
.form-required-note { margin: -8px 0 18px; font-size: 0.85rem; color: var(--text-soft); font-family: 'Source Serif 4', serif; }
.field-required { color: var(--stamp-deep); margin-left: 2px; }

.field { margin-bottom: 20px; }
/* Fixed by a /impeccable typeset pass: no text-transform: uppercase.
   Short labels (Company, Email) read fine shouting; the longer ones here
   are full questions ("Does this product require pasteurization?") and
   all-caps removes the word-shape cues (ascenders/descenders) that make
   longer text scannable — a real reading regression on a form whose only
   job is being filled out quickly and correctly. Kept as one uniform
   rule rather than a length-based exception: a label role that's
   sometimes shouting and sometimes not would be a worse inconsistency
   than just not shouting at all. Courier Prime mono still carries the
   "data label" identity on its own; it doesn't need the case too. */
.field label {
  display: block;
  font-family: 'Courier Prime', 'Courier New', monospace;
  font-weight: 400;
  font-size: 0.74rem;
  line-height: 1.3;
  letter-spacing: 0.03em;
  color: var(--text-soft);
  margin-bottom: 7px;
}
/* Paired fields in a .field-row wrap to a different number of lines
   depending on label length ("Company" vs. "Does this product require
   pasteurization?"), which was pushing their inputs out of alignment
   with each other by ~19px. Reserving room for 2 lines on every
   field-row label, regardless of whether it actually wraps, keeps every
   paired input starting on the same baseline. */
.field-row .field label { min-height: 2rem; }
.field input, .field select, .field textarea {
  width: 100%;
  padding: 11px 12px;
  border: 1px solid var(--report-dim);
  border-bottom: 2px solid var(--text-soft);
  border-radius: var(--radius);
  background: #fff;
  font-family: 'Source Serif 4', serif;
  font-size: 1rem;
  color: var(--text);
}
.field input.invalid, .field select.invalid, .field textarea.invalid { border-bottom-color: var(--stamp-deep); }
.field textarea { min-height: 110px; resize: vertical; }
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.field-error {
  display: none;
  margin-top: 6px;
  font-size: 0.82rem;
  color: var(--stamp-deep);
  font-family: 'Source Serif 4', serif;
}
.field.has-error .field-error { display: block; }

.submit-row { display: flex; align-items: center; gap: 16px; margin-top: 8px; flex-wrap: wrap; }

.contact-direct h3 { font-size: 1.1rem; margin-bottom: 10px; text-transform: none; }
.contact-direct .email { font-family: 'Staatliches', serif; font-weight: 400; font-size: 1.3rem; text-transform: none; }
.contact-direct p { font-family: 'Source Serif 4', serif; }
/* Fixed by a /impeccable typeset pass: was an inline font-size:0.95rem,
   an arbitrary one-off value not matching any documented step. This
   note plays the same supporting-copy role as .form-hint/
   .form-required-note beside it on the page, so it now shares their
   0.85rem rather than inventing its own nearby-but-different size. */
.contact-direct p + p { font-size: 0.85rem; }

.form-hint { margin-top: 10px; font-size: 0.85rem; color: var(--text-soft); font-family: 'Source Serif 4', serif; }
.form-hint a { color: var(--stamp-deep); }

.form-status { margin-top: 16px; font-size: 0.92rem; display: none; font-family: 'Source Serif 4', serif; padding-top: 12px; border-top: 1px solid var(--report-dim); }
/* #2E6B3E measured ~4.56:1 on --report — passes AA but with almost no
   headroom for status text whose whole job is being read correctly.
   Darkened; still unambiguously green, now ~7.8:1. */
.form-status.ok { color: #1D4526; display: block; }
.form-status.err { color: var(--stamp-deep); display: block; }

/* ---------- Footer / title block ---------- */

.site-footer {
  background: var(--ink-deep);
  border-top: 1px solid var(--ink-mid);
  padding: 0;
}

.footer-nav {
  border-bottom: 1px solid var(--ink-mid);
  padding: 10px 0;
}
.footer-nav ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 4px 20px;
  font-family: 'Source Serif 4', serif;
  font-weight: 600;
  font-size: 0.97rem;
}
.footer-nav a { color: var(--bone-dim); display: inline-flex; align-items: center; min-height: 44px; padding: 0 8px; }
.footer-nav a[aria-current="page"] { color: var(--stamp-link); }

/* Fixed by a critique pass: .title-block-meta (a "Drawn / Sheet X of 4 /
   ©" row styled like a technical drawing's title-block stamp) was a
   lingering blueprint/drafting-table device this design system otherwise
   explicitly refuses — removed site-wide. .title-block-id (company name,
   location, mailto) is ordinary footer content, kept as-is; .title-block
   no longer needs to be a two-column grid now that it holds one child. */
.title-block { padding-top: 26px; padding-bottom: 30px; }

.title-block-id {
  font-family: 'Source Serif 4', serif;
  font-size: 0.92rem;
  color: var(--bone-dim);
}
.title-block-id b { color: var(--bone); display: block; font-family: 'Staatliches', sans-serif; font-size: 1.15rem; margin-bottom: 4px; letter-spacing: 0.02em; }
.title-block-id a { color: var(--stamp-link); }
/* Plain copyright line, added back after .title-block-meta's removal —
   deliberately not the removed block's mono/bordered-row treatment,
   just a small, quieter line under the address. */
.title-block-copyright { margin: 6px 0 0; font-size: 0.8rem; opacity: 0.8; }

/* ---------- Responsive ---------- */

@media (max-width: 840px) {
  .split, .contact-grid, .hero:not(.hero--inner) .hero-inner { grid-template-columns: 1fr; }
  /* Fixed by a critique pass: .contact-direct is the second grid child,
     so once the grid collapses to one column it rendered after the
     entire 9-field form — the lowest-commitment escape hatch hidden
     behind the highest-commitment path. Desktop is unaffected (both
     cards are already visible side by side there, so order is moot). */
  .contact-direct { order: -1; }
  .values-row { grid-template-columns: 1fr; }
  .equip-list--grid { grid-template-columns: 1fr; }
  .field-row { grid-template-columns: 1fr 1fr; }

  .nav-toggle { display: block; }
  /* Visible-by-default, JS collapses (not the reverse): if nav.js fails to
     load or errors, mobile visitors still get the full nav, just always
     expanded under the header, instead of a permanently hidden dead end.
     nav.js adds .collapsed on load as the enhanced/closed starting state,
     then toggles it on click — the hamburger button itself stays visible
     either way, but only becomes interactive once JS attaches it. */
  .nav-links {
    display: flex;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    background: var(--ink-deep);
    border-top: 1px solid var(--ink-mid);
    border-bottom: 1px solid var(--ink-mid);
    flex-direction: column;
    gap: 0;
    padding: 6px 0;
    transform-origin: top;
    transform: scaleY(1) translateY(0);
    opacity: 1;
    visibility: visible;
    transition: transform 240ms cubic-bezier(0.16, 1, 0.3, 1), opacity 180ms ease;
  }
  .nav-links.collapsed {
    transform: scaleY(0.9) translateY(-4px);
    opacity: 0;
    visibility: hidden;
    transition: transform 240ms cubic-bezier(0.16, 1, 0.3, 1), opacity 180ms ease, visibility 0s linear 240ms;
  }
  .nav-links a {
    padding: 13px 28px;
    display: block;
    border-top: 1px solid var(--ink-mid);
  }
  .nav-links a:first-child { border-top: none; }
  .brand .loc { display: none; }
  .hero-mark--small { opacity: 0.16; }
  .nav-cta { padding: 10px 14px; }
  .nav-cta .cta-full { display: none; }
  .nav-cta .cta-short { display: inline; }
}

@media (max-width: 520px) {
  .field-row { grid-template-columns: 1fr; }

  /* Timeline never got a breakpoint of its own — at narrow phone widths
     the fixed 110px year column left only ~190px for entry text, wrapping
     every line into 5-7 short lines on the page's strongest asset. Stack
     year above text instead, matching how .split/.contact-grid collapse. */
  .timeline li { grid-template-columns: 1fr; gap: 4px; }
  .timeline .yr { padding-top: 0; }
}

/* ---------- Motion ----------
   No authored signature motion at this pass on Home/Capabilities/Contact
   (the compliance-stamp press-in this section used to describe was
   retired with .stamp/.stamp-sheet; About separately carries the site's
   first scroll-reveal motion, see its own will-animate rules). Buttons
   and nav get small, real state feedback, not decoration. Genuinely true
   now (a critique finding caught this comment overclaiming it earlier):
   .nav-links defaults to visible on every viewport, JS only adds
   .collapsed as an enhancement — nothing on the site depends on JS to
   become visible, a failed/blocked script can only ever fail to collapse
   the mobile nav, never hide it. */

@media (prefers-reduced-motion: reduce) {
  .nav-links {
    transform: none;
    transition: opacity 150ms ease;
  }
  .nav-links.collapsed {
    transition: opacity 150ms ease, visibility 0s linear 150ms;
  }
}
