/* ==========================================================================
   Dad Made — Editorial Luxury
   Warm cream canvas · variable serif display · nested hardware enclosures
   ========================================================================== */

/* --------------------------------------------------------------------------
   Fonts — self-hosted
   Three variable files, latin subset, covering every face the system renders.
   Served from this origin, so first paint costs no DNS lookup, no TLS
   handshake and no render-blocking round-trip to a third party — and no
   visitor's IP reaches one either, which is what /privacy/ already promises.
   Newsreader and Hanken Grotesk are both SIL OFL 1.1; see assets/fonts/OFL.txt.
   -------------------------------------------------------------------------- */

@font-face {
  font-family: "Newsreader";
  src: url("assets/fonts/newsreader-roman-var.woff2") format("woff2-variations"),
    url("assets/fonts/newsreader-roman-var.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Newsreader";
  src: url("assets/fonts/newsreader-italic-var.woff2") format("woff2-variations"),
    url("assets/fonts/newsreader-italic-var.woff2") format("woff2");
  font-weight: 300 400;
  font-style: italic;
  font-display: swap;
}

@font-face {
  font-family: "Hanken Grotesk";
  src: url("assets/fonts/hanken-grotesk-var.woff2") format("woff2-variations"),
    url("assets/fonts/hanken-grotesk-var.woff2") format("woff2");
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
}

/* --------------------------------------------------------------------------
   Metric-matched fallbacks
   Measured against the real faces with canvas TextMetrics rather than
   estimated: Georgia sits 3.83% narrower than Newsreader, Arial 0.68%
   narrower than Hanken Grotesk. Overriding the vertical metrics too means
   the swap when the webfonts arrive changes the letterforms without moving
   a single line — no reflow, no layout shift, on a page whose fonts come
   from a third party.
   -------------------------------------------------------------------------- */

@font-face {
  font-family: "Newsreader Fallback";
  src: local("Georgia"), local("Times New Roman");
  size-adjust: 103.83%;
  ascent-override: 70.79%;
  descent-override: 25.52%;
  line-gap-override: 0%;
}

@font-face {
  font-family: "Hanken Fallback";
  src: local("Arial"), local("Helvetica");
  size-adjust: 100.68%;
  ascent-override: 99.32%;
  descent-override: 30.29%;
  line-gap-override: 0%;
}

:root {
  color-scheme: light;

  /* --- Ground ------------------------------------------------------- */
  --cream: #fdfbf7;
  --cream-sunk: #f6f1e6;
  --linen: #ffffff;

  /* --- Ink ---------------------------------------------------------- */
  --espresso: #17150f;
  --ink: #1c1c15;
  --ink-soft: #59564c;
  --ink-faint: #6f6b61;

  /* --- Structure ---------------------------------------------------- */
  --navy: #1b2a4a;
  --navy-ground: #1b2a4a;
  --caramel: #975f13;
  --slate: #8ea9c7;

  /* --- On navy ------------------------------------------------------ */
  /* The rundown panel is a navy fill in both themes. --navy remaps to a
     light accent so chips and buttons on paper stay visible; these do not. */
  --on-navy: #fdfbf7;
  --on-navy-soft: rgba(253, 251, 247, 0.72);
  --on-navy-faint: rgba(253, 251, 247, 0.62);

  /* Lit cream that never follows the theme: tags on photographs, product
     sweep fallbacks, and the cream pill sitting on the rundown panel. */
  --paper: #fdfbf7;
  --paper-ink: #17150f;

  /* --- State (rundown panel only) ----------------------------------- */
  --state-error: #ffb4ab;
  --state-error-field: #ffc9c2;
  --state-success: #9fd8b4;

  /* --- Enclosures --------------------------------------------------- */
  --shell: rgba(23, 21, 15, 0.045);
  --shell-ring: rgba(23, 21, 15, 0.08);
  --core: var(--linen);
  --core-inset: inset 0 1px 1px rgba(255, 255, 255, 0.9);
  --ambient: 0 32px 64px -40px rgba(23, 21, 15, 0.35),
    0 8px 20px -16px rgba(23, 21, 15, 0.2);
  --ambient-lift: 0 44px 88px -44px rgba(23, 21, 15, 0.42),
    0 12px 28px -18px rgba(23, 21, 15, 0.24);
  --hairline: rgba(23, 21, 15, 0.09);

  --glass: rgba(253, 251, 247, 0.72);
  --glass-ring: rgba(23, 21, 15, 0.08);

  /* --- Type --------------------------------------------------------- */
  --serif: "Newsreader", "Newsreader Fallback", "Iowan Old Style", Georgia, serif;
  --grotesk: "Hanken Grotesk", "Hanken Fallback", ui-sans-serif, system-ui, sans-serif;

  /* Tracking by job, not by eye. Every tracked-caps label in the system is
     11px, so the only thing that can distinguish them is their job: words
     read at one value, number-dense metadata a step tighter because heavy
     tracking pulls numerals apart, and column heads a step looser. */
  --track-label: 0.16em;
  --track-data: 0.12em;
  --track-head: 0.2em;

  /* Optical compensation, light theme = the reference. See the dark block. */
  --track-display: -0.025em;
  --track-headline: -0.02em;
  --lead-prose: 1.8;

  /* --- Radii (concentric) ------------------------------------------- */
  --bezel-pad: 0.375rem;
  --r-shell: 2rem;
  --r-core: calc(var(--r-shell) - var(--bezel-pad));
  --r-shell-lg: 2.75rem;
  --r-core-lg: calc(var(--r-shell-lg) - var(--bezel-pad));
  --r-shell-sm: 1.125rem;
  --r-core-sm: calc(var(--r-shell-sm) - var(--bezel-pad));

  /* --- Motion ------------------------------------------------------- */
  --fluid: cubic-bezier(0.32, 0.72, 0, 1);
  --t-fast: 320ms;
  --t-base: 560ms;
  --t-slow: 880ms;

  /* --- Rhythm ------------------------------------------------------- */
  --gutter: 1.25rem;
  --shell-w: 1440px;
  --bay: 7rem;
}

@media (min-width: 768px) {
  :root {
    --gutter: 2.5rem;
    --bay: 10rem;
  }
}

@media (min-width: 1280px) {
  :root {
    --gutter: 4rem;
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;

  --cream: #12110d;
  --cream-sunk: #0d0c09;
  --linen: #1c1a15;

  --espresso: #f4f0e6;
  --ink: #f0ece2;
  --ink-soft: #a8a396;
  --ink-faint: #8b877b;

  --navy: #c9d6f5;
  --navy-ground: #101c34;
  --caramel: #e0a94c;
  --slate: #9dbdd6;

  --on-navy: #f0ece2;
  --on-navy-soft: rgba(240, 236, 226, 0.72);
  --on-navy-faint: rgba(240, 236, 226, 0.62);

  --shell: rgba(255, 255, 255, 0.045);
  --shell-ring: rgba(255, 255, 255, 0.09);
  --core: #1c1a15;
  --core-inset: inset 0 1px 1px rgba(255, 255, 255, 0.07);
  --ambient: 0 32px 64px -40px rgba(0, 0, 0, 0.8),
    0 8px 20px -16px rgba(0, 0, 0, 0.6);
  --ambient-lift: 0 44px 88px -44px rgba(0, 0, 0, 0.9),
    0 12px 28px -18px rgba(0, 0, 0, 0.7);
  --hairline: rgba(255, 255, 255, 0.1);

  --glass: rgba(18, 17, 13, 0.72);
  --glass-ring: rgba(255, 255, 255, 0.1);

  /* Light type on a dark ground blooms: strokes spread, tight tracking closes
     up, and a long measure gets harder to track line to line. Same face, same
     sizes — only the optics are corrected. Relaxing display tracking by
     0.005em and opening prose leading by 0.05 is the whole adjustment; going
     further starts to read as a different typeface. */
  --track-display: -0.02em;
  --track-headline: -0.015em;
  --lead-prose: 1.85;
}

/* --------------------------------------------------------------------------
   Base
   -------------------------------------------------------------------------- */

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

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

body {
  margin: 0;
  background: var(--cream);
  color: var(--ink);
  font-family: var(--grotesk);
  font-size: 1rem;
  line-height: 1.6;
  font-feature-settings: "kern", "liga";
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
  touch-action: manipulation;
  -webkit-tap-highlight-color: rgba(27, 42, 74, 0.1);
}

body[data-scroll-locked] {
  overflow: hidden;
}

::selection {
  background: var(--caramel);
  color: var(--cream);
}

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

/* The AVIF sources are wrapped in <picture>, which is an inline box by
   default and would sit between the img and every layout rule written for it.
   `contents` removes the box and leaves the img as the direct child it was. */
picture {
  display: contents;
}

h1,
h2,
h3,
h4 {
  margin: 0;
  font-weight: 400;
  text-wrap: balance;
}

p {
  margin: 0;
  text-wrap: pretty;
}

a {
  color: inherit;
  text-decoration: none;
}

button {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}

input {
  font: inherit;
  color: inherit;
}

:where(a, button, input, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--caramel);
  outline-offset: 4px;
  border-radius: 4px;
}

[hidden] {
  display: none !important;
}

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

/* Film grain — fixed, non-interactive, never attached to scrolling content. */
.grain {
  position: fixed;
  inset: 0;
  z-index: 60;
  pointer-events: none;
  opacity: 0.035;
  mix-blend-mode: multiply;
  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.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}

:root[data-theme="dark"] .grain {
  mix-blend-mode: screen;
  opacity: 0.045;
}

/* --------------------------------------------------------------------------
   Layout primitives
   -------------------------------------------------------------------------- */

.bay {
  max-width: var(--shell-w);
  margin-inline: auto;
  /* max(), not +: in landscape on a notched phone the inset can exceed the
     gutter, and the gutter is already generous everywhere else. */
  padding-inline: max(var(--gutter), env(safe-area-inset-left))
    max(var(--gutter), env(safe-area-inset-right));
}

.section {
  padding-block: var(--bay);
  scroll-margin-top: 7rem;
}

.section--sunk {
  background: var(--cream-sunk);
}


/* --- Eyebrow tag ---------------------------------------------------- */



/* --- Display type --------------------------------------------------- */

.display {
  font-family: var(--serif);
  font-optical-sizing: auto;
  font-weight: 400;
  letter-spacing: var(--track-display);
  line-height: 0.94;
  font-size: clamp(3.25rem, 1.6rem + 6.8vw, 8rem);
}

.display em {
  font-style: italic;
  font-weight: 300;
}

.headline {
  font-family: var(--serif);
  font-weight: 400;
  letter-spacing: var(--track-headline);
  line-height: 1.02;
  font-size: clamp(2.5rem, 1.5rem + 3.8vw, 4.5rem);
}

.lede {
  font-family: var(--serif);
  font-style: italic;
  font-weight: 300;
  font-size: clamp(1.125rem, 1rem + 0.6vw, 1.4rem);
  line-height: 1.55;
  color: var(--ink-soft);
  max-width: 34ch;
}

/* --------------------------------------------------------------------------
   Double-bezel enclosure
   -------------------------------------------------------------------------- */

.bezel {
  padding: var(--bezel-pad);
  border-radius: var(--r-shell);
  background: var(--shell);
  box-shadow: inset 0 0 0 1px var(--shell-ring);
}

.bezel--lg {
  border-radius: var(--r-shell-lg);
}

.bezel__core {
  position: relative;
  overflow: hidden;
  border-radius: var(--r-core);
  background: var(--core);
  box-shadow: var(--core-inset), var(--ambient);
  transition: box-shadow var(--t-base) var(--fluid),
    transform var(--t-base) var(--fluid);
}

.bezel--lg .bezel__core {
  border-radius: var(--r-core-lg);
}

/* --------------------------------------------------------------------------
   Fluid island navigation
   -------------------------------------------------------------------------- */

.dock {
  position: sticky;
  top: calc(1.25rem + env(safe-area-inset-top));
  /* Above .veil so the morphed X stays visible and reachable while the
     overlay is open. */
  z-index: 52;
  width: max-content;
  max-width: calc(100% - 2rem);
  margin: calc(1.25rem + env(safe-area-inset-top)) auto 0;
  padding: 0.4rem;
  border-radius: 999px;
  background: var(--glass);
  backdrop-filter: blur(24px) saturate(180%);
  -webkit-backdrop-filter: blur(24px) saturate(180%);
  box-shadow: inset 0 0 0 1px var(--glass-ring), var(--ambient);
  transition: transform var(--t-base) var(--fluid),
    box-shadow var(--t-base) var(--fluid);
}

.dock__row {
  display: flex;
  align-items: center;
  gap: 0.25rem;
}

.dock__brand {
  display: flex;
  align-items: center;
  padding-inline: 0.9rem 0.5rem;
}

.dock__brand img {
  width: 116px;
  height: auto;
}

.dock__links {
  display: none;
  align-items: center;
  gap: 0.125rem;
}

@media (min-width: 1024px) {
  .dock__links {
    display: flex;
  }
}

.dock__link {
  position: relative;
  padding: 0.55rem 0.9rem;
  border-radius: 999px;
  font-size: 0.8125rem;
  font-weight: 500;
  letter-spacing: 0.01em;
  color: var(--ink-soft);
  transition: color var(--t-fast) var(--fluid),
    background-color var(--t-fast) var(--fluid);
}

.dock__link:hover {
  color: var(--ink);
  background: var(--shell);
}

/* Five labels appear the instant the burger leaves. At this width the island
   still fits (≈38rem), but the base 0.9rem padding packs the row. Tighten
   until the viewport can spare the full padding. After the base rule so
   the shorthand does not win. */
@media (min-width: 1024px) and (max-width: 1199px) {
  .dock__brand {
    padding-inline: 0.7rem 0.35rem;
  }

  .dock__link {
    padding-inline: 0.65rem;
  }
}

.dock__tail {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  padding-left: 0.25rem;
}

.dock__divider {
  width: 1px;
  height: 1.4rem;
  background: var(--hairline);
  margin-inline: 0.35rem;
}

/* Icon control — hairline glyphs, circular target */
.ctl {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  width: 2.375rem;
  height: 2.375rem;
  border-radius: 999px;
  color: var(--ink-soft);
  transition: color var(--t-fast) var(--fluid),
    background-color var(--t-fast) var(--fluid),
    transform var(--t-fast) var(--fluid);
}

.ctl:hover {
  color: var(--ink);
  background: var(--shell);
}

.ctl:active {
  transform: scale(0.94);
}

.ctl svg {
  width: 1.125rem;
  height: 1.125rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.15;
  stroke-linecap: round;
  stroke-linejoin: round;
}


/* --- Hamburger morph ------------------------------------------------ */

.burger {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.375rem;
  height: 2.375rem;
  border-radius: 999px;
  color: var(--ink);
  transition: background-color var(--t-fast) var(--fluid);
}

@media (min-width: 1024px) {
  .burger {
    display: none;
  }
}

.burger:hover {
  background: var(--shell);
}

.burger__bar {
  position: absolute;
  left: 50%;
  width: 1.05rem;
  height: 1.25px;
  border-radius: 2px;
  background: currentColor;
  transition: transform var(--t-base) var(--fluid);
}

.burger:hover .burger__bar,
.burger[aria-expanded="true"] .burger__bar {
  will-change: transform;
}

.burger__bar:nth-child(1) {
  transform: translate(-50%, -4px);
}

.burger__bar:nth-child(2) {
  transform: translate(-50%, 4px);
}

.burger[aria-expanded="true"] .burger__bar:nth-child(1) {
  transform: translate(-50%, 0) rotate(45deg);
}

.burger[aria-expanded="true"] .burger__bar:nth-child(2) {
  transform: translate(-50%, 0) rotate(-45deg);
}

/* --- Full-bleed menu overlay ---------------------------------------- */

.veil {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: calc(7.5rem + env(safe-area-inset-top)) var(--gutter)
    calc(3rem + env(safe-area-inset-bottom));
  background: var(--glass);
  backdrop-filter: blur(48px) saturate(160%);
  -webkit-backdrop-filter: blur(48px) saturate(160%);
  overscroll-behavior: contain;
  overflow-y: auto;
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--t-base) var(--fluid),
    visibility 0s var(--fluid) var(--t-base);
}

