/* ============================================================
   Mathews Artisan Butchers, preview
   Ultra high-end pass · Editorial Luxury archetype
   Design system per _briefs/THEME_DNA.md
   (No glassmorphism, no decorative gradients, no card drop-shadows ,
    depth comes from framing, grain, type & motion.)
   ============================================================ */

/* ---------- Self-hosted fonts (fontsource, latin subset) ---------- */
@font-face { font-family: "Cormorant Garamond"; font-style: normal; font-weight: 500; font-display: swap; src: url("../fonts/cormorant-500.woff2") format("woff2"); }
@font-face { font-family: "Cormorant Garamond"; font-style: normal; font-weight: 600; font-display: swap; src: url("../fonts/cormorant-600.woff2") format("woff2"); }
@font-face { font-family: "Cormorant Garamond"; font-style: italic; font-weight: 500; font-display: swap; src: url("../fonts/cormorant-500-italic.woff2") format("woff2"); }
@font-face { font-family: "Cormorant Garamond"; font-style: italic; font-weight: 600; font-display: swap; src: url("../fonts/cormorant-600-italic.woff2") format("woff2"); }
@font-face { font-family: "Hanken Grotesk"; font-style: normal; font-weight: 300 700; font-display: swap; src: url("../fonts/hanken-variable.woff2") format("woff2"); }

:root {
  /* Brand */
  --gold:        oklch(0.68 0.140 84);
  --gold-bright: oklch(0.75 0.145 86);
  /* THE WORDMARK'S OWN INK, sampled 2026-08-27 from assets/img/mathews-wordmark-900.png:
     11,311 fully-opaque pixels of rgb(203,151,0), the single dominant colour in the mark.
     Use it where type is meant to read as the SAME gold as the logo rather than as the
     palette's accent. Note it is DARKER than --gold-bright (relative luminance 0.349 vs
     0.417), so it is harder to read, not easier, and anything using it over photography
     needs its own legibility treatment. */
  --gold-mark:   #cb9700;

  /* THE HALO FOR TYPE OVER PHOTOGRAPHY, extracted 2026-08-27 so it is defined once.
     The home hero had these three stacked shadows from the start; the story hero was given a
     different treatment on a different night and the two silently diverged, which is precisely
     the drift this token exists to stop. Three layers on purpose: a tight 2px to hold the
     letterform edge, a 4px offset down for a sense of lift, and a wide 16px to separate the word
     from a busy ground. Any new type placed over an image should use this rather than invent
     its own. */
  --halo-on-photo: 0 0 2px color-mix(in oklch, var(--near-black) 80%, transparent), 0 1px 4px color-mix(in oklch, var(--near-black) 88%, transparent), 0 2px 16px color-mix(in oklch, var(--near-black) 58%, transparent);
  --gold-deep:   oklch(0.50 0.12 72);

  /* Ink & light surfaces */
  --ink:        oklch(0.27 0.008 85);
  --ink-soft:   oklch(0.46 0.012 85);
  --cream:      oklch(0.965 0.018 85);
  --cream-panel:oklch(0.935 0.022 85);
  --hairline:   oklch(0.84 0.014 85);

  /* Dark surfaces */
  --near-black:  oklch(0.185 0.006 80);
  --near-black-2:oklch(0.235 0.008 80);
  --bone:        oklch(0.95 0.012 85);
  --bone-soft:   oklch(0.80 0.013 85);

  /* Accent */
  --oxblood:    oklch(0.40 0.075 30);

  /* Type */
  --font-display: "Cormorant Garamond", Georgia, "Times New Roman", serif;
  --font-body: "Hanken Grotesk", "Segoe UI", system-ui, sans-serif;

  --step--1: 0.78rem;
  --step-0: 1.0625rem;
  --step-1: clamp(1.2rem, 0.95rem + 0.95vw, 1.5rem);
  --step-2: clamp(1.6rem, 1.05rem + 1.9vw, 2.35rem);
  --step-3: clamp(2.4rem, 1.35rem + 4vw, 4.1rem);
  --step-4: clamp(3rem, 1.4rem + 6vw, 5.5rem);
  --hero: clamp(3.2rem, 1.4rem + 8vw, 7.25rem);

  /* Spacing (4pt) */
  --space-2xs: 4px; --space-xs: 8px; --space-sm: 12px; --space-md: 16px;
  --space-lg: 24px; --space-xl: 32px; --space-2xl: 48px; --space-3xl: 72px;
  --space-4xl: 112px; --space-5xl: 160px;
  --section-y: clamp(72px, 10vw, 150px);

  /* Shape & motion */
  /* Persistent action bar chrome: 52px button + 12px padding x2 + 1px rule. Lives here
     because both the bar itself and the focus scroll-margin that clears it need the number. */
  --action-bar-h: 77px;

  --radius: 2px;
  --wrap: 1240px;
  --ease-lux: cubic-bezier(0.32, 0.72, 0, 1);
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --dur-fast: 240ms; --dur-mid: 520ms; --dur-slow: 760ms; --dur-cine: 1100ms;
}

/* ---------- Reset / base ---------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; background: var(--near-black); }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  font-family: var(--font-body);
  font-size: var(--step-0);
  line-height: 1.62;
  color: var(--ink);
  background: var(--near-black);
  font-feature-settings: "kern", "liga", "calt";
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

img { display: block; max-width: 100%; height: auto; }
picture { display: contents; }
a { color: inherit; text-decoration: none; }
a, button { -webkit-tap-highlight-color: transparent; }

::selection { background: var(--gold); color: var(--near-black); }
:focus-visible { outline: 2px solid var(--gold-deep); outline-offset: 3px; border-radius: 1px; }

/* ---------- Film-grain texture (fixed, non-interactive) ---------- */
body::after {
  content: "";
  position: fixed; inset: 0; z-index: 90;
  pointer-events: none;
  opacity: 0.045;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 180px 180px;
  mix-blend-mode: soft-light;
}

/* ---------- Layout ---------- */
.wrap { width: 100%; max-width: var(--wrap); margin-inline: auto; padding-inline: clamp(22px, 5.5vw, 72px); }
.section { padding-block: var(--section-y); }

.skip-link {
  position: fixed; left: 50%; top: var(--space-sm); z-index: 200;
  translate: -50% -200%; opacity: 0; pointer-events: none;
  background: var(--ink); color: var(--cream);
  padding: var(--space-sm) var(--space-lg); border-radius: var(--radius);
  transition: translate var(--dur-mid) var(--ease-lux), opacity var(--dur-mid) var(--ease-lux);
}
.skip-link:focus { translate: -50% 0; opacity: 1; pointer-events: auto; }
.skip-link:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }

/* ---------- Shared type ---------- */
.eyebrow {
  display: inline-flex; align-items: center; gap: var(--space-sm);
  font-size: var(--step--1); font-weight: 600;
  letter-spacing: 0.28em; text-transform: uppercase;
  color: var(--ink-soft);
}
.eyebrow::before {
  content: ""; width: clamp(24px, 4vw, 44px); height: 1px;
  background: currentColor; opacity: 0.5;
}
.eyebrow--gold { color: var(--gold-deep); }

.display {
  font-family: var(--font-display);
  font-weight: 600; line-height: 1.04; letter-spacing: -0.015em;
  font-optical-sizing: auto;
  text-wrap: balance;
}
.display em { font-style: italic; font-weight: 500; }
.range__title em, .craft__title em { color: var(--gold-deep); }
/* .craft is a dark section, so its italic accent takes the bright gold instead. */
.craft .craft__title em { color: var(--gold-bright); }

/* Colour inherits the ground it sits on: ink on cream, bone on the dark sections.
   Hardcoding var(--ink) put "Read the full story" at 1.24:1 on the dark heritage band. */
.link-underline {
  display: inline-flex; align-items: center; gap: 8px;
  font-weight: 600; color: inherit; padding-bottom: 3px;
  background-image: linear-gradient(var(--gold), var(--gold));
  background-size: 0% 1.5px; background-position: 0 100%; background-repeat: no-repeat;
  transition: background-size var(--dur-mid) var(--ease-lux);
}
.link-underline .arrow { transition: translate var(--dur-mid) var(--ease-lux); }
.link-underline:hover, .link-underline:focus-visible { background-size: 100% 1.5px; }
.link-underline:hover .arrow { translate: 5px 0; }
.link-underline:active { opacity: 0.6; }

/* ---------- Buttons (editorial, crisp: with nested icon) ---------- */
.btn {
  /* One off-white across every button, 2026-08-27. --gold now uses --bone, so the base moved
     off --cream to match: two near-identical off-whites on the same component is drift, not a
     decision, and the difference (L .965 vs .95) is invisible while the inconsistency is real. */
  --btn-bg: var(--ink); --btn-fg: var(--bone); --btn-bd: var(--ink);
  position: relative;
  display: inline-flex; align-items: center; gap: var(--space-md);
  font-family: var(--font-body); font-size: 1rem; font-weight: 600; letter-spacing: 0.01em;
  padding: 0.95em 1.1em 0.95em 1.6em;
  border: 1px solid var(--btn-bd); border-radius: var(--radius);
  background: var(--btn-bg); color: var(--btn-fg);
  cursor: pointer; will-change: transform;
  transition: background var(--dur-fast) var(--ease-lux), color var(--dur-fast) var(--ease-lux),
              border-color var(--dur-fast) var(--ease-lux), scale var(--dur-fast) var(--ease-lux),
              transform 280ms var(--ease-lux);
}
.btn:active { scale: 0.96; }
.btn__icon {
  display: inline-grid; place-items: center;
  width: 30px; height: 30px; border-radius: 999px;
  background: color-mix(in oklch, var(--btn-fg) 16%, transparent);
  transition: translate var(--dur-mid) var(--ease-lux), background var(--dur-fast) var(--ease-lux);
}
.btn__icon svg { width: 14px; height: 14px; }
@media (hover: hover) { .btn:hover .btn__icon { translate: 4px 0; background: color-mix(in oklch, var(--btn-fg) 28%, transparent); } }
/* WHITE ON GOLD, operator's decision 2026-08-27 after seeing all four options rendered
   side by side on the hero photograph at phone size.
   THIS IS A DOCUMENTED, DELIBERATE CONTRAST DEVIATION. Measured, not estimated:
     near-black on gold (previous)   6.40:1   passes
     white on gold      (this)       2.92:1   FAILS both 4.5:1 and the 3:1 large-text floor
   Uses var(--bone), the site's own warm off-white, NOT pure white: the premium floor bans it
   on a warm palette and the deploy gate rejects it outright (design-slop pure-black-white).
   NOTE for the next reader: that lint is a regex over the SERVED css and does not skip
   comments, so writing the literal hex in a comment trips it even when the code is clean.
   Recorded in _briefs/STANDARDS_OVERRIDES.md with the reasoning and the rejected alternatives.
   Do NOT "fix" this back to near-black in a later pass without asking the operator: it is a
   choice that was made with the numbers in front of him, not an oversight.
   The two alternatives that keep white text AND pass are both still on the table if this is
   revisited: deepening the gold to about 78% (#977000, 4.53:1, visually near-identical at this
   size), or inverting to a near-black fill with a gold edge (18.69:1).
   Applied to the class rather than one button on purpose: six gold buttons ship across five
   pages, and two different treatments of the same component would read as a bug. */
.btn--gold { --btn-bg: var(--gold); --btn-fg: var(--near-black); --btn-bd: var(--gold); }
.btn--gold:hover,
.btn--gold:focus-visible,
.btn--gold:active { --btn-fg: var(--near-black); }
.btn--gold:hover { --btn-bg: var(--gold-bright); --btn-bd: var(--gold-bright); }
.btn--ink:hover { --btn-bg: transparent; --btn-fg: var(--ink); }
.btn--ghost-light { --btn-bg: transparent; --btn-fg: var(--bone); --btn-bd: color-mix(in oklch, var(--bone) 45%, transparent); }
.btn--ghost-light:hover { --btn-bg: var(--bone); --btn-fg: var(--near-black); --btn-bd: var(--bone); }

/* ---------- Framed-print imagery (mat + offset gold rule, no shadow) ---------- */
.framed { position: relative; display: block; }
.framed__accent {
  position: absolute; inset: 0; z-index: 0;
  border: 1px solid var(--gold);
  translate: 18px 18px;
  transition: translate var(--dur-slow) var(--ease-lux);
}
.framed img { position: relative; z-index: 1; border-radius: var(--radius); }
@media (hover: hover) { .framed:hover .framed__accent { translate: 12px 12px; } }
figcaption {
  position: relative; z-index: 1;
  font-size: var(--step--1); letter-spacing: 0.04em; color: var(--ink-soft);
  margin-top: var(--space-md);
}

/* ============================================================
   Header / nav, floating, refined, solid (no glass)
   ============================================================ */
.site-header {
  position: fixed; inset: 0 0 auto 0; z-index: 100;
}
/* Cream backing: clip-wipes UP the bar tracking the exact hero→cream content edge
   (--bclip = px clipped off the top, set per scroll frame). A path reveal, not a uniform fade. */
.site-header::after {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background: var(--cream); border-bottom: 1px solid var(--hairline);
  clip-path: inset(var(--bclip, 999px) 0 0 0);
}
/* Legibility scrim: clipped to the hero side of the content edge (--scrimclip px off the bottom),
   so the gradient never spills below the bar onto the cream content */
.site-header::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 240%; z-index: -2;
  pointer-events: none;
  background: linear-gradient(to bottom, color-mix(in oklch, var(--near-black) 66%, transparent), transparent);
  clip-path: inset(0 0 var(--scrimclip, 0) 0);
}
/* SAFE AREAS, added 2026-08-27 alongside viewport-fit=cover in the <head>.
   Cover mode is what lets the page paint edge to edge so iOS Safari's chrome blends into it
   instead of sitting on a white slab. The cost is that it ALSO paints under the Dynamic Island,
   the notch and the rounded corners, and this header is position:fixed at inset 0, so without
   these insets the wordmark and the cleaver would sit underneath the island on every modern
   iPhone. env() resolves to 0px on every device that has no inset and in every desktop browser,
   so this is inert everywhere it is not needed. The inline insets matter in landscape, where the
   notch eats the left or right edge depending on which way the phone is turned. */
.site-header__inner {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-lg);
  padding-block: clamp(16px, 2.2vw, 26px);
  padding-top: calc(clamp(16px, 2.2vw, 26px) + env(safe-area-inset-top));
  /* ADD the inset to .wrap's padding, never replace it. This element is class="wrap
     site-header__inner": .wrap supplies padding-inline: clamp(22px, 5.5vw, 72px), and this rule
     sits later in the file at the same specificity, so a bare `padding-left: env(...)` WINS and
     resolves to 0px in portrait, where there is no inset. That silently deleted the header's side
     padding on every phone and clipped the cleaver off the right edge. Shipped 2026-08-27, caught
     by the operator on a real device the same day. The clamp is repeated here rather than
     inherited because there is no way to reference the value .wrap already computed. */
  padding-left: calc(clamp(22px, 5.5vw, 72px) + env(safe-area-inset-left));
  padding-right: calc(clamp(22px, 5.5vw, 72px) + env(safe-area-inset-right));
}
/* header bg + border are scroll-linked via --sp on .site-header::after; .is-scrolled now only flips text/logo */

.brand { display: flex; align-items: center; line-height: 1; }
.brand__logo {
  height: clamp(44px, 5vw, 66px); width: auto;
  filter: drop-shadow(0 0 3px rgba(20,19,18, calc(0.85 * var(--shadow, 1)))) drop-shadow(0 2px 11px rgba(20,19,18, calc(0.5 * var(--shadow, 1))));
}
/* logo shadow is scroll-linked via --sp above: fades out as the header creams in */

.nav__menu { list-style: none; display: flex; align-items: center; gap: clamp(20px, 2.6vw, 42px); padding: 0; }
.nav__menu > li > a {
  font-size: 1rem; font-weight: 600;
  color: color-mix(in oklch, var(--bone), var(--ink) calc((66px - var(--bclip, 999px)) / 16px * 100%));
  text-shadow: 0 1px 12px color-mix(in oklch, var(--near-black) calc(clamp(0, (var(--bclip, 999px) - 50px) / 16px, 1) * 55%), transparent);
  position: relative; padding-block: 4px;
}
.site-header.is-scrolled .nav__menu > li > a { font-weight: 500; }
.nav__menu > li > a::after {
  content: ""; position: absolute; left: 0; bottom: 0; width: 100%; height: 1.5px;
  background: var(--gold); scale: 0 1; transform-origin: left; transition: scale var(--dur-mid) var(--ease-lux);
}
.nav__menu > li > a:hover::after { scale: 1 1; }

.nav__menu-call a {
  color: color-mix(in oklch, var(--bone), var(--ink) calc((66px - var(--bclip, 999px)) / 16px * 100%)) !important;
  border: 1px solid color-mix(in oklch, var(--gold-bright) calc(var(--shadow, 1) * 100%), var(--gold-deep));
  border-radius: 999px; padding: 0.55em 1.15em !important; font-weight: 600;
}
.nav__menu-call a::after { display: none; }
.nav__menu-call a:hover { background: var(--gold); color: var(--near-black) !important; border-color: var(--gold); }

.nav-toggle { display: none; place-items: center; width: 56px; height: 56px; padding: 0; background: none; border: none; cursor: pointer; z-index: 110; color: var(--gold); border-radius: 50%; -webkit-tap-highlight-color: transparent; filter: drop-shadow(0 0 4px rgba(20,19,18, calc(0.85 * var(--shadow, 1)))) drop-shadow(0 3px 14px rgba(20,19,18, calc(0.5 * var(--shadow, 1)))); transition: transform 140ms var(--ease-lux); }
.nav-toggle:active { transform: scale(0.96); }
.nav-toggle:focus { outline: none; }
.nav-toggle:focus-visible { outline: 2px solid var(--gold-deep); outline-offset: 2px; }
.nav-toggle svg { width: 67px; height: 78px; overflow: visible; transform: translateY(9px); }
.nav-toggle .cleaver { transform-box: view-box; transform-origin: 79px 61px; transform: rotate(90deg); will-change: transform; }

@media (max-width: 860px) {
  /* centered logo with the cleaver toggle pinned right */
  .site-header__inner { display: grid; grid-template-columns: 1fr auto 1fr; gap: var(--space-sm); }
  .brand { grid-column: 2; justify-self: center; }
  .nav-toggle { grid-column: 3; grid-row: 1; justify-self: end; display: grid; margin-right: -6px; }

  /* Compact dropdown: collapse the padding-less .nav wrapper so closed = 0 height (no band over the logo) */
  .nav {
    position: fixed; top: 0; left: 0; right: 0; z-index: 99;
    max-height: 0; overflow: hidden;
    /* visibility:hidden pulls the collapsed links out of the tab order. Without it
       a keyboard user tabs through five clipped, off-screen links before the hero. */
    visibility: hidden;
    transition: max-height 480ms var(--ease-lux), visibility 0s linear 480ms;
  }
  .nav-open .nav { max-height: 480px; visibility: visible; transition: max-height 480ms var(--ease-lux), visibility 0s; }
  .nav__menu {
    flex-direction: column; align-items: stretch; gap: 0;
    background: var(--cream);
    padding: 80px 0 max(10px, env(safe-area-inset-bottom));
    border-bottom: 1px solid var(--hairline);
  }
  .nav__menu > li > a, .site-header .nav__menu > li > a {
    display: block; padding: 15px clamp(24px, 6vw, 40px);
    font-family: var(--font-body); font-size: 1.1rem; font-weight: 500;
    color: var(--ink); text-shadow: none;
    border-top: 1px solid var(--hairline);
  }
  .nav__menu > li:first-child > a { border-top: none; }
  .nav__menu > li > a::after { display: none; }
  .nav__menu > li > a:active { background: color-mix(in oklch, var(--ink) 7%, transparent); }

  .nav__menu-call { padding: 14px clamp(24px, 6vw, 40px) 4px; border-top: 1px solid var(--hairline); }
  .nav__menu-call a {
    display: inline-flex; padding: 0.6em 1.2em !important;
    color: var(--ink) !important; border: 1px solid var(--hairline); border-radius: 999px;
    font-family: var(--font-body); font-size: 1rem;
  }

  /* solid header while open, so the panel reads as a clean dropdown */
  .nav-open .site-header::after { clip-path: inset(0 0 0 0); }
  .nav-open .site-header::before { opacity: 0; }
  .nav-open .nav-toggle { filter: drop-shadow(0 0 4px color-mix(in oklch, var(--near-black) 38%, transparent)); }
  /* logo always above the dropdown (so it doesn't hide behind the collapsing panel on close) */
  .brand { position: relative; z-index: 110; }
  .nav-open .brand__logo { filter: drop-shadow(0 0 3px transparent) drop-shadow(0 1px 3px color-mix(in oklch, var(--near-black) 22%, transparent)); }
}

/* ============================================================
   Hero
   ============================================================ */