.veil[data-open] {
  opacity: 1;
  visibility: visible;
  transition: opacity var(--t-base) var(--fluid), visibility 0s;
}

.veil__nav {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  max-width: var(--shell-w);
  width: 100%;
  margin-inline: auto;
}

.veil__item {
  overflow: hidden;
}

.veil__link {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  padding-block: 0.5rem;
  font-family: var(--serif);
  font-size: clamp(2.25rem, 1.2rem + 4.6vw, 4rem);
  line-height: 1.1;
  letter-spacing: var(--track-headline);
  transform: translateY(110%);
  opacity: 0;
  transition: transform var(--t-slow) var(--fluid),
    opacity var(--t-slow) var(--fluid), color var(--t-fast) var(--fluid);
}

.veil[data-open] .veil__link {
  transform: translateY(0);
  opacity: 1;
}

.veil__item:nth-child(1) .veil__link { transition-delay: 60ms; }
.veil__item:nth-child(2) .veil__link { transition-delay: 110ms; }
.veil__item:nth-child(3) .veil__link { transition-delay: 160ms; }
.veil__item:nth-child(4) .veil__link { transition-delay: 210ms; }
.veil__item:nth-child(5) .veil__link { transition-delay: 260ms; }
.veil__item:nth-child(6) .veil__link { transition-delay: 310ms; }

.veil__link:hover {
  color: var(--caramel);
}


.veil__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  max-width: var(--shell-w);
  width: 100%;
  margin: 3rem auto 0;
  padding-top: 2rem;
  border-top: 1px solid var(--hairline);
  opacity: 0;
  transform: translateY(1.5rem);
  transition: opacity var(--t-slow) var(--fluid) 340ms,
    transform var(--t-slow) var(--fluid) 340ms;
}

.veil[data-open] .veil__foot {
  opacity: 1;
  transform: none;
}

/* --------------------------------------------------------------------------
   Nested CTA — island button with button-in-button icon
   -------------------------------------------------------------------------- */

.cta {
  --cta-bg: var(--navy);
  --cta-fg: var(--cream);
  --cta-nest: rgba(255, 255, 255, 0.14);

  display: inline-flex;
  align-items: center;
  gap: 0.85rem;
  padding: 0.5rem 0.5rem 0.5rem 1.5rem;
  border-radius: 999px;
  background: var(--cta-bg);
  color: var(--cta-fg);
  font-size: 0.875rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  white-space: nowrap;
  box-shadow: var(--ambient);
  transition: transform var(--t-fast) var(--fluid),
    box-shadow var(--t-fast) var(--fluid),
    background-color var(--t-fast) var(--fluid);
}

.cta:hover {
  box-shadow: var(--ambient-lift);
}

.cta:active {
  transform: scale(0.975);
}

.cta__nest {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  flex: none;
  border-radius: 999px;
  background: var(--cta-nest);
  transition: transform var(--t-fast) var(--fluid),
    background-color var(--t-fast) var(--fluid);
}

.cta:hover .cta__nest {
  transform: translate(3px, -1px) scale(1.06);
  background: rgba(255, 255, 255, 0.24);
}

.cta__nest svg {
  width: 0.875rem;
  height: 0.875rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.3;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.cta--ghost {
  --cta-bg: transparent;
  --cta-fg: var(--ink);
  --cta-nest: var(--shell);
  box-shadow: inset 0 0 0 1px var(--shell-ring);
}

.cta--ghost:hover {
  background: var(--shell);
  box-shadow: inset 0 0 0 1px var(--shell-ring);
}

.cta--ghost:hover .cta__nest {
  background: var(--shell-ring);
}

.cta[aria-disabled="true"] {
  opacity: 0.62;
  cursor: progress;
}

/* --------------------------------------------------------------------------
   Hero — editorial split
   -------------------------------------------------------------------------- */

.hero {
  display: grid;
  align-items: center;
  gap: 3.5rem;
  min-height: min(100dvh, 62rem);
  padding-block: 5rem var(--bay);
  grid-template-columns: 1fr;
}

@media (min-width: 1024px) {
  .hero {
    grid-template-columns: minmax(0, 0.92fr) minmax(0, 1.08fr);
    gap: 4rem;
    padding-block: 4rem var(--bay);
  }
}

.hero__copy {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 1.75rem;
  min-width: 0;
}

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

.hero__meta {
  /* Grid, not wrap: the stats stay on one row at every width instead of
     orphaning the last one. Column count follows the markup, so removing a
     stat does not leave an empty track behind. */
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: auto;
  justify-content: start;
  gap: 1.5rem;
  margin-top: 0.75rem;
}

.stat__num {
  display: block;
  font-family: var(--serif);
  font-size: 1.75rem;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
}

.stat__label {
  display: block;
  margin-top: 0.15rem;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--ink-faint);
}

/* --- Horizontal recipe rail ----------------------------------------- */

.rail {
  min-width: 0;
}

.rail__track {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: min(78vw, 21rem);
  gap: 1.25rem;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  overscroll-behavior-x: contain;
  padding-block: 0.5rem 1.25rem;
  padding-inline: 0 var(--gutter);
  margin-inline: 0 calc(var(--gutter) * -1);
  scrollbar-width: thin;
  scrollbar-color: var(--shell-ring) transparent;
}

@media (min-width: 1024px) {
  .rail__track {
    grid-auto-columns: 20rem;
  }
}

.rail__track > * {
  scroll-snap-align: start;
}

.rail__nav {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.5rem;
}

.rail__all {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  transition: color var(--t-fast) var(--fluid);
}

.rail__all svg {
  width: 0.8rem;
  height: 0.8rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: transform var(--t-fast) var(--fluid);
}

.rail__all:hover {
  color: var(--caramel);
}

.rail__all:hover svg {
  transform: translateX(3px);
}

.rail__hint {
  margin-left: auto;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--ink-faint);
}

/* --- Recipe card ---------------------------------------------------- */

.recipe {
  display: block;
  min-width: 0;
}

.recipe .bezel__core {
  display: flex;
  flex-direction: column;
}

.recipe:hover .bezel__core {
  transform: translateY(-4px);
  box-shadow: var(--core-inset), var(--ambient-lift);
}

.recipe__shot {
  position: relative;
  aspect-ratio: 4 / 5;
  overflow: hidden;
  background: var(--cream-sunk);
}

.recipe__shot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--t-slow) var(--fluid);
}

.recipe:hover .recipe__shot img {
  transform: scale(1.045);
}

.recipe__tag {
  position: absolute;
  z-index: 2;
  top: 0.75rem;
  left: 0.75rem;
  padding: 0.3rem 0.7rem;
  border-radius: 999px;
  /* Opaque, not translucent: this tag lives inside a scroll container, where a
     backdrop blur would repaint the rail every frame. --paper / --paper-ink
     never remap, so the label stays cream-on-espresso over the photograph. */
  background: var(--paper);
  color: var(--paper-ink);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}

.recipe__body {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  padding: 1.15rem 1.25rem 1.35rem;
}

.recipe__title {
  font-family: var(--serif);
  font-size: 1.3125rem;
  line-height: 1.25;
  letter-spacing: -0.01em;
  overflow-wrap: break-word;
  transition: color var(--t-fast) var(--fluid);
}