.hero {
  position: relative; min-height: 100svh; display: flex; align-items: flex-end;
  padding-bottom: clamp(80px, 13vh, 160px);
  color: var(--bone); overflow: hidden; isolation: isolate;
}
.hero__media { position: absolute; inset: 0; z-index: -2; overflow: hidden; background: var(--near-black); animation: heroZoom 16s var(--ease-out) forwards; }
.hero__img { position: absolute; left: 0; top: -17%; width: 100%; height: 134%; object-fit: cover; object-position: 14% 50%; will-change: transform; }
.hero__scrim {
  position: absolute; inset: 0; z-index: -1;
  background:
    linear-gradient(to top, color-mix(in oklch, var(--near-black) 96%, transparent) 0%,
                    color-mix(in oklch, var(--near-black) 80%, transparent) 32%,
                    color-mix(in oklch, var(--near-black) 56%, transparent) 56%,
                    color-mix(in oklch, var(--near-black) 28%, transparent) 76%, transparent 94%),
    linear-gradient(92deg, color-mix(in oklch, var(--near-black) 84%, transparent) 0%,
                    color-mix(in oklch, var(--near-black) 52%, transparent) 46%, transparent 82%);
}
.hero__eyebrow { color: oklch(0.85 0.115 88); text-shadow: var(--halo-on-photo); }
.hero__eyebrow::before { background: var(--gold-bright); opacity: 0.85; }
.hero__title {
  font-family: var(--font-display); font-weight: 600; line-height: 0.98; letter-spacing: -0.015em;
  font-size: var(--hero); max-width: 13ch; font-optical-sizing: auto;
  text-shadow: 0 2px 28px color-mix(in oklch, var(--near-black) 42%, transparent);
}
.hero__title em { font-style: italic; color: var(--gold-bright); }
.hero__lede { font-size: var(--step-1); line-height: 1.5; color: var(--bone-soft); max-width: 44ch; margin-top: var(--space-xs); text-shadow: 0 1px 16px color-mix(in oklch, var(--near-black) 48%, transparent); }
.hero__actions { display: flex; flex-wrap: wrap; gap: var(--space-md); margin-top: var(--space-lg); }

.hero__scroll { position: absolute; left: 50%; bottom: var(--space-xl); translate: -50% 0; width: 1px; height: 64px; overflow: hidden; }
.hero__scroll-line { display: block; width: 1px; height: 100%; background: color-mix(in oklch, var(--bone) 30%, transparent); position: relative; overflow: hidden; }
.hero__scroll-line::after { content: ""; position: absolute; inset: 0; background: var(--gold-bright); animation: scrollPulse 2.6s var(--ease-lux) infinite; }

@keyframes heroZoom { from { scale: 1.1; } to { scale: 1; } }
@keyframes scrollPulse { 0% { translate: 0 -100%; } 55%, 100% { translate: 0 100%; } }

/* ---- Layered asymmetric hero (oversized headline + meta rail) ---- */
.hero--layered { align-items: stretch; padding-bottom: 0; }
.hero--layered .hero__grid {
  width: 100%; max-width: var(--wrap); margin-inline: auto;
  padding-inline: clamp(22px, 5.5vw, 72px);
  padding-top: clamp(120px, 17vh, 168px); padding-bottom: clamp(72px, 12vh, 140px);
  display: grid; grid-template-columns: 1fr auto; align-items: end; gap: clamp(28px, 5vw, 88px);
}
@media (min-width: 821px) {
  .hero--layered .hero__grid {
    padding-top: calc(clamp(120px, 17vh, 168px) - 5px);
    padding-bottom: calc(clamp(72px, 12vh, 140px) - 5px);
  }
}
.hero--layered .hero__content { display: grid; gap: var(--space-md); max-width: 18ch; align-self: end; }
/* MEASURE, retuned 2026-08-26 when the H1 changed from "Purveyors of fine meats." (24 chars)
   to "Dry-aged beef, house sausages." (30). The type SCALE is deliberately untouched: this pass
   is a re-arrangement, and the oversized headline is the hero.
   What changed is the measure, and the reason is worth writing down because it is not obvious
   from reading the CSS. `.hero__content` is capped at `18ch` of the BODY font, which computes to
   about 171px, so the headline was never sized by its container at all , as a grid item with
   `min-width: auto` it was floored at its own min-content width, i.e. the widest single word.
   With `max-width: 11ch` that produced a box of roughly 300-490px at every desktop width, and
   the new 30-character headline rendered FIVE lines at 1440px. Rendered and counted, not
   calculated; `max-width` alone could not fix it because max-width only ever caps a box, it
   cannot widen one that is being sized by min-content.
   So the desktop rule below gives the headline a real width. Measured result across
   821 / 1024 / 1280 / 1440 / 1920: three lines, matching the behaviour that was already
   reviewed and accepted for this hero, and clear of the meta rail at every one of them.
   The clamp FLOOR moved 3.8rem -> 3.4rem, and it only binds below a 398px viewport. At the old
   floor the headline held two lines at 390px but went to three at 375 and at 360, and 375 is
   the iPhone SE width the standards name. Rendered and counted at 320 / 344 / 360 / 375 / 390 /
   412 / 430: two lines from 344px up, three at 320px, which is narrower than any phone this
   shop's customers are carrying. Two lines at 390px is the rule that binds
   (craft/typography.md, the 2-line H1 iron rule). */
.hero--layered .hero__title {
  font-weight: 600; font-size: clamp(3.4rem, 1rem + 11vw, 9.5rem);
  line-height: 0.9; letter-spacing: -0.02em; margin: 0; max-width: 13ch;
  text-wrap: balance;
}
@media (min-width: 821px) {
  /* 56vw, not a flat 700px: at 821px the meta rail's left edge leaves only about 468px of
     room, and a fixed 700px headline would run underneath it. */
  .hero--layered .hero__title { width: min(700px, 56vw); max-width: none; }
}
.hero--layered .hero__title em { font-weight: 600; }
.hero__rule { width: clamp(56px, 7vw, 120px); height: 2px; background: var(--gold); margin-block: var(--space-2xs); }
.hero--layered .hero__lede { max-width: 34ch; margin-top: 0; }
.hero--layered .hero__actions { margin-top: var(--space-sm); justify-content: flex-end; }
.hero--layered .hero__actions .btn { justify-content: space-between; min-width: 252px; }
.hero__meta {
  align-self: end; display: grid; gap: var(--space-xl); padding-left: var(--space-xl);
  border-left: 1px solid color-mix(in oklch, var(--bone) 32%, transparent);
  min-width: 190px; text-shadow: 0 1px 14px color-mix(in oklch, var(--near-black) 80%, transparent);
}
.hero__meta .meta { display: grid; gap: 6px; }
/* Icons beside the two rail labels, added 2026-08-27 (operator). The label is letter-spaced
   uppercase at a small size, so the glyph has to be pinned rather than left to sit on the text
   baseline: inline-flex with a fixed 1em box keeps the icon optically centred against the caps
   height at every clamp step, and the 0.24em tracking is cancelled on the icon itself, otherwise
   the letter-spacing adds a phantom quarter-em to its right edge and the gap reads uneven.
   `aria-hidden` on both: the label text already says "Open" and "Find us", so an accessible name
   here would be read out twice. */
.hero__meta .meta__label { font-size: var(--step--1); letter-spacing: 0.24em; text-transform: uppercase; color: var(--gold-bright);
  display: inline-flex; align-items: center; gap: 0.6em; }
.hero__meta .meta__icon { width: 1.15em; height: 1.15em; flex: none; letter-spacing: normal; opacity: 0.9; }
.hero__meta .meta__val { font-size: 0.98rem; line-height: 1.45; color: var(--bone); }
.hero__meta .meta__val a { transition: color var(--dur-fast) var(--ease-lux); }
.hero__meta .meta__val a:hover { color: var(--gold-bright); }
.hero--layered .hero__scrim {
  background:
    linear-gradient(to top, color-mix(in oklch, var(--near-black) 96%, transparent) 0%,
      color-mix(in oklch, var(--near-black) 72%, transparent) 30%,
      color-mix(in oklch, var(--near-black) 40%, transparent) 56%, transparent 82%),
    linear-gradient(92deg, color-mix(in oklch, var(--near-black) 80%, transparent) 0%,
      color-mix(in oklch, var(--near-black) 42%, transparent) 38%, transparent 66%),
    linear-gradient(270deg, color-mix(in oklch, var(--near-black) 45%, transparent) 0%, transparent 24%);
}
@media (max-width: 820px) {
  .hero--layered .hero__grid { grid-template-columns: 1fr; padding-top: clamp(108px, 15vh, 140px); }
  .hero--layered .hero__content { max-width: none; }
  /* Centred on phones, changed 2026-08-27 (operator). It used to be right-aligned, which matched
     the older two-button stack. With "Get directions" out of the hero there is one button left,
     and a single call-to-action hanging off the right edge under left-aligned type reads as a
     leftover rather than a decision. Centred, it is unmistakably the thing to press. The desktop
     rule still right-aligns, because there the button sits beneath a right-hand meta rail and the
     alignment is doing real work. */
  .hero--layered .hero__actions { flex-direction: column; align-items: center; }
  .hero--layered .hero__actions .btn { width: auto; min-width: 240px; }

  /* THE FIRST SCREEN IS THE PITCH, changed 2026-08-27 (operator).
     On a phone the grid is one column, so the hours/address/phone rail sat immediately under the
     Call button and landed inside the opening viewport. That put the shop's admin details in the
     same glance as the headline and the ask, and the eye has nowhere to rest.
     So the content block is given a screen of its own: eyebrow, headline, lede, Call. The rail
     starts below the fold and is the reward for the first scroll.
     `svh`, not `vh` or `dvh`, and the choice matters on exactly this element. `vh` on iOS is the
     viewport with the browser chrome HIDDEN, so at rest it under-measures and the rail creeps back
     up into view. `dvh` changes as the chrome collapses on scroll, which would re-flow the hero
     mid-gesture. `svh` is the smallest state, chrome visible, which is what the page looks like
     on load, so this holds at rest and never moves while scrolling.
     The subtraction is the grid's own top padding, otherwise the block is a full screen tall
     PLUS the padding and the button is pushed off the bottom.
     `align-content: space-between` is what "spread out" means here: the gap below is a floor, and
     any screen taller than the content distributes the surplus between the four elements rather
     than piling it under the button. On a tall phone the hero opens up; on a short one it falls
     back to the gap and nothing collides. */
  .hero--layered .hero__content {
    min-height: calc(100svh - clamp(108px, 15vh, 140px) - var(--space-2xl));
    align-content: space-between;
    gap: var(--space-lg);
  }
  /* The gap, not the content height, is what puts the rail below the fold. The content block is
     `align-content: space-between`, so the Call button is pinned to the BOTTOM of that block:
     growing the block moves the button down with it and buys nothing, and pushed far enough it
     puts the button itself under the fold, which would be a worse bug than the one being fixed.
     MEASURED before this line existed, five real phone viewports, and the rail was landing
     20 to 28px INSIDE the screen every time: SE 647/667, 13 mini 609/629, 15 714/734,
     15 Pro Max 776/796, Pixel 7 712/732. Consistent, because the old gap was a flat token while
     the shortfall scales with the screen. So the gap scales too. */
  .hero--layered .hero__grid { gap: clamp(76px, 13svh, 148px); padding-bottom: var(--space-3xl); }
  .hero__meta {
    border-left: none; padding-left: 0; padding-top: var(--space-lg);
    border-top: 1px solid color-mix(in oklch, var(--bone) 26%, transparent);
    grid-template-columns: 1fr 1fr; gap: var(--space-lg); min-width: 0;
  }
  .hero__meta .meta:nth-child(3) { grid-column: 1 / -1; }
  .hero__scroll { display: none; }
}

/* (The "Welcome / intro" section was removed from the home page on 2026-08-26 in the
   information-architecture pass, and it was the only page that carried it. Its rules are
   deleted rather than left behind: unreferenced CSS is shelfware that the next pass has to
   read and discount. `.welcome__title em` was also dropped from the shared `.display em`
   gold rule above. Recover from git if the block is ever wanted back.) */

/* ============================================================
   Heritage / our story
   ============================================================ */
.heritage { background: var(--near-black); color: var(--bone); padding-block: var(--section-y); position: relative; isolation: isolate; }
.heritage__grid { display: grid; gap: clamp(40px, 6vw, 96px); grid-template-columns: 1fr; align-items: center; }
@media (min-width: 900px) { .heritage__grid { grid-template-columns: 0.82fr 1.18fr; } }
.heritage__figure img { width: 100%; max-width: 460px; aspect-ratio: 1; object-fit: cover; outline: 1px solid color-mix(in oklch, var(--bone) 12%, transparent); outline-offset: -1px; }
.heritage__figure .framed__accent { border-color: color-mix(in oklch, var(--gold) 80%, transparent); }
.heritage__figure figcaption { color: var(--bone-soft); }
.heritage__text { display: grid; gap: var(--space-lg); max-width: 50ch; }
.heritage__title { font-size: var(--step-3); }
.heritage__text p { color: var(--bone-soft); line-height: 1.7; }
.heritage__text em { color: var(--gold-bright); font-style: italic; }

/* Cream variant, used on the home page from 2026-08-26. The page's one dark band moved to
   `.craft` (the price answer), so the story beat below it sits on cream and the vintage
   Nicolas photograph reads as a framed print rather than a bleed. */
.heritage--light { background: var(--cream); color: var(--ink); }
.heritage--light .heritage__text p { color: var(--ink-soft); }
.heritage--light .heritage__text em { color: var(--gold-deep); }
.heritage--light .heritage__title em { color: var(--gold-deep); }
.heritage--light .heritage__figure img { outline-color: color-mix(in oklch, var(--ink) 10%, transparent); }
.heritage--light .heritage__figure figcaption { color: var(--ink-soft); }

/* ============================================================
   The Range, editorial index list
   ============================================================ */
.range { background: var(--cream); }
/* Opener: heading block plus the counter photograph. The home page leads with the goods from
   2026-08-26, so this section needed one image to read as a shop window rather than an index.
   The photo is the already-optimised cabinet shot the Range page uses; no new bytes were
   introduced beyond a second reference to a file the site already ships. */
.range__opener { display: grid; gap: clamp(36px, 5vw, 72px); grid-template-columns: 1fr; align-items: end; margin-bottom: var(--space-3xl); }
@media (min-width: 900px) { .range__opener { grid-template-columns: 1fr 0.82fr; } }
.range__figure img { width: 100%; aspect-ratio: 860 / 687; object-fit: cover; }
.range__head { max-width: 56ch; display: grid; gap: var(--space-lg); }
.range__title { font-size: var(--step-3); }
.range__intro { color: var(--ink-soft); font-size: var(--step-1); }
.range__list { list-style: none; padding: 0; display: grid; gap: 0; border-top: 1px solid var(--hairline); margin-bottom: var(--space-3xl); }
.range__item {
  display: grid; gap: var(--space-sm); grid-template-columns: 1fr;
  padding-block: var(--space-2xl); border-bottom: 1px solid var(--hairline);
}
/* Motion fix 2026-09-13: this row also carries [data-reveal], whose own transition shorthand
   (declared later in this file, for the on-scroll entrance) fully replaces any transition set
   here at equal specificity, so the padding-left hover indent was landing with ZERO duration ,
   an instant snap, which read as the whole Range section hovering "too snappy". Re-declared on
   the combined selector below, which outranks the plain [data-reveal] rule, so it always wins
   regardless of file order. */
.range__item[data-reveal] {
  transition: padding-left var(--dur-fast) var(--ease-lux),
              opacity var(--dur-cine) var(--ease-lux), translate var(--dur-cine) var(--ease-lux),
              filter var(--dur-cine) var(--ease-lux);
}
.range__num { font-family: var(--font-display); font-size: calc(var(--step-1) + 4px); font-weight: 600; letter-spacing: 0; line-height: 1; color: var(--gold-deep); font-variant-numeric: tabular-nums; }
@media (min-width: 800px) {
  .range__item { grid-template-columns: 3rem 0.4fr 0.6fr; gap: var(--space-2xl); align-items: baseline; }
  .range__item:hover { padding-left: var(--space-md); }
}
.range__item-title { font-family: var(--font-display); font-weight: 600; font-size: var(--step-2); line-height: 1.08; }
.range__item p { color: var(--ink-soft); max-width: 50ch; }
.range__item em { font-style: italic; color: var(--gold-deep); }

/* Each row deep-links to its own section on /the-range. ONE anchor per row, stretched over
   the whole row by ::after, so the row is a 44px-plus target on a phone without nesting a
   second link inside a link. The stretch is scoped to the row via `position: relative` on
   `.range__item`; `.range__num` is lifted above it so the numeral is not swallowed. */
.range__item { position: relative; }
.range__link { color: inherit; }
.range__link::after { content: ""; position: absolute; inset: 0; }
/* No `outline: none` here on purpose. The global `:focus-visible` ring at the top of this file
   has lower specificity than `.range__link:focus`, so suppressing the link's own ring in
   favour of a `:has()`-driven row outline would leave zero focus indicator on any engine
   without `:has()`. The title text carries the ring; that is enough and it cannot fail. */
@media (hover: hover) {
  .range__link { background-image: linear-gradient(var(--gold), var(--gold)); background-size: 0% 1.5px; background-position: 0 100%; background-repeat: no-repeat; padding-bottom: 2px; transition: background-size var(--dur-fast) var(--ease-lux); }
  .range__item:hover .range__link { background-size: 100% 1.5px; }
}

.range__foot { display: grid; justify-items: center; gap: var(--space-md); text-align: center; }
.range__foot .btn { width: max-content; }
.range__foot-note { color: var(--ink-soft); font-size: 15px; line-height: 1.6; letter-spacing: 0.02em; max-width: 46ch; }

/* ============================================================
   Why it costs what it costs , the home page's dark band and its thesis.
   Two columns on desktop: the claim on the left, its evidence on the right, so the
   heading is read before the four facts rather than sitting on top of a wall of them.
   A hairline-ruled list, not cards: the brand has no card chrome anywhere and this
   section is not the place to introduce it.
   ============================================================ */
.craft { background: var(--near-black); color: var(--bone); padding-block: var(--section-y); position: relative; isolation: isolate; }
/* `.eyebrow--gold` ships `--gold-deep`, which is the accessible gold for small text on CREAM.
   On this dark ground it measured 3.07:1 against the near-black, and an eyebrow is 12.5px
   uppercase, so it needs the 4.5:1 body threshold, not the large-text one. `--gold-bright`
   is the dark-ground gold (THEME_DNA §1) and it is what every other dark section uses. */
.craft .eyebrow--gold { color: var(--gold-bright); }
.craft__inner { display: grid; gap: clamp(40px, 5vw, 88px); grid-template-columns: 1fr; align-items: start; }
@media (min-width: 900px) {
  .craft__inner { grid-template-columns: 0.82fr 1.18fr; column-gap: clamp(56px, 7vw, 112px); }
  .craft__head { position: sticky; top: clamp(96px, 13vh, 132px); }
}
.craft__head { display: grid; gap: var(--space-lg); max-width: 34ch; }
.craft__title { font-size: var(--step-3); }
.craft__lede { color: var(--bone-soft); line-height: 1.7; }

.craft__list { list-style: none; padding: 0; display: grid; gap: 0; border-top: 1px solid color-mix(in oklch, var(--bone) 20%, transparent); }
.craft__item { display: grid; gap: var(--space-sm); padding-block: var(--space-2xl); border-bottom: 1px solid color-mix(in oklch, var(--bone) 14%, transparent); }
.craft__item-title { font-family: var(--font-display); font-weight: 600; font-size: var(--step-2); line-height: 1.1; color: var(--bone); }
.craft__item p { color: var(--bone-soft); line-height: 1.7; max-width: 54ch; }

/* The award is the receipt, not the headline: same quiet treatment it gets in the footer. */
.craft__award { margin-top: var(--space-xl); font-size: 15px; line-height: 1.6; color: var(--bone-soft); }
.craft__award strong { display: block; color: var(--gold-bright); font-weight: 600; font-size: 0.64rem; letter-spacing: 0.22em; text-transform: uppercase; margin-bottom: 5px; }

/* ============================================================
   Visit
   ============================================================ */