.recipe:hover .recipe__title {
  color: var(--caramel);
}

.recipe__foot {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: var(--track-data);
  text-transform: uppercase;
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}

.recipe__dot {
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: currentColor;
}

/* --- Gear card ------------------------------------------------------- */

/* A gear card is a recipe card holding a product shot. Product photography
   arrives square and on a white sweep, so it is framed rather than cropped —
   covering a 4:5 box takes the handle off a knife. */

/* The panel is unthemed in both the card and the index thumb: product
   photography arrives on a light sweep, and a light sweep on a dark card is a
   lit box no matter what sits behind it. The generator paints each panel with
   that photo's own backdrop colour inline, so the sweep and the panel are one
   surface; the value here is only the fallback for a shot with no readable
   backdrop. */
/* Square, because the generator trims the seller's backdrop off and what is
   left is the product's own proportions — anything from a 1:5 knife to a
   squat pepper mill. A square panel is the one shape where both touch two
   edges instead of floating. */
.gear .recipe__shot {
  aspect-ratio: 1 / 1;
  background: var(--paper);
}

.gear .recipe__shot img {
  object-fit: contain;
  padding: 1.25rem;
}

.gear:hover .recipe__shot img {
  transform: scale(1.03);
}

/* Gear photography arrives with the seller's sweep baked into the pixels — no
   alpha — and the panel is painted that same value so there is no seam around
   the product. In dark theme the two have to move together: a scrim across the
   whole shot takes the sweep and the panel down by the same amount, so the
   product sits into the page and the seam stays invisible. Darkening only the
   panel would put a bright rectangle back on the card, which is precisely what
   the matched panel exists to prevent.

   A scrim rather than a filter: these cards live in a horizontal rail, and the
   tag above already refuses backdrop-blur to keep the rail from repainting
   every frame. The tag sits above the scrim on its own z-index — it is a label
   on the card, not part of the photograph. */
:root[data-theme="dark"] .gear .recipe__shot::after,
:root[data-theme="dark"] .idx__thumb span::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--cream);
  opacity: 0.26;
  pointer-events: none;
}

.gear__lede {
  margin: 1.25rem 0 2.75rem;
  max-width: 46ch;
}

.gear__note {
  font-size: 0.875rem;
  line-height: 1.5;
  color: var(--ink-soft);
}

/* The arrow is the only thing on a gear card that says "this leaves the site".
   The link also carries an sr-only "opens in a new tab", because an arrow is
   not announced. */
.gear__out {
  width: 0.7rem;
  height: 0.7rem;
  margin-left: 0.35rem;
  vertical-align: -1px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.idx__row--gear .idx__thumb span {
  background: var(--paper);
}

.idx__row--gear .idx__thumb img {
  object-fit: contain;
  padding: 0.35rem;
}

/* Eats: the score takes the thumbnail's column. It is Beli's circle, drawn in
   the site's hairline instead of Beli's green, because the number is the
   review and a photo of somebody else's dining room is not. */
.idx__score {
  grid-row: span 2;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 3.5rem;
  aspect-ratio: 1;
  border-radius: 50%;
  box-shadow: inset 0 0 0 1px var(--hairline);
  font-family: var(--serif);
  font-size: 1.25rem;
  font-variant-numeric: lining-nums tabular-nums;
  color: var(--ink);
  transition: box-shadow var(--t-fast) var(--fluid);
}

@media (min-width: 768px) {
  .idx__score {
    grid-row: auto;
    width: 4.25rem;
    font-size: 1.5rem;
  }
}

.idx__row:hover .idx__score {
  box-shadow: inset 0 0 0 1px var(--caramel);
}

/* Unlike the note, the dishes survive the narrowest screens: "what do I
   order" is the question the page exists to answer. */
.idx__dishes {
  display: block;
  margin-top: 0.3rem;
  overflow-wrap: break-word;
  font-size: 0.875rem;
  line-height: 1.5;
  color: var(--ink-soft);
}

.idx__dishes b {
  margin-right: 0.35rem;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: var(--track-data);
  text-transform: uppercase;
  color: var(--ink-faint);
}

/* --------------------------------------------------------------------------
   About — z-axis split
   -------------------------------------------------------------------------- */

.about {
  display: grid;
  gap: 3rem;
  align-items: center;
  grid-template-columns: 1fr;
}

@media (min-width: 1024px) {
  .about {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 5rem;
  }
}

.about__frame {
  aspect-ratio: 4 / 5;
}

.about__frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.about__copy {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 1.5rem;
  min-width: 0;
}

.prose {
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
  font-size: 1.0625rem;
  line-height: var(--lead-prose);
  color: var(--ink-soft);
  max-width: 56ch;
}

.about__sign {
  font-family: var(--serif);
  font-style: italic;
  font-size: 1.25rem;
  line-height: 1.4;
  color: var(--ink);
  padding-top: 1.5rem;
  border-top: 1px solid var(--hairline);
  width: 100%;
}

/* --------------------------------------------------------------------------
   Recipe index
   -------------------------------------------------------------------------- */

.index {
  display: grid;
  gap: 3rem;
  align-items: start;
  grid-template-columns: 1fr;
}

@media (min-width: 1024px) {
  .index {
    grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
    gap: 5rem;
  }
}

.search {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  max-width: 26rem;
  padding: 0.5rem 0.5rem 0.5rem 1.35rem;
  border-radius: 999px;
  background: var(--core);
  box-shadow: inset 0 0 0 1px var(--shell-ring), var(--ambient);
  transition: box-shadow var(--t-fast) var(--fluid);
}

.search:focus-within {
  box-shadow: inset 0 0 0 1.5px var(--caramel), var(--ambient-lift);
}

.search input {
  flex: 1;
  min-width: 0;
  background: none;
  border: 0;
  padding: 0.4rem 0;
  font-family: var(--serif);
  font-size: 1.0625rem;
}

/* The caret is a browser surface like any other. Caramel reads at 5.13 on
   cream and 8.95 on the dark ground, so it stays visible in both themes — and
   it is the one blinking thing on the page, so it may as well be ours. Scoped
   to search: the rundown field sits on the fixed navy panel, where caramel
   falls to 2.68 and the light caret it inherits is the correct one. */
.search input {
  caret-color: var(--caramel);
}

.search input::placeholder {
  color: var(--ink-faint);
}

.search input:focus-visible {
  outline: none;
}

.search input::-webkit-search-cancel-button {
  -webkit-appearance: none;
}

.search__go {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  flex: none;
  border-radius: 999px;
  background: var(--navy);
  color: var(--cream);
  transition: transform var(--t-fast) var(--fluid);
}

.search__go:hover {
  transform: translate(2px, -1px) scale(1.05);
}

.search__go svg {
  width: 0.95rem;
  height: 0.95rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.35;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* --------------------------------------------------------------------------
   Rundown
   -------------------------------------------------------------------------- */

.rundown .bezel__core {
  padding: clamp(2.5rem, 1.5rem + 4vw, 5.5rem) clamp(1.5rem, 0.5rem + 4vw, 5rem);
  text-align: center;
  background: var(--navy-ground);
  color: var(--on-navy);
  box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.1), var(--ambient);
}

.rundown__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.25rem;
  max-width: 44rem;
  margin-inline: auto;
}


.rundown .headline {
  color: var(--on-navy);
}

.rundown__copy {
  font-family: var(--serif);
  font-style: italic;
  font-size: 1.125rem;
  line-height: 1.6;
  color: var(--on-navy-soft);
  max-width: 40ch;
}

.rundown__form {
  width: 100%;
  max-width: 32rem;
  margin-top: 0.75rem;
}

.rundown__field {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.4rem 0.4rem 0.4rem 1.4rem;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.08);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.16);
  transition: box-shadow var(--t-fast) var(--fluid);
}