.visit { background: var(--cream-panel); padding-block: var(--section-y); }
.visit__actions { display: flex; flex-wrap: wrap; gap: var(--space-md); }
.visit__inner { display: grid; gap: clamp(28px, 3.4vw, 44px); }
.visit__head { display: grid; gap: var(--space-md); }
.visit__title { font-size: var(--step-3); max-width: 20ch; }
.visit__contact { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-sm) var(--space-md); color: var(--ink-soft); }
.visit__sep { color: var(--gold-deep); }
.visit__rule { border: none; border-top: 1px solid var(--ink); margin: 0; }
.visit__cols { display: grid; gap: clamp(32px, 5vw, 72px); grid-template-columns: 1fr; }
@media (min-width: 720px) { .visit__cols { grid-template-columns: 1fr 1fr; align-items: start; } }
.visit__addr-block { display: grid; gap: var(--space-lg); justify-items: start; align-content: start; }
.visit__addr { font-size: var(--step-1); line-height: 1.4; }
@media (max-width: 560px) {
  .visit__contact { flex-direction: column; align-items: flex-start; gap: var(--space-sm); }
  .visit__sep { display: none; }
}
.visit__hours-title { font-family: var(--font-display); font-weight: 600; font-size: var(--step-1); margin-bottom: var(--space-md); }
.hours { display: grid; gap: var(--space-sm); }
.hours > div { display: flex; justify-content: space-between; gap: var(--space-md); padding-bottom: var(--space-sm); border-bottom: 1px solid var(--hairline); }
.hours dt { color: var(--ink-soft); }
.hours dd { font-weight: 600; }
.hours > div:last-child { border-bottom: none; padding-bottom: 0; }

/* (Visit locator map removed: to be redesigned) */

/* ============================================================
   Active nav state (current page indicator, primary + footer)
   ============================================================ */
/* Desktop primary nav: full-width gold underline on the current page */
.nav__menu > li > a[aria-current="page"]::after {
  scale: 1 1;
}
.nav__menu > li > a[aria-current="page"] {
  color: var(--gold-bright);
}
.site-header.is-scrolled .nav__menu > li > a[aria-current="page"] {
  color: var(--gold-deep);
}
/* Mobile dropdown: gold left-mark on the active row, no underline */
@media (max-width: 860px) {
  .nav__menu > li > a[aria-current="page"] {
    color: var(--gold-deep);
    box-shadow: inset 3px 0 0 0 var(--gold);
  }
}
/* Footer nav: small gold dot before the active link */
.site-footer__nav a[aria-current="page"] {
  color: var(--gold-bright);
  position: relative;
}
.site-footer__nav a[aria-current="page"]::before {
  content: ""; display: inline-block; width: 5px; height: 5px;
  background: var(--gold); border-radius: 50%;
  margin-right: 8px; vertical-align: middle;
}

/* ============================================================
   Inner-page hero, shorter, type-led or photo-led, sits below
   the fixed header. Reuses .hero typography where possible.
   ============================================================ */
.page-hero {
  position: relative;
  padding-top: clamp(140px, 22vh, 200px);
  padding-bottom: clamp(56px, 9vh, 112px);
  isolation: isolate;
  overflow: hidden;
}
.page-hero--cream { background: var(--cream); color: var(--ink); }
.page-hero--dark  { background: var(--near-black); color: var(--bone); }
.page-hero .wrap { display: grid; gap: var(--space-lg); max-width: var(--wrap); }
.page-hero__eyebrow { color: var(--gold-deep); }
.page-hero--dark .page-hero__eyebrow {
  color: var(--gold-bright);
}
.page-hero__title {
  font-family: var(--font-display); font-weight: 600;
  font-size: clamp(2.6rem, 1.4rem + 5.2vw, 5.5rem);
  line-height: 0.98; letter-spacing: -0.018em;
  max-width: 18ch; text-wrap: balance;
}
.page-hero__title em { font-style: italic; color: var(--gold-deep); font-weight: 500; }
/* The accent gold has to follow the GROUND, not the component. --gold-deep was darkened
   specifically to clear 4.5:1 on CREAM, so on a dark ground it is a dark-on-dark pairing.
   /our-story is the only --photo hero on the site and it was falling through to the cream gold:
   measured 2026-08-27 against the real pixels behind the word (the word hidden, the region
   re-shot, the mean sampled), the ground is rgb(33,33,25) and the word scored **2.63:1** at 43px,
   which fails even the 3:1 large-text floor. --gold-bright measures 5.38:1 on the same pixels.
   Same failure class as the 1.24:1 "Read the full story" link found on 2026-08-25: a token that
   is correct for one ground applied to its opposite. The --dark rule already existed and was
   right; it simply did not name the variant this site actually uses. */
.page-hero--dark .page-hero__title em,
.page-hero--photo .page-hero__title em { color: var(--gold-bright); }
.page-hero__rule { width: clamp(56px, 7vw, 120px); height: 2px; background: var(--gold); margin-block: var(--space-xs); }
.page-hero__lede {
  font-size: var(--step-1); line-height: 1.5;
  max-width: 44ch;
  color: var(--ink-soft);
}
.page-hero--dark .page-hero__lede { color: var(--bone-soft); }

/* Photo-led inner hero (Our Story): image full-bleed behind, text on a dark scrim */
.page-hero--photo { color: var(--bone); }
.page-hero--photo .page-hero__media { position: absolute; inset: 0; z-index: -2; overflow: hidden; background: var(--near-black); }
.page-hero--photo .page-hero__media img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 40%; }
/* SCRIM REWORKED 2026-08-27 (operator): "background less darkness and put the darkness around
   the text". The old version was two FULL-COVERAGE gradients stacked, 90/65/40 bottom-to-top plus
   80/36/18 left-to-right, which meant the darkest part of the picture was darkened most and the
   portrait of Mathew was being flattened to hold up type that only occupies the left third.
   Now the job is split. This layer keeps only a light overall settle, enough to stop the photo
   fighting the fixed header at the top and to seat it against the section below. The heavy
   lifting moved to a feathered pool behind the text itself (.wrap::before), so the darkness
   follows the words instead of covering the man. */
.page-hero--photo .page-hero__scrim {
  position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background:
    linear-gradient(to top, color-mix(in oklch, var(--near-black) 55%, transparent) 0%,
                    color-mix(in oklch, var(--near-black) 12%, transparent) 42%,
                    color-mix(in oklch, var(--near-black) 30%, transparent) 100%);
}

/* The pool that carries the type. Radial and feathered to nothing by 80%, so there is no edge to
   notice: a rectangular panel here would read as a box on a photograph. It bleeds outside the
   text block on every side so the falloff happens off the text, not across it. */
.page-hero--photo > .wrap { position: relative; }
.page-hero--photo > .wrap::before {
  content: ""; position: absolute; z-index: -1; pointer-events: none;
  inset: -8vh -14vw -8vh -12vw;
  /* RE-AIMED 2026-08-27 after the content was split: the pool was centred at 52% down, which is
     where the text USED to sit. Raising the eyebrow put it at 104px, over the pale wall behind
     Mathew, and it measured 2.86:1 with 96% of its area under 4.5 , small gold type on the
     brightest part of the photograph. The pool now sits over the eyebrow-and-headline block in the
     upper left, which is where the type that actually needs help lives, and it stays off his face.
     The lede does not need it: at the foot of the hero it sits on rosemary in shadow and measures
     10.79:1 unaided. A second, much softer wash holds the bottom so the type there never depends
     on the photograph staying dark if the image is ever swapped. */
  /* RE-SHAPED 2026-08-27 once the headline moved to the foot. The pool used to be a large ellipse
     covering the whole upper left, because the headline lived there and sat on the pale wall. With
     the headline gone from the top, that pool was darkening a large area of photograph for the
     sake of one line of small caps, which is what made the top read heavy.
     It is now two small, specific things:
       1. a tight fade behind the EYEBROW only, top left. It still has to be this deep, because
          gold at 12px on the brightest part of the picture measured 2.86:1 unaided;
       2. a wash at the FOOT, now reaching higher and darker than before because it carries the
          headline as well as the paragraph.
     Everything between them is left alone, which is the part of the frame Mathew occupies. */
  background:
    linear-gradient(to top,
      color-mix(in oklch, var(--near-black) 72%, transparent) 0%,
      color-mix(in oklch, var(--near-black) 58%, transparent) 22%,
      color-mix(in oklch, var(--near-black) 30%, transparent) 40%,
      transparent 54%);
}
/* MATCHED TO THE LOGO, 2026-08-27 (operator), and carrying its own legibility rather than
   leaning on a dark patch behind it.
   The patch was covering a large area of the photograph purely to rescue one line of 12px gold
   sitting on the pale wall behind Mathew's head, and it was the single thing making the top of
   the picture read heavy. Measured: it could not go lighter than 80% without the words dropping
   under 4.5:1, and shrinking its footprint failed even at full opacity because the words run
   wider than the ellipse.
   So the darkness moved onto the glyphs themselves, using the SAME visual language as the mobile
   drawer: when the nav opens, the cleaver and the wordmark each take a soft drop-shadow halo
   (`drop-shadow(0 0 4px near-black 38%)` and `drop-shadow(0 1px 3px near-black 22%)`) rather
   than an outline. A hard four-way edge was tried first and read as a sticker next to these.
   The drawer's OWN values were tried literally and are too weak here: they were tuned to lift
   the logo off a cream panel, and against a pale wall in a photograph they do almost nothing.
   Same technique, three stacked halos, at a strength the ground actually needs.
   HONEST NOTE: this does NOT change the computed contrast ratio, which considers only text
   colour against background colour and ignores shadows entirely. On paper this still measures as
   a fail. In practice it is clearly legible, and the rendered comparison is the evidence.
   Recorded as a deliberate deviation in _briefs/STANDARDS_OVERRIDES.md. */
.page-hero--photo .page-hero__eyebrow {
  color: var(--gold-mark);
  text-shadow: var(--halo-on-photo);
}

/* FULL-SCREEN PHOTO HERO ON PHONES, 2026-08-27 (operator), matching the home page.
   Before this the hero measured 652px inside a 734px viewport, so the next section peeked in
   under it and the portrait of Mathew was cropped short of the fold for no reason. It is the
   only --photo hero on the site; the two --cream heroes are unaffected and stay as they are,
   because a cream hero that stops early reads as a page beginning, while a photographic one
   that stops early just reads as a mistake.
   `svh` for the same reason as the home hero: `vh` on iOS measures the viewport with the
   browser chrome HIDDEN, so it over-runs at rest, and `dvh` re-flows mid-scroll as the chrome
   collapses. `svh` is the loaded state and it holds still.
   `min-height`, never `height`: the lede on this page runs to eight lines on a phone and must
   be allowed to push the hero taller rather than be clipped by it.
   `align-content: space-between` distributes the surplus between eyebrow, headline, rule and
   lede instead of leaving it in a slab at the bottom, which is what "spread out" means here.
   The existing `gap` stays as the floor, so on a short phone this degrades to the old layout
   rather than colliding. */