.rundown__field:focus-within {
  box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--paper) 90%, transparent);
}

.rundown__field input {
  flex: 1;
  min-width: 0;
  background: none;
  border: 0;
  padding: 0.7rem 0;
  /* Paper, not --cream: a theme token here inverts to near-black on navy. */
  color: var(--paper);
  font-size: 0.9375rem;
}

.rundown__field input::placeholder {
  color: var(--on-navy-faint);
}

.rundown__field input:focus-visible {
  outline: none;
}

.rundown__field input[aria-invalid="true"] {
  color: var(--state-error-field);
}

.rundown__field .cta {
  --cta-bg: var(--paper);
  --cta-fg: var(--navy-ground);
  --cta-nest: color-mix(in srgb, var(--navy-ground) 10%, transparent);
  box-shadow: none;
  padding-left: 1.25rem;
}

.rundown__field .cta:hover .cta__nest {
  background: color-mix(in srgb, var(--navy-ground) 18%, transparent);
}

.rundown .note {
  min-height: 1.4em;
  margin-top: 0.85rem;
  font-size: 0.875rem;
  color: var(--on-navy-faint);
  overflow-wrap: break-word;
}

.rundown .note[data-state="error"] {
  color: var(--state-error);
}

.rundown .note[data-state="success"] {
  color: var(--state-success);
}

.spinner {
  width: 0.8rem;
  height: 0.8rem;
  border: 1.5px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  /* Constant rotation is the one linear in the system: easing a spin reads
     as a stall. Reduced-motion replaces this with a static ring. */
  animation: spin 640ms linear infinite;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

/* --------------------------------------------------------------------------
   Footer
   -------------------------------------------------------------------------- */

.footer {
  padding-block: 4rem calc(3rem + env(safe-area-inset-bottom));
  border-top: 1px solid var(--hairline);
}

.footer__grid {
  display: grid;
  gap: 2.5rem;
  grid-template-columns: 1fr;
}

@media (min-width: 900px) {
  .footer__grid {
    grid-template-columns: minmax(0, 1.4fr) repeat(2, minmax(0, 1fr));
    gap: 3rem;
  }
}

.footer__mark {
  width: 148px;
  height: auto;
  margin-bottom: 1.1rem;
}

.footer__blurb {
  font-family: var(--serif);
  font-style: italic;
  font-size: 1rem;
  line-height: 1.5;
  color: var(--ink-soft);
  max-width: 32ch;
}

.footer__title {
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: var(--track-head);
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: 1rem;
}

.footer__list {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  list-style: none;
  margin: 0;
  padding: 0;
  font-size: 0.9375rem;
  color: var(--ink-soft);
}

/* A 15px link is a 19px box, and seven of them stacked 0.6rem apart clears
   neither half of WCAG 2.5.8: not 24px tall, and not 24px from its neighbour
   either, so the spacing exception does not rescue it. The coarse-pointer
   block below already gives a thumb its 44px row; this is the mouse, which
   still has a minimum. Height only — the text sets the width, and every label
   here is wider than 24px already. */
.footer__list a {
  display: inline-flex;
  align-items: center;
  min-height: 1.5rem;
  transition: color var(--t-fast) var(--fluid);
}

.footer__list a:hover {
  color: var(--caramel);
}

.footer__base {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-top: 3.5rem;
  padding-top: 1.75rem;
  border-top: 1px solid var(--hairline);
  font-size: 0.8125rem;
  color: var(--ink-faint);
}

.footer__socials {
  display: flex;
  gap: 0.25rem;
}

/* --------------------------------------------------------------------------
   Scroll interpolation
   -------------------------------------------------------------------------- */

/* One authored moment, on load, with no JavaScript anywhere in the path: the
   hero settles out of a blur while the rail lands in sequence, 90ms apart.

   Nothing here animates opacity, and that is the point. An element at
   `opacity: 0` is not a paint, so fading the hero in means the largest thing
   on the page does not count as painted until the animation ends — the entry
   choreography becomes the LCP. Travel and blur read as the same arrival and
   cost nothing, because the text and the photographs are on screen from the
   first frame. */
[data-reveal] {
  animation: settle var(--t-slow) var(--fluid) both;
}

/* Blur is reserved for the few hero-scale elements so the GPU cost stays
   bounded; everything else resolves with transform alone. */
[data-reveal="soft"] {
  animation-name: settle-soft;
}

[data-stagger] > * {
  animation: land var(--t-slow) var(--fluid) both;
}

[data-stagger] > *:nth-child(1) { animation-delay: 0ms; }
[data-stagger] > *:nth-child(2) { animation-delay: 90ms; }
[data-stagger] > *:nth-child(3) { animation-delay: 180ms; }
/* Past the fourth card the rail is off-screen; staggering further only delays
   cards nobody is looking at. */
[data-stagger] > *:nth-child(n + 4) { animation-delay: 270ms; }

@keyframes settle {
  from { transform: translate3d(0, 3.5rem, 0); }
}

@keyframes settle-soft {
  from {
    transform: translate3d(0, 3.5rem, 0);
    filter: blur(10px);
  }
}

@keyframes land {
  from { transform: translate3d(0, 2.5rem, 0); }
}

/* --------------------------------------------------------------------------
   Reduced motion — every choreography above stands down
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  /* Reduced motion, not absent feedback. Spatial change is removed entirely,
     but colour, opacity, and elevation still transition — briefly — so a
     chip selecting, a field erroring, or a control focusing stays legible.
     Excluding transform from the property list means state still arrives, it
     just arrives without travel: the hamburger still becomes an X.
     Animations (entry, spinner) are none, not a 0.01ms flash. */
  *,
  *::before,
  *::after {
    transition-property: opacity, color, background-color, border-color,
      outline-color, box-shadow, fill, stroke !important;
    transition-duration: 140ms !important;
    transition-delay: 0ms !important;
    animation: none !important;
  }

  .veil__link,
  .veil__foot {
    opacity: 1;
    transform: none;
    filter: none;
  }

  /* Cards still deepen their shadow on hover; they no longer rise. */
  .recipe:hover .recipe__shot img,
  .recipe:hover .bezel__core,
  .idx__row:hover .idx__thumb img,
  .rail__all:hover svg,
  .cta:hover .cta__nest,
  .search__go:hover {
    transform: none;
  }

  .spinner {
    animation: none;
    border-top-color: currentColor;
    opacity: 0.55;
  }
}

/* --------------------------------------------------------------------------
   Late additions
   -------------------------------------------------------------------------- */

.skip {
  position: fixed;
  top: 1rem;
  left: 1rem;
  z-index: 70;
  padding: 0.75rem 1.35rem;
  border-radius: 999px;
  background: var(--navy);
  color: var(--cream);
  font-size: 0.8125rem;
  font-weight: 600;
  box-shadow: var(--ambient-lift);
  clip-path: inset(50%);
}

.skip:focus-visible {
  clip-path: none;
}

.about__frame .bezel__core {
  height: 100%;
}

/* --------------------------------------------------------------------------
   Input method
   Screen size does not tell you how the page is being touched. These two
   blocks key off the pointer itself, so a touchscreen laptop gets thumb-sized
   targets and a mouse keeps the tight pill vocabulary.
   -------------------------------------------------------------------------- */

@media (pointer: coarse) {
  .ctl,
  .burger {
    width: 2.75rem;
    height: 2.75rem;
  }


  .dock__link {
    display: inline-flex;
    align-items: center;
    min-height: 2.75rem;
  }

  .chip {
    min-height: 2.75rem;
    display: inline-flex;
    align-items: center;
  }

  .search {
    padding-left: 1.25rem;
  }

  .search input {
    padding-block: 0.6rem;
  }

  .search__go {
    width: 2.75rem;
    height: 2.75rem;
  }

  /* The base rule already seats these as flex rows at the mouse minimum; the
     thumb wants the full 44px. */
  .footer__list a {
    min-height: 2.75rem;
  }

  .rail__all {
    min-height: 2.75rem;
  }

  /* Per-step tools sit at ~27px on a mouse; the bar at 40px. Both miss the
     thumb. Keep the mouse pills, grow only when the pointer is coarse. */
  .cook-tool,
  .cook-bar__nav .cook-tool {
    min-height: 2.75rem;
    padding-block: 0.65rem;
    padding-inline: 1rem;
  }

  .rcp__step-tools {
    gap: 0.65rem;
  }

  html[data-cooking] {
    scroll-padding-bottom: 9rem;
  }

  html[data-cooking] body {
    padding-bottom: 8.5rem;
  }
}

@media (hover: none) {
  /* No hover means no un-hover: on touch these states latch after a tap and
     stay until the visitor taps elsewhere. The press feedback on .cta and the
     focus ring carry the acknowledgement instead. */
  .recipe:hover .bezel__core,
  .recipe:hover .recipe__shot img,
  .idx__row:hover .idx__thumb img,
  .rail__all:hover svg,
  .cta:hover .cta__nest,
  .cta--ghost:hover .cta__nest,
  .rundown__field .cta:hover .cta__nest,
  .search__go:hover {
    transform: none;
  }

  .recipe:hover .bezel__core {
    box-shadow: var(--core-inset), var(--ambient);
  }

  /* Cook-tool hover is a fill, so a tap latches linen until the next tap.
     Press acknowledgement is :active; the resting fill stays. Selectors
     match the hover rules so this block actually wins. */
  .cook-tool:hover:not(:disabled) {
    color: var(--ink-soft);
    background: var(--shell);
  }

  .cook-bar__nav .cook-tool--primary:hover:not(:disabled),
  .cook-tool[aria-pressed="true"]:hover:not(:disabled) {
    color: var(--cream);
    background: var(--navy);
    box-shadow: var(--ambient);
  }
}

/* --------------------------------------------------------------------------
   Recipe page
   A single recipe read standing up in a kitchen: the ingredients stay on
   screen while the method scrolls, because the two get consulted in
   alternation and re-finding your place costs more than the column width.
   -------------------------------------------------------------------------- */

.rcp__top {
  padding-top: 9rem;
  padding-bottom: var(--bay);
}

@media (min-width: 900px) {
  .rcp__top {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
    align-items: center;
    gap: 4rem;
    padding-top: 11rem;
  }
}

/* The course, carried by the only element that can do something with it:
   this returns to the index already filtered to the recipes beside this one.
   It replaced a tracked-caps chip that sat above the title saying the same
   word and doing nothing. */
.rcp__back {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.75rem;
  font-size: 0.8125rem;
  font-weight: 500;
  letter-spacing: 0.01em;
  color: var(--ink-soft);
  transition: color var(--t-fast) var(--fluid);
}

.rcp__back:hover {
  color: var(--caramel);
}

.rcp__head {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 1.5rem;
  min-width: 0;
}

/* The recipe title is the page's only display-scale element, but it is real
   prose of unpredictable length — cap it below .display's ceiling so a long
   title does not turn into six lines of 8rem type. */
.rcp__title {
  font-family: var(--serif);
  font-optical-sizing: auto;
  font-weight: 400;
  letter-spacing: var(--track-display);
  line-height: 0.98;
  font-size: clamp(2.75rem, 1.5rem + 4.6vw, 5rem);
  max-width: 16ch;
  /* Titles are authored prose of unknown shape. One unbroken token — a German
     compound, a pasted URL — has no wrap opportunity and takes the page with
     it. Break as a last resort only; normal words still wrap normally. */
  overflow-wrap: break-word;
}

.rcp__lede {
  max-width: 42ch;
}

.rcp__meta {
  display: flex;
  flex-wrap: wrap;
  gap: 2rem;
  margin: 0;
  padding-top: 0.5rem;
}

.rcp__meta > div {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}

.rcp__meta dt {
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--ink-faint);
}

.rcp__meta dd {
  margin: 0;
  overflow-wrap: break-word;
  font-family: var(--serif);
  font-size: 1.375rem;
  line-height: 1.1;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

.rcp__meta dd a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 0.2em;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--hairline);
  transition: color var(--t-fast) var(--fluid);
}

.rcp__meta dd a:hover {
  color: var(--caramel);
}

.rcp__shot {
  margin: 3rem 0 0;
}

@media (min-width: 900px) {
  .rcp__shot {
    margin-top: 0;
  }
}

.rcp__shot img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

/* --- The two columns ------------------------------------------------ */

.rcp__grid {
  display: grid;
  gap: 3rem;
  grid-template-columns: 1fr;
}

@media (min-width: 900px) {
  .rcp__grid {
    grid-template-columns: minmax(0, 20rem) minmax(0, 1fr);
    gap: 4.5rem;
    align-items: start;
  }

  .rcp__aside {
    position: sticky;
    /* Clears the floating dock plus its shadow. */
    top: 7.5rem;
    max-height: calc(100vh - 9rem);
    overflow-y: auto;
    overscroll-behavior: contain;
  }
}

.rcp__h {
  font-family: var(--serif);
  font-size: 1.75rem;
  line-height: 1.1;
  letter-spacing: -0.015em;
  margin: 0 0 1.5rem;
}

.rcp__ing {
  padding: 1.75rem 1.75rem 2rem;
}

.rcp__group {
  margin: 1.75rem 0 0.85rem;
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--caramel);
}

.rcp__group:first-of-type {
  margin-top: 0;
}

.rcp__ings {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}

.rcp__ings li {
  padding-block: 0.7rem;
  font-size: 0.9375rem;
  line-height: 1.5;
  color: var(--ink-soft);
  border-top: 1px solid var(--hairline);
}

.rcp__ings li:first-child {
  border-top: 0;
  padding-top: 0;
}

.rcp__steps {
  list-style: none;
  counter-reset: step;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 2rem;
}

.rcp__steps li {
  counter-increment: step;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 1.25rem;
  align-items: start;
}

/* The number is generated, not authored: renumbering a reordered method by
   hand is exactly the kind of thing that silently goes wrong. */
.rcp__steps li::before {
  content: counter(step);
  display: flex;
  align-items: center;
  justify-content: center;
  grid-column: 1;
  grid-row: 1;
  width: 2.25rem;
  height: 2.25rem;
  border-radius: 50%;
  background: var(--shell);
  box-shadow: inset 0 0 0 1px var(--shell-ring);
  font-family: var(--serif);
  font-size: 1rem;
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
}

.rcp__steps p {
  margin: 0;
  padding-top: 0.35rem;
  font-size: 1.0625rem;
  line-height: 1.7;
  color: var(--ink-soft);
  max-width: 62ch;
}

.rcp__notes {
  margin-top: 3.5rem;
  padding: 1.75rem 2rem;
  border-radius: var(--r-core);
  background: var(--shell);
  box-shadow: inset 0 0 0 1px var(--shell-ring);
}

.rcp__notes h2 {
  margin: 0 0 0.75rem;
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--caramel);
}

.rcp__notes p {
  margin: 0 0 0.85rem;
  font-size: 0.9375rem;
  line-height: 1.7;
  color: var(--ink-soft);
  max-width: 62ch;
}

.rcp__notes p:last-child {
  margin-bottom: 0;
}

.rcp__source {
  margin-top: 2.5rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--hairline);
  font-size: 0.9375rem;
  line-height: 1.6;
  color: var(--ink-faint);
}

.rcp__source a {
  color: var(--ink-soft);
  text-decoration: underline;
  text-underline-offset: 0.2em;
  text-decoration-thickness: 1px;
  transition: color var(--t-fast) var(--fluid);
}

.rcp__source a:hover {
  color: var(--caramel);
}

/* --- Cooking session -------------------------------------------------
   Progressive enhancement on the method: a launch row, per-step tools,
   and a fixed cook bar. Without JS the method is still the method. */