@media (max-width: 820px) {
  .page-hero--photo { min-height: 100svh; display: grid; }
  /* SPLIT, not stacked. 2026-08-27, second pass, from the operator's marked-up screenshot:
     the eyebrow belongs right up under the header, and the BODY belongs down in the empty space
     at the foot of the hero, not bunched under the headline.
     My first attempt used `align-content: start`, which grouped all four children at the top and
     left the whole lower half of the picture empty. That was the opposite of the ask.
     The shared .page-hero padding-top is clamp(140px, 22vh, 200px), which is 161px on a 734px
     phone and pushed the eyebrow a third of the way down. It only has to clear the fixed header,
     which measures 88px on a phone, plus a little air.
     The mechanism: four rows, the first three sized to content and the fourth taking every
     remaining pixel, with the lede pinned to the BOTTOM of that fourth row. `align-content` cannot
     do this, it distributes rows evenly and would leave the lede floating mid-screen; the free
     space has to belong to one row so it can all collect in one place. */
  .page-hero--photo { padding-top: calc(88px + env(safe-area-inset-top) + var(--space-md)); }
  /* THREE-PART HERO, 2026-08-27 (operator markup): eyebrow anchors the top, the photograph owns
     the whole middle, and the headline + rule + paragraph group together at the foot as one block.
     The free space is given to row 2, between the eyebrow and the headline, so it all collects in
     one place; `align-content` would spread it evenly and float the type mid-screen. */
  /* ALL FOUR PIECES OF TYPE GROUP AT THE FOOT, 2026-08-27 (operator). The eyebrow came down off
     the top to sit above the headline, so the photograph now owns everything from the header to
     roughly two thirds down with nothing over it at all.
     The empty row moved from position 2 to position 1: the free space now sits ABOVE the eyebrow
     rather than between it and the headline, which is what pushes the whole block to the bottom.
     Worth noting this also retires the reason the eyebrow needed rescuing. It used to sit on the
     pale wall behind Mathew's head, which is why it measured 2.86:1 and needed a dark patch. Down
     here it sits on rosemary in shadow, so the halo becomes belt and braces rather than the thing
     holding it up. Re-measured after the move rather than assumed. */
  .page-hero--photo > .wrap {
    grid-template-rows: 1fr auto auto auto auto;
    align-content: stretch;
    gap: var(--space-lg);
  }
  .page-hero--photo .page-hero__eyebrow { grid-row: 2; align-self: end; }
  .page-hero--photo .page-hero__title   { grid-row: 3; align-self: end; }
  .page-hero--photo .page-hero__rule    { grid-row: 4; align-self: end; }
  /* The paragraph needs its row named too. Without it, auto-placement dropped it into the
     empty 1fr row and it rendered ABOVE the headline. */
  .page-hero--photo .page-hero__lede    { grid-row: 5; align-self: end; }
  /* The paragraph sits at the BOTTOM of the screen, 2026-08-27 (operator). The shared
     .page-hero padding-bottom is clamp(56px, 9vh, 112px), which is 66px on a 734px phone and was
     holding it off the foot. It only needs enough to not touch the edge. */
  .page-hero--photo { padding-bottom: var(--space-lg); }
  .page-hero--photo .page-hero__lede { align-self: end; }
}
.page-hero--photo .page-hero__title { text-shadow: 0 2px 24px color-mix(in oklch, var(--near-black) 50%, transparent); }
.page-hero--photo .page-hero__lede { color: var(--bone-soft); text-shadow: 0 1px 16px color-mix(in oklch, var(--near-black) 48%, transparent); }

/* ============================================================
   Placeholder figure, sized, on-brand, captioned. Reads
   as deliberate empty (not a missing asset). Replaced with
   real <picture> when Mathew supplies photography.
   ============================================================ */
.ph-figure {
  position: relative; display: block;
  /* 92/8, not 88/8: percentages under 100 leave the panel semi-transparent and
     darkened the label's ground below the 4.5:1 floor. */
  background: color-mix(in oklch, var(--cream-panel) 92%, var(--ink) 8%);
  border: 1px solid var(--hairline);
  outline: 1px dashed color-mix(in oklch, var(--gold-deep) 35%, transparent);
  outline-offset: -10px;
  overflow: hidden;
}
.ph-figure--dark {
  background: color-mix(in oklch, var(--near-black-2) 96%, var(--bone) 4%);
  border-color: color-mix(in oklch, var(--bone) 14%, transparent);
  outline-color: color-mix(in oklch, var(--gold-bright) 30%, transparent);
}
.ph-figure__inner {
  aspect-ratio: 4 / 3; display: grid; place-items: center; padding: var(--space-2xl);
  text-align: center;
}
.ph-figure--portrait .ph-figure__inner { aspect-ratio: 4 / 5; }
.ph-figure--wide .ph-figure__inner { aspect-ratio: 16 / 9; }
.ph-figure--square .ph-figure__inner { aspect-ratio: 1 / 1; }
.ph-figure__label {
  font-family: var(--font-body); font-size: var(--step--1); font-weight: 600;
  letter-spacing: 0.22em; text-transform: uppercase;
  /* --gold-deep alone measured 3.96:1 on the placeholder panel. */
  color: color-mix(in oklch, var(--gold-deep) 76%, var(--ink) 24%);
  display: inline-flex; align-items: center; gap: 10px;
}
.ph-figure--dark .ph-figure__label { color: var(--gold-bright); }
.ph-figure__label::before { content: ""; width: 22px; height: 1px; background: currentColor; opacity: 0.7; }
.ph-figure__shot {
  margin-top: var(--space-sm);
  font-family: var(--font-display); font-style: italic; font-weight: 500;
  font-size: var(--step-1); line-height: 1.3;
  color: var(--ink-soft); max-width: 28ch;
}
.ph-figure--dark .ph-figure__shot { color: var(--bone-soft); }

/* ============================================================
   Our Story, editorial long-read
   Alternating cream/dark sections, framed figures, named beats
   ============================================================ */
.story-section { padding-block: var(--section-y); }
.story-section--cream { background: var(--cream); color: var(--ink); }
.story-section--dark  { background: var(--near-black); color: var(--bone); }
.story-section--dark p { color: var(--bone-soft); line-height: 1.7; }
.story-section--dark em { color: var(--gold-bright); font-style: italic; }
.story-section--cream em { color: var(--gold-deep); font-style: italic; }

.story-grid {
  display: grid; gap: clamp(40px, 6vw, 96px); grid-template-columns: 1fr;
  align-items: center;
}
@media (min-width: 900px) {
  .story-grid--photo-right { grid-template-columns: 1.05fr 0.95fr; }
  .story-grid--photo-left  { grid-template-columns: 0.95fr 1.05fr; }
  .story-grid--photo-left .story-grid__text { order: 2; }
  .story-grid--photo-left .story-grid__media { order: 1; }
}
.story-grid__text { display: grid; gap: var(--space-lg); max-width: 52ch; }
.story-grid__title {
  font-family: var(--font-display); font-weight: 600;
  font-size: var(--step-3); line-height: 1.04; letter-spacing: -0.015em;
}
.story-grid__title em { font-style: italic; font-weight: 500; }
.story-section--cream .story-grid__title em { color: var(--gold-deep); }
.story-section--dark  .story-grid__title em { color: var(--gold-bright); }
/* Captions on the dark story beats: --ink-soft read 2.63:1 on --near-black. */
.story-section--dark figcaption { color: var(--bone-soft); }
.story-grid__media img,
.story-grid__media .ph-figure { width: 100%; }
.story-grid__media .framed img { aspect-ratio: 4 / 5; object-fit: cover; max-width: 520px; margin-inline: auto; }

/* Story pull-quote: the "what we mean by artisan" beat */
.story-pullquote { padding-block: var(--section-y); background: var(--cream); }
.story-pullquote .wrap { max-width: 64ch; text-align: left; display: grid; gap: var(--space-xl); }
.story-pullquote__lede { font-size: var(--step-1); color: var(--ink-soft); }
.story-pullquote__quote {
  font-family: var(--font-display); font-style: italic; font-weight: 500;
  font-size: clamp(1.7rem, 1rem + 2.2vw, 2.65rem);
  line-height: 1.18; color: var(--ink); letter-spacing: -0.01em;
  border-top: 1px solid var(--gold);
  border-bottom: 1px solid var(--gold);
  padding-block: var(--space-2xl);
}

/* Story award block: small, hairline above, low in the page */
.story-award {
  padding-block: clamp(48px, 7vw, 96px);
  background: var(--cream);
  border-top: 1px solid var(--hairline);
}
.story-award .wrap { max-width: 56ch; display: grid; gap: var(--space-md); }
.story-award__eyebrow { color: var(--gold-deep); }
.story-award p { color: var(--ink-soft); }
/* The award heading sits a step below the story headings: this block is
   deliberately quiet, not a hero moment. */
.story-award .story-grid__title { font-size: var(--step-2); }

/* Story CTA strip: close-out, links onward to /the-range + /visit */
.story-cta {
  padding-block: var(--section-y);
  background: var(--near-black); color: var(--bone);
  text-align: center;
}
.story-cta .wrap { display: grid; gap: var(--space-xl); justify-items: center; max-width: 56ch; }
.story-cta__title { font-family: var(--font-display); font-weight: 600; font-size: var(--step-3); line-height: 1.05; text-wrap: balance; }
.story-cta__title em { font-style: italic; color: var(--gold-bright); font-weight: 500; }
.story-cta__lede { color: var(--bone-soft); max-width: 44ch; }
.story-cta__actions { display: flex; flex-wrap: wrap; gap: var(--space-md); justify-content: center; }

/* ============================================================
   The Range page, expanded asymmetric showcase, 4 categories
   ============================================================ */
.range-page { background: var(--cream); padding-block: var(--section-y); }
.range-page__intro { max-width: 60ch; display: grid; gap: var(--space-lg); margin-bottom: var(--space-4xl); }
.range-page__intro p { color: var(--ink-soft); font-size: var(--step-1); line-height: 1.55; }

.range-cat {
  display: grid; gap: clamp(32px, 5vw, 80px); grid-template-columns: 1fr;
  padding-block: clamp(56px, 8vw, 120px);
  border-top: 1px solid var(--hairline);
  align-items: start;
}
@media (min-width: 900px) {
  .range-cat--photo-right { grid-template-columns: 1.1fr 0.9fr; }
  .range-cat--photo-left  { grid-template-columns: 0.9fr 1.1fr; }
  .range-cat--photo-left .range-cat__text  { order: 2; }
  .range-cat--photo-left .range-cat__media { order: 1; }
}
/* Anchor targets, added 2026-08-26. The four categories carry ids (#beef, #sausages, #pork,
   #lamb) because 24 of the 64 launch redirect rules from the old Shopify product URLs point at
   them, and without an id every one of those lands at the top of a long page. .site-header is
   position:fixed, so a bare anchor would put the heading UNDERNEATH the bar; scroll-margin-top
   clears it with a little breathing room above the number. Measured, not guessed: the header
   renders 88px at 390 and 118px at 1440, so the first attempt at clamp(84px, 12vw, 112px) left
   the section hairline UNDER the bar at both widths. These values clear it by 24px and 34px. */