.cook-launch {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.85rem 1.25rem;
  margin: 0 0 2rem;
}

.cook-launch__hint {
  margin: 0;
  font-size: 0.8125rem;
  line-height: 1.45;
  color: var(--ink-faint);
  max-width: 36ch;
}

.rcp__step-main {
  grid-column: 2;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
}

.rcp__step-tools {
  display: none;
  flex-wrap: wrap;
  gap: 0.5rem;
}

html[data-cooking] .rcp__step-tools {
  display: flex;
}

.cook-tool {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.4rem 0.85rem;
  border-radius: 999px;
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--ink-soft);
  background: var(--shell);
  box-shadow: inset 0 0 0 1px var(--shell-ring);
  transition: color var(--t-fast) var(--fluid),
    background-color var(--t-fast) var(--fluid),
    box-shadow var(--t-fast) var(--fluid);
}

.cook-tool:hover:not(:disabled) {
  color: var(--ink);
  background: var(--linen);
}

.cook-tool:active:not(:disabled) {
  color: var(--ink);
  background: var(--linen);
}

.cook-tool[aria-pressed="true"] {
  color: var(--cream);
  background: var(--navy);
  box-shadow: var(--ambient);
}

.cook-tool[aria-pressed="true"]:hover:not(:disabled),
.cook-tool[aria-pressed="true"]:active:not(:disabled) {
  color: var(--cream);
  background: var(--navy);
}

.cook-tool:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

.rcp__steps li.is-done .rcp__step-main > p {
  color: var(--ink-faint);
  text-decoration: line-through;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--hairline);
}

.rcp__steps li.is-done::before {
  content: "";
  background: var(--navy);
  box-shadow: none;
}

/* Same 24-unit hairline as .ctl — not a Unicode glyph. Sits on the
   number disc; hidden until the step is marked done. */
.rcp__step-check {
  display: none;
  grid-column: 1;
  grid-row: 1;
  width: 2.25rem;
  height: 2.25rem;
  fill: none;
  stroke: currentColor;
  color: var(--cream);
  stroke-width: 1.15;
  stroke-linecap: round;
  stroke-linejoin: round;
  pointer-events: none;
  z-index: 1;
}

.rcp__steps li.is-done .rcp__step-check {
  display: block;
}

html[data-cooking] .rcp__steps {
  gap: 1.25rem;
}

html[data-cooking] .rcp__steps li {
  padding: 1rem 1rem 1rem 0.25rem;
  border-radius: var(--r-core);
  transition: background-color var(--t-fast) var(--fluid),
    box-shadow var(--t-fast) var(--fluid),
    color var(--t-fast) var(--fluid);
}

/* Do not dim with opacity: 0.42 on --ink-soft is 1.97:1 on cream, and the
   row stays a control. Faint ink still clears 5.14:1. */
html[data-cooking] .rcp__steps li:not(.is-current) .rcp__step-main > p {
  color: var(--ink-faint);
}

html[data-cooking] .rcp__steps li.is-current {
  background: var(--shell);
  box-shadow: inset 0 0 0 1px var(--shell-ring);
  padding-inline: 1rem 1.15rem;
}

html[data-cooking] .rcp__steps li.is-current .rcp__step-main > p {
  color: var(--ink);
}

html[data-cooking] .rcp__steps li.is-current::before {
  background: var(--navy);
  color: var(--cream);
  box-shadow: none;
}

html[data-cooking] {
  scroll-padding-bottom: 7rem;
}

html[data-cooking] body {
  padding-bottom: 6.5rem;
}

.cook-bar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 40;
  padding: 0.75rem 1.25rem calc(0.75rem + env(safe-area-inset-bottom, 0px));
  background: var(--glass);
  backdrop-filter: blur(24px) saturate(180%);
  -webkit-backdrop-filter: blur(24px) saturate(180%);
  box-shadow: inset 0 1px 0 var(--glass-ring),
    0 -18px 40px -28px rgba(23, 21, 15, 0.28);
  transform: translateY(110%);
  opacity: 0;
  pointer-events: none;
  transition: transform var(--t-base) var(--fluid),
    opacity var(--t-base) var(--fluid);
}

html[data-cooking] .cook-bar {
  transform: translateY(0);
  opacity: 1;
  pointer-events: auto;
}

.cook-bar__inner {
  display: grid;
  gap: 0.65rem;
  max-width: 44rem;
  margin-inline: auto;
}

.cook-bar__row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 0.65rem;
}

.cook-bar__status {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--ink);
}

.cook-bar__status span {
  display: block;
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: 0.15rem;
}

.cook-bar__timer {
  font-family: var(--serif);
  font-variant-numeric: tabular-nums;
  font-size: 1.25rem;
  line-height: 1;
  color: var(--ink);
  min-width: 4.5ch;
  text-align: right;
}

.cook-bar__timer[data-state="idle"] {
  color: var(--ink-faint);
  font-size: 0.8125rem;
  font-family: var(--grotesk);
  font-weight: 500;
  min-width: 0;
}

.cook-bar__timer[data-state="done"] {
  color: var(--caramel);
}

.cook-bar__nav {
  display: flex;
  flex-wrap: wrap;
  gap: 0.45rem;
  width: 100%;
}

.cook-bar__nav .cook-tool {
  flex: 1 1 auto;
  justify-content: center;
  min-height: 2.5rem;
  padding-block: 0.55rem;
}

.cook-bar__nav .cook-tool--primary {
  color: var(--cream);
  background: var(--navy);
  box-shadow: var(--ambient);
}

.cook-bar__nav .cook-tool--primary:hover:not(:disabled),
.cook-bar__nav .cook-tool--primary:active:not(:disabled) {
  color: var(--cream);
  background: var(--navy);
}

.cook-live {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

@media (min-width: 640px) {
  .cook-bar__inner {
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
  }

  .cook-bar__nav {
    width: auto;
    flex-wrap: nowrap;
  }

  .cook-bar__nav .cook-tool {
    flex: 0 0 auto;
  }
}

@media (prefers-reduced-motion: reduce) {
  .cook-bar {
    transition: none;
  }
}


/* --------------------------------------------------------------------------
   Recipe index (/recipes/)
   A printed contents page. Fifty double bezels stacked would read as a wall of
   cards, so the enclosure holds the photograph and the list stays a list —
   hairline-separated rows, the way .rcp__ings already does.
   -------------------------------------------------------------------------- */

.idx__top {
  padding-top: 9rem;
  padding-bottom: 3.5rem;
}

@media (min-width: 900px) {
  .idx__top {
    padding-top: 11rem;
  }
}

.idx__head {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 1.5rem;
  min-width: 0;
}

/* --- Controls -------------------------------------------------------- */

.idx__controls {
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
  margin-top: 2.75rem;
}

@media (min-width: 900px) {
  .idx__controls {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    gap: 2rem;
  }
}

/* The tray is a segmented well, not a sideways drawer. A hidden scrollbar
   buried Italy–UK on Where I Eat and Salads–Sweets on a 320px Recipe Book.
   Wrapping keeps every chip on the board. Chips stay pills; the well uses
   the small shell radius so two rows do not become a stadium. */
.filters {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  padding: 0.4rem;
  border-radius: var(--r-shell-sm);
  background: var(--shell);
  box-shadow: inset 0 0 0 1px var(--shell-ring);
  width: max-content;
  max-width: 100%;
  min-width: 0;
}

@media (min-width: 900px) {
  .idx__controls .filters {
    flex: 1 1 12rem;
  }
}

.chip {
  padding: 0.5rem 1.05rem;
  border-radius: 999px;
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--ink-soft);
  white-space: nowrap;
  transition: color var(--t-fast) var(--fluid),
    background-color var(--t-fast) var(--fluid),
    box-shadow var(--t-fast) var(--fluid);
}

.chip:hover {
  color: var(--ink);
  background: var(--shell);
}

.chip[aria-pressed="true"] {
  color: var(--cream);
  background: var(--navy);
  box-shadow: var(--ambient);
}

.idx__controls .search {
  max-width: 22rem;
}

/* --- Course groups --------------------------------------------------- */

.idx__group {
  margin-top: 4rem;
}

.idx__group:first-of-type {
  margin-top: 0;
}

.idx__group[hidden] {
  display: none;
}

/* Label left, hairline running out to the measure. The rule is the contents
   page; the heading is a label because the rows carry the weight. */
.idx__course {
  display: flex;
  align-items: center;
  gap: 1.25rem;
  margin: 0 0 0.5rem;
  font-family: var(--grotesk);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: var(--track-head);
  text-transform: uppercase;
  color: var(--ink-faint);
}

.idx__course::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--hairline);
}

.idx__group[data-collapsed] .idx__course {
  display: none;
}

.idx__list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.idx__list li {
  border-top: 1px solid var(--hairline);
}

.idx__list li[hidden] {
  display: none;
}

/* --- The row --------------------------------------------------------- */

.idx__row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 0.35rem 1.1rem;
  align-items: center;
  padding: 1.1rem 0.85rem;
  margin-inline: -0.85rem;
  border-radius: var(--r-core-sm);
  transition: background-color var(--t-fast) var(--fluid);
}

@media (min-width: 768px) {
  .idx__row {
    grid-template-columns: auto minmax(0, 1fr) auto;
    gap: 1.5rem;
    padding-block: 1.35rem;
  }
}

.idx__row:hover {
  background: var(--shell);
}

.idx__thumb {
  grid-row: span 2;
  width: 4.5rem;
  padding: var(--bezel-pad);
  border-radius: var(--r-shell-sm);
  background: var(--shell);
  box-shadow: inset 0 0 0 1px var(--shell-ring);
}

@media (min-width: 768px) {
  .idx__thumb {
    grid-row: auto;
    width: 5.5rem;
  }
}

.idx__thumb span {
  display: block;
  position: relative;
  overflow: hidden;
  border-radius: var(--r-core-sm);
  background: var(--cream-sunk);
  box-shadow: var(--core-inset), var(--ambient);
}

.idx__thumb img {
  width: 100%;
  height: auto;
  aspect-ratio: 1;
  object-fit: cover;
  transition: transform var(--t-slow) var(--fluid);
}

.idx__row:hover .idx__thumb img {
  transform: scale(1.06);
}

.idx__body {
  min-width: 0;
}

.idx__title {
  font-family: var(--serif);
  font-size: 1.3125rem;
  line-height: 1.25;
  letter-spacing: -0.01em;
  overflow-wrap: break-word;
  color: var(--ink);
  transition: color var(--t-fast) var(--fluid);
}

@media (min-width: 768px) {
  .idx__title {
    font-size: 1.5rem;
  }
}

.idx__row:hover .idx__title {
  color: var(--caramel);
}

/* The one line of voice. Dropped on the narrowest screens: at 375px it costs
   three lines per row, and a contents page that does not fit on a screen has
   stopped being a contents page. */
.idx__note {
  display: none;
  overflow-wrap: break-word;
  margin-top: 0.35rem;
  font-size: 0.9375rem;
  line-height: 1.55;
  color: var(--ink-soft);
  max-width: 58ch;
}

@media (min-width: 640px) {
  .idx__note {
    display: block;
  }
}

/* min-width:0 because a grid item's automatic minimum is its min-content
   width: with nowrap on the container, "1 HR 15 MIN · SERVES 6 TO 8" refuses
   to shrink and pushes the whole page sideways at 320px. The values stay
   unbreakable; the pair is allowed to wrap between them. */
.idx__meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.15rem 0.5rem;
  min-width: 0;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: var(--track-data);
  text-transform: uppercase;
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}

.idx__meta > span {
  white-space: nowrap;
}

@media (min-width: 768px) {
  .idx__meta {
    justify-content: flex-end;
  }
}

.idx__dot {
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: currentColor;
  flex: none;
}

@media (max-width: 767px) {
  /* Once the pair can wrap, the separator between them is doing nothing but
     stranding itself at the end of a line. */
  .idx__meta {
    gap: 0 0.5rem;
  }

  .idx__dot {
    display: none;
  }
}

.idx__empty {
  padding: 4rem 0;
  font-family: var(--serif);
  font-style: italic;
  font-size: 1.25rem;
  line-height: 1.5;
  color: var(--ink-faint);
  max-width: 40ch;
}

.idx__empty[hidden] {
  display: none;
}

.idx__empty b {
  font-style: normal;
  font-weight: 400;
  color: var(--ink);
}

/* --------------------------------------------------------------------------
   Legal pages
   Standalone documents (/privacy/). They borrow .bay, .headline and .lede and
   add only what a long-form text page needs that the one-pager never did:
   a plain header, and real heading/list rhythm inside running prose.
   -------------------------------------------------------------------------- */

.legal__head {
  padding-block: 2rem 0;
  border-bottom: 1px solid var(--hairline);
}

.legal__head .bay {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}

.legal__brand {
  display: inline-block;
  width: 8.5rem;
  max-width: 40vw;
}

.legal__brand img {
  display: block;
  width: 100%;
  height: auto;
}

.legal {
  max-width: 62ch;
  margin-top: 3.5rem;
  font-size: 1.0625rem;
  line-height: var(--lead-prose);
  color: var(--ink-soft);
}

.legal h2 {
  font-family: var(--serif);
  font-weight: 400;
  font-size: 1.5rem;
  line-height: 1.2;
  letter-spacing: -0.01em;
  color: var(--ink);
  margin: 3rem 0 0.75rem;
  padding-top: 1.75rem;
  border-top: 1px solid var(--hairline);
}

.legal h2:first-child {
  margin-top: 0;
  padding-top: 0;
  border-top: 0;
}

.legal p {
  margin: 0 0 1.25rem;
}

.legal ul {
  margin: 0 0 1.25rem;
  padding-left: 1.1rem;
}

.legal li {
  margin-bottom: 0.6rem;
}

.legal strong {
  color: var(--ink);
  font-weight: 600;
}

.legal code {
  font-size: 0.9375rem;
  padding: 0.1em 0.4em;
  border-radius: 3px;
  background: var(--cream-sunk);
  border: 1px solid var(--hairline);
}

.legal a {
  color: var(--ink);
  text-decoration: underline;
  text-underline-offset: 0.2em;
  text-decoration-thickness: 1px;
}

.legal a:hover {
  color: var(--caramel);
}

.legal__stamp {
  margin-top: 3rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--hairline);
  font-family: var(--grotesk);
  font-size: 0.8125rem;
  color: var(--ink-faint);
}

/* --------------------------------------------------------------------------
   404
   Its own centred layout rather than the legal-page frame: there is no
   document to read here, so the page is one short apology and a way out.
   -------------------------------------------------------------------------- */

.notfound {
  min-height: 100svh;
  display: grid;
  place-items: center;
  text-align: center;
  padding-block: 4rem;
}

.notfound__theme {
  position: absolute;
  top: calc(1.25rem + env(safe-area-inset-top));
  right: max(1.25rem, env(safe-area-inset-right));
  z-index: 52;
}

.notfound__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.notfound__brand {
  display: block;
  width: 9rem;
  max-width: 45vw;
  margin-bottom: 3.5rem;
}

.notfound__brand img {
  display: block;
  width: 100%;
  height: auto;
}

.notfound .lede {
  max-width: 38ch;
  margin-top: 1.25rem;
}

.notfound__links {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 1.25rem 2rem;
  margin-top: 2.75rem;
}

.notfound__alt {
  font-family: var(--grotesk);
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--ink-faint);
  text-decoration: none;
}

.notfound__alt:hover {
  color: var(--ink);
}