.range-cat[id] { scroll-margin-top: clamp(112px, 14vw, 152px); }
/* Below 900px the grid collapses to ONE column, so DOM order decides, and the
   --photo-left variants (categories 02 and 04) carry .range-cat__media BEFORE
   .range-cat__text in the markup. That put the image above the category heading on a
   phone. With those two categories currently on "Photography pending" placeholders it
   read as a 432px hole above the title, while 01 and 03 read correctly. Text first at
   narrow widths; the 900px rule above still gives the alternating left/right rhythm on
   desktop, which is the only width where that rhythm is legible anyway. */
@media (max-width: 899.98px) {
  .range-cat--photo-left .range-cat__text  { order: 1; }
  .range-cat--photo-left .range-cat__media { order: 2; }
}
.range-cat__text { display: grid; gap: var(--space-lg); max-width: 50ch; }
.range-cat__num {
  font-family: var(--font-display); font-weight: 600;
  font-size: calc(var(--step-1) + 6px); line-height: 1;
  color: var(--gold-deep); font-variant-numeric: tabular-nums;
  letter-spacing: 0;
}
.range-cat__title {
  font-family: var(--font-display); font-weight: 600;
  font-size: var(--step-3); line-height: 1.05; letter-spacing: -0.012em;
  text-wrap: balance;
}
.range-cat__title em { font-style: italic; color: var(--gold-deep); font-weight: 500; }
.range-cat__lede { font-size: var(--step-1); color: var(--ink); line-height: 1.55; }
.range-cat__lede em { font-style: italic; color: var(--gold-deep); }
.range-cat__listlabel {
  font-size: var(--step--1); font-weight: 600;
  letter-spacing: 0.22em; text-transform: uppercase;
  color: var(--ink-soft);
  padding-top: var(--space-md); border-top: 1px solid var(--hairline);
}
/* Unstyled product-name list: proof of depth, not a price list */
.range-cat__list {
  list-style: none; padding: 0; margin: 0;
  display: grid; gap: 0;
}
.range-cat__list li {
  padding-block: var(--space-sm) var(--space-sm);
  border-top: 1px solid color-mix(in oklch, var(--hairline) 55%, transparent);
  color: var(--ink);
  font-size: 1rem; line-height: 1.5;
}
.range-cat__list li:first-child { border-top: none; }
.range-cat__list li strong { font-weight: 600; color: var(--ink); }
.range-cat__close { color: var(--ink-soft); font-style: italic; }
.range-cat__media .framed img { aspect-ratio: 4 / 5; object-fit: cover; }
.range-cat__media .ph-figure { width: 100%; }

.range-page__footer {
  margin-top: var(--space-4xl); padding-top: var(--space-3xl);
  border-top: 1px solid var(--hairline);
  display: grid; gap: var(--space-xl); justify-items: center; text-align: center;
  max-width: 56ch; margin-inline: auto;
}
.range-page__footer h2 { font-family: var(--font-display); font-weight: 600; font-size: var(--step-2); line-height: 1.1; }
.range-page__footer p { color: var(--ink-soft); max-width: 50ch; }
.range-page__footer .range-page__actions { display: flex; flex-wrap: wrap; gap: var(--space-md); justify-content: center; }

/* ============================================================
   Visit page, expanded statement-left / details-right rhythm
   ============================================================ */
.visit-page { background: var(--cream-panel); padding-block: var(--section-y); }
/* "Find us" heads its own block rather than sitting beside it, so it needs the
   full statement gap beneath it that .visit-section__grid supplies elsewhere. */
.visit-page .visit-section__statement { margin-bottom: var(--space-3xl); }
.visit-page__lede {
  max-width: 56ch; font-size: var(--step-1); line-height: 1.55;
  color: var(--ink); margin-bottom: var(--space-3xl);
}

.visit-grid {
  display: grid; gap: clamp(32px, 5vw, 80px); grid-template-columns: 1fr;
}
@media (min-width: 820px) {
  .visit-grid { grid-template-columns: 1fr 1.05fr; align-items: start; }
}

/* NAP block: bold labels, clean stack, no card chrome */
.visit-nap { display: grid; gap: var(--space-xl); }
.visit-nap__row { display: grid; gap: var(--space-xs); padding-bottom: var(--space-lg); border-bottom: 1px solid var(--hairline); }
.visit-nap__row:last-child { border-bottom: none; padding-bottom: 0; }
.visit-nap__label {
  font-size: var(--step--1); font-weight: 600;
  letter-spacing: 0.22em; text-transform: uppercase; color: var(--gold-deep);
}
.visit-nap__val {
  font-family: var(--font-display); font-weight: 500;
  font-size: var(--step-2); line-height: 1.15; color: var(--ink);
  font-style: normal;
}
.visit-nap__val a { color: var(--ink); transition: color var(--dur-fast) var(--ease-lux); }
.visit-nap__val a:hover { color: var(--gold-deep); }
.visit-nap__val--sm { font-size: var(--step-1); }
/* Long email + IG handle wrap on narrow viewports (matches the .site-footer__contact a pattern) */
.visit-nap__val--sm a { overflow-wrap: anywhere; }
.visit-nap__actions { display: flex; flex-wrap: wrap; gap: var(--space-md); margin-top: var(--space-md); }

/* Map slot: Google Maps embed of the Business Profile listing, with the visible
   "Open in Google Maps" link beneath it. The frame keeps the panel treatment the drawn
   placeholder had (hairline edge, house radius, same 4 / 3.2 ratio) so the slot geometry
   is unchanged. The ratio is declared HERE, not on the iframe, because the iframe is
   lazy: without a reserved box the late frame would shift the page (CLS). border: 0 is a
   rule and not a style="" attribute because the CSP carries no 'unsafe-inline' for styles. */
.visit-map { display: grid; justify-items: start; }
.visit-map__frame {
  position: relative; aspect-ratio: 4 / 3.2; width: 100%;
  border: 1px solid var(--hairline); border-radius: var(--radius);
  background: color-mix(in oklch, var(--cream) 92%, var(--ink) 6%);
  overflow: hidden;
}
.visit-map__embed { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.visit-map__cta { margin-top: var(--space-lg); }

/* Visit page sub-sections: Trading hours, Getting here, What to expect, One small thing */
.visit-section { padding-block: var(--section-y); }
/* Runs straight on from the section above it on the same cream ground, so the
   two read as one block instead of two. Drops only the leading pad. */
.visit-section--joined { padding-top: 0; }
.visit-section--cream { background: var(--cream); }
.visit-section--dark  { background: var(--near-black); color: var(--bone); }
.visit-section--dark p { color: var(--bone-soft); }
.visit-section__grid {
  display: grid; gap: clamp(32px, 4vw, 64px);
  grid-template-columns: 1fr;
}
@media (min-width: 820px) {
  .visit-section__grid { grid-template-columns: 0.85fr 1.15fr; align-items: start; }
}
.visit-section__statement {
  font-family: var(--font-display); font-weight: 600;
  font-size: var(--step-3); line-height: 1.06; letter-spacing: -0.012em;
  text-wrap: balance; max-width: 16ch;
}
.visit-section__statement em { font-style: italic; font-weight: 500; }
.visit-section--cream .visit-section__statement em { color: var(--gold-deep); }
.visit-section--dark .visit-section__statement em { color: var(--gold-bright); }
.visit-section__details { display: grid; gap: var(--space-lg); max-width: 56ch; }
.visit-section__details p { font-size: 1.0625rem; line-height: 1.65; }

/* Trading-hours pretty list, used on /visit */
.hours-card { display: grid; gap: var(--space-md); padding: var(--space-xl); border: 1px solid var(--hairline); background: var(--cream); }
.hours-card .hours-row { display: flex; justify-content: space-between; gap: var(--space-md); padding-bottom: var(--space-sm); border-bottom: 1px solid var(--hairline); }
.hours-card .hours-row:last-child { padding-bottom: 0; border-bottom: none; }
.hours-card dt { color: var(--ink-soft); font-weight: 500; }
.hours-card dd { font-family: var(--font-display); font-weight: 600; color: var(--ink); font-variant-numeric: tabular-nums; }
.hours-card__note { font-size: var(--step--1); color: var(--ink-soft); margin-top: var(--space-xs); line-height: 1.5; }

/* "One small thing": deliberate warmth at the bottom of /visit */
.visit-aside {
  padding-block: clamp(56px, 7vw, 96px);
  background: var(--cream-panel);
  border-top: 1px solid var(--hairline);
}
.visit-aside .wrap { max-width: 52ch; display: grid; gap: var(--space-md); }
.visit-aside__eyebrow { color: var(--gold-deep); }
.visit-aside p {
  font-family: var(--font-display); font-style: italic; font-weight: 500;
  font-size: var(--step-2); line-height: 1.3; color: var(--ink);
}

/* ============================================================
   Footer
   ============================================================ */
/* Footer: centered editorial on mobile; brand-left + 3 columns on desktop. Styling shared. */
.site-footer { background: var(--near-black); color: var(--bone-soft); padding-block: clamp(60px, 9vw, 100px) clamp(30px, 5vw, 44px); text-align: center; }
.site-footer__inner { max-width: 600px; margin-inline: auto; }
.site-footer__logo { display: block; height: clamp(56px, 9vw, 74px); width: auto; margin: 0 auto; }
.site-footer__eyebrow { font-size: 0.64rem; font-weight: 600; letter-spacing: 0.22em; text-transform: uppercase; color: var(--gold-bright); margin-top: 16px; }
.site-footer__rule { width: 40px; height: 1px; background: var(--gold); margin: clamp(22px, 4vw, 30px) auto; }
.site-footer__h { display: none; font-size: 0.64rem; letter-spacing: 0.22em; text-transform: uppercase; color: var(--gold-bright); font-weight: 600; margin-bottom: var(--space-md); }
.site-footer__nav { display: flex; justify-content: center; flex-wrap: wrap; gap: 10px clamp(20px, 4vw, 34px); font-size: 1rem; }
.site-footer__nav a { color: var(--bone-soft); transition: color var(--dur-fast) var(--ease-lux); }
.site-footer__nav a:hover { color: var(--gold-bright); }
.site-footer__visit { margin-top: clamp(26px, 5vw, 34px); }
.site-footer__addr { font-style: normal; font-size: 1rem; line-height: 1.7; color: var(--bone); }
.site-footer__hours { margin-top: 6px; font-size: 0.92rem; color: var(--bone-soft); }
.site-footer__hours-row { display: inline; }
.site-footer__hours-row dt, .site-footer__hours-row dd { display: inline; margin: 0; }
.site-footer__hours-row + .site-footer__hours-row::before { content: " · "; }
.site-footer a.site-footer__directions { display: inline-flex; align-items: center; gap: 8px; margin-top: 18px; font-size: 0.92rem; color: var(--gold-bright); border-bottom: 1px solid color-mix(in oklch, var(--gold-bright) 40%, transparent); padding-bottom: 3px; transition: border-color var(--dur-fast) var(--ease-lux); }
.site-footer a.site-footer__directions:hover { border-bottom-color: var(--gold-bright); }
.site-footer__directions span { transition: transform var(--dur-fast) var(--ease-lux); }
.site-footer a.site-footer__directions:hover span { transform: translateX(4px); }
.site-footer__contact { margin-top: clamp(26px, 5vw, 34px); font-size: 0.92rem; }
.site-footer__contact a { display: block; line-height: 2.1; color: var(--bone-soft); overflow-wrap: anywhere; transition: color var(--dur-fast) var(--ease-lux); }
.site-footer__contact a:hover { color: var(--gold-bright); }
.site-footer__award { margin-top: clamp(24px, 4vw, 30px); font-size: 15px; line-height: 1.6; color: color-mix(in oklch, var(--bone-soft) 85%, transparent); }
.site-footer__award strong { display: block; color: var(--gold-bright); font-weight: 600; font-size: 0.64rem; letter-spacing: 0.22em; text-transform: uppercase; margin-bottom: 4px; }
.site-footer__cp { margin-top: clamp(30px, 5vw, 40px); padding-top: var(--space-lg); border-top: 1px solid color-mix(in oklch, var(--bone) 12%, transparent); font-size: 0.72rem; color: color-mix(in oklch, var(--bone-soft) 72%, transparent); display: flex; flex-wrap: wrap; justify-content: center; align-items: baseline; gap: var(--space-sm) var(--space-lg); }
.site-footer__credit { color: inherit; letter-spacing: 0.04em; transition: color var(--dur-fast) var(--ease-lux); }
.site-footer__credit:hover { color: var(--gold-bright); }

/* Desktop: brand + award on the left, Explore / Visit / Contact columns on the right */
@media (min-width: 920px) {
  .site-footer { text-align: left; }
  .site-footer__inner {
    max-width: var(--wrap);
    display: grid; align-items: start; column-gap: clamp(32px, 4vw, 64px); row-gap: 0;
    /* the Visit column carries the widest pair ("Mon to Sat" + "8am to 6pm"); at 1fr it
       is ~148px in the 920-1100px band and the hours row wraps. */
    grid-template-columns: 1.5fr 0.9fr 1.15fr 1.25fr;
    grid-template-areas:
      "brand explore visit contact"
      "award explore visit contact"
      "cp    cp      cp    cp";
  }
  .site-footer__brand { grid-area: brand; }
  .site-footer__nav { grid-area: explore; display: block; margin-top: 0; }
  .site-footer__visit { grid-area: visit; margin-top: 0; }
  .site-footer__contact { grid-area: contact; margin-top: 0; }
  .site-footer__award { grid-area: award; margin-top: var(--space-lg); align-self: start; max-width: 26ch; }
  .site-footer__cp { grid-area: cp; margin-top: clamp(44px, 5vw, 72px); justify-content: space-between; }
  .site-footer__logo { margin: 0; }
  .site-footer__eyebrow { margin-top: 14px; }
  .site-footer__rule { margin: 18px 0 0; }
  .site-footer__h { display: block; }
  .site-footer__nav a { display: block; line-height: 2; }
  .site-footer__hours { display: grid; gap: 5px; }
  /* 22ch, not 20: "Mon to Sat" + "8am to 6pm" + the gap lands 1px over 20ch and wraps. */
  .site-footer__hours-row { display: flex; justify-content: space-between; gap: 18px; max-width: 22ch; }
  .site-footer__hours-row dt { color: var(--bone); }
  .site-footer__hours-row dd { color: var(--bone-soft); font-variant-numeric: tabular-nums; }
  .site-footer__hours-row + .site-footer__hours-row::before { content: none; }
}

/* ============================================================
   Persistent call/directions bar , narrow viewports only (home page).
   The two conversions this business has, reachable from anywhere on the page.
   Not shown from 861px up: on a desktop the header keeps a phone affordance in view and
   a fixed bar there would be an obstruction, not a shortcut.
   ============================================================ */
.action-bar { display: none; }
@media (max-width: 860px) {
  .action-bar {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 200;
    display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-xs);
    padding: var(--space-sm) var(--space-md) calc(var(--space-sm) + env(safe-area-inset-bottom));
    /* TRANSPARENT BAR, 2026-08-27 (operator). The cream slab and its hairline are gone, so the
       page runs underneath and the two controls read as floating rather than as a docked tray.
       THE COST, and why the buttons below changed in the same edit: that slab was what made the
       directions button legible. It was `background: transparent` with `--ink` text, which only
       ever worked because it sat on cream. The home page alternates cream and near-black bands,
       so with the slab gone a dark-on-transparent button disappears entirely over every dark
       section. Both controls now carry their own ground and are legible over either. */
    background: transparent;
  }
  /* Reserved on the class, not on `body` and not via `:has()`: only the home page carries the
     bar, and a bare `body` rule would open 77px of dead space at the foot of the four inner
     pages that share this stylesheet. */
  body.has-action-bar { padding-bottom: calc(var(--action-bar-h) + env(safe-area-inset-bottom)); }
  .action-bar__btn {
    display: flex; align-items: center; justify-content: center; gap: var(--space-xs);
    min-height: 52px; padding-inline: var(--space-sm);
    font-family: var(--font-body); font-size: 0.94rem; font-weight: 600; letter-spacing: 0.01em;
    border: 1px solid; border-radius: var(--radius);
    transition: scale var(--dur-fast) var(--ease-lux), background var(--dur-fast) var(--ease-lux);
  }
  .action-bar__btn svg { width: 17px; height: 17px; flex: none; }
  .action-bar__btn:active { scale: 0.96; }
  .action-bar__btn--call { background: var(--gold); border-color: var(--gold); color: var(--bone); }
  /* Solid near-black rather than transparent: it has to hold its own over both grounds now. The
     gold edge keeps it a sibling of the call button rather than a plain dark pill. */
  .action-bar__btn--dir { background: var(--near-black); border-color: var(--gold); color: var(--bone); }
  /* Focus must not land under the bar (WCAG 2.4.11, craft/interaction.md §2). The matching
     top margin clears the fixed header, which had no reservation before this pass either. */
  :target, :focus { scroll-margin-bottom: calc(var(--action-bar-h) + var(--space-md)); }
}
:target, :focus { scroll-margin-top: clamp(88px, 13vh, 128px); }

/* The bar is armed by IntersectionObserver once the hero has left, so it never sits over the
   hero's own two buttons. The pre-armed state is gated on `scripting: enabled` for the same
   reason the reveals below are: with JavaScript off the bar is simply always visible, which
   is the correct fallback for a phone number. */
@media (scripting: enabled) and (max-width: 860px) {
  .action-bar { translate: 0 100%; opacity: 0; pointer-events: none; transition: translate var(--dur-mid) var(--ease-lux), opacity var(--dur-mid) var(--ease-lux); }
  .action-bar.is-visible { translate: 0 0; opacity: 1; pointer-events: auto; }
}

/* ============================================================
   404 page. Moved out of an inline <style> block in 404.html so the
   build can ship a CSP with no 'unsafe-inline' in style-src. Every
   selector is scoped under .nf, which exists on no other page, so
   these rules cannot reach the rest of the site.
   ============================================================ */
.nf { min-height: 100svh; display: grid; place-items: center; text-align: center; padding: var(--space-2xl) var(--space-lg); background: var(--near-black); color: var(--bone); }
.nf__inner { display: grid; gap: var(--space-md); justify-items: center; max-width: 34rem; }
.nf__mark { width: clamp(190px, 44vw, 280px); height: auto; margin-bottom: var(--space-2xs); }
.nf__code { font-family: var(--font-display); font-weight: 600; font-size: clamp(4.5rem, 20vw, 9rem); line-height: 0.9; color: var(--gold-bright); }
.nf__title { font-size: var(--step-2); }
.nf p.nf__lede { color: var(--bone-soft); max-width: 36ch; }
.nf .btn { margin-top: var(--space-xs); }

/* ============================================================
   Reveal animations (progressive enhancement)

   The gate is `@media (scripting: enabled)`, not a `.js` class set by an
   inline <head> script. Same job, same timing: the query is evaluated when
   this render-blocking stylesheet is parsed, so the pre-reveal state is in
   place before first paint and there is no flash. Removing the inline script
   is what lets _headers ship a CSP with no 'unsafe-inline'.

   Fallback is correct in every direction. Scripting off, or a browser without
   the `scripting` media feature: the query is false, nothing is hidden, all
   content renders immediately with no animation. That is the same path the
   `.js` class produced when the script did not run.
   ============================================================ */
@media (scripting: enabled) {
  [data-reveal] { opacity: 0; translate: 0 32px; filter: blur(8px); transition: opacity var(--dur-cine) var(--ease-lux), translate var(--dur-cine) var(--ease-lux), filter var(--dur-cine) var(--ease-lux); }
  [data-reveal].is-visible { opacity: 1; translate: 0 0; filter: blur(0); }

  .hero [data-reveal] { transition-duration: 1000ms; }
  .hero.is-ready [data-reveal] { opacity: 1; translate: 0 0; filter: blur(0); }
}
.hero.is-ready .hero__eyebrow  { transition-delay: 150ms; }
.hero.is-ready .hero__title    { transition-delay: 300ms; }
.hero.is-ready .hero__rule     { transition-delay: 480ms; }
.hero.is-ready .hero__lede     { transition-delay: 600ms; }
.hero.is-ready .hero__actions  { transition-delay: 720ms; }
.hero.is-ready .hero__meta     { transition-delay: 840ms; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; }
  [data-reveal] { opacity: 1 !important; translate: none !important; filter: none !important; transition: none !important; }
  .nav__menu > li { opacity: 1 !important; translate: none !important; }
  .nav { transition: none !important; }
  .hero__media { animation: none; }
  .hero__scroll-line::after { animation: none; }
  .framed__accent { transition: none; }
  /* The action bar still ARMS under reduced motion (it is a state change, not a flourish);
     only the slide-and-fade that carries it is removed, so it snaps into place. */
  .action-bar { transition: none !important; }
}
