/* ============================================================================
   Fine Mess Smokehouse — production stylesheet
   Built from HANDOFF.md §3–§6 and the *.dc.html prototypes (never shipped).
   Custom properties are carried over exactly as authored — CLAUDE.md rule.
   ========================================================================= */


/* ── 1. Design tokens ─────────────────────────────────────────────────────
   Content of _ds/.../tokens/*.css, held here unmodified because _ds/ is not
   shipped (CLAUDE.md .gitignore). Do not rename or restructure these blocks;
   an edition or brand-book change is pasted straight over the matching one. */

/* tokens/colors.css */
:root{
--fm-ember:#7c261c;
--fm-ember-dark:#5c1a13;
--fm-oyster:#e8e3dd;
--fm-oyster-dark:#d8d0c6;
--fm-brass:#ad8a53;
--fm-brass-light:#dac18f;
--fm-brass-dark:#8a6e42;
--fm-charcoal:#232323;
--fm-charcoal-soft:#3a3733;
--fm-white:#fffdfa;
--color-bg:var(--fm-oyster);
--color-bg-dark:var(--fm-charcoal);
--color-surface:var(--fm-white);
--color-surface-sunken:var(--fm-oyster-dark);
--color-text:var(--fm-charcoal);
--color-text-muted:#5c5852;
--color-text-inverse:var(--fm-oyster);
--color-logo:var(--fm-ember);/* reserved exclusively for the logotype — never use for UI */
--color-primary:var(--fm-brass);
--color-primary-hover:var(--fm-brass-dark);
--color-accent:var(--fm-brass);
--color-accent-light:var(--fm-brass-light);
--color-border:#c9bda9;
--color-border-strong:var(--fm-brass);
--color-focus-ring:var(--fm-brass-dark);
--color-danger:#8a2b1e;
--color-success:#4f6b3f;
}

/* tokens/typography.css
   --font-greek / Literata substitutes for Playfair ONLY where Greek script
   is actually set in a Playfair context — client-confirmed, see the note at
   the end of this file. Nothing in the current copy does that (DOC is
   Fira-only per HANDOFF §5, and Playfair elsewhere never carries Greek), so
   the token is defined for completeness but Literata itself is not loaded
   in partials/head.html until a page needs it — no point paying for the
   request today. */
:root{
--font-display:'Playfair Display',"Times New Roman",serif;
--font-display-headline:'Playfair Display',"Times New Roman",serif;
--font-serif-text:'Playfair Display',"Times New Roman",serif;
--font-greek:'Literata',"Times New Roman",serif;
--font-body:'Fira Sans',-apple-system,Helvetica,Arial,sans-serif;
--tracking-wordmark:0.28em;
--tracking-label:0.14em;
--tracking-tight:-0.01em;
--text-display-xl:clamp(3.5rem,6vw,6.5rem);
--text-display-l:clamp(2.75rem,4.5vw,4.5rem);
--text-display-m:clamp(2rem,3vw,2.75rem);
--text-display-s:1.5rem;
--text-body-l:1.25rem;
--text-body:1rem;
--text-body-s:0.875rem;
--text-label:0.75rem;
--leading-display:1.05;
--leading-heading:1.2;
--leading-body:1.6;
--weight-light:300;
--weight-regular:400;
--weight-medium:500;
--weight-semibold:600;
--weight-bold:700;
/* PT body carries Playfair OR Fira Sans Light italic depending on page
   (HANDOFF §5 PT, §5 ITS deviations on Our way of Low & Slow, A day in the
   life, Elena's bio). Pages that deviate set --fm-pt-body themselves. */
--fm-pt-body:'Playfair','Playfair Display',"Times New Roman",serif;
}

/* tokens/spacing.css */
:root{
--space-1:4px;
--space-2:8px;
--space-3:12px;
--space-4:16px;
--space-5:24px;
--space-6:32px;
--space-7:48px;
--space-8:64px;
--space-9:96px;
--space-10:128px;
--container-max:1200px;
--container-pad:clamp(20px,5vw,64px);
}

/* tokens/effects.css */
:root{
--radius-s:2px;
--radius-m:4px;
--radius-l:6px;
--radius-pill:999px;
--border-hairline:1px solid var(--color-border);
--border-strong:1px solid var(--color-border-strong);
--shadow-card:0 1px 2px rgba(35,35,35,0.06),0 4px 16px rgba(35,35,35,0.08);
--shadow-elevated:0 8px 32px rgba(35,35,35,0.16);
--shadow-inset:inset 0 1px 3px rgba(35,35,35,0.12);
--ease-standard:cubic-bezier(0.4,0,0.2,1);
--duration-fast:120ms;
--duration-standard:200ms;
}

/* tokens/fonts.css — Fira Sans, self-hosted. Both scripts are subsetted
   woff2 (~15-20KB each), scoped with unicode-range — a browser fetches a
   file only when a codepoint in its range appears on the page. Greek was
   ~450KB per weight as shipped from _ds/ (full unsubsetted static TTFs);
   Phase 5 (HANDOFF §3/§23 item 4) ran `pyftsubset --unicodes=U+0370-03FF,
   U+1F00-1FFF --flavor=woff2` on all four, verified against the original
   TTFs' cmap so no Greek glyph was lost in the process. */
@font-face{font-family:'Fira Sans';font-style:italic;font-weight:400;font-display:swap;src:url('assets/fonts/FiraSans-400i.woff2') format('woff2');unicode-range:U+0000-024F,U+0259,U+1E00-1EFF,U+2000-206F,U+2074,U+20A0-20CF,U+2113,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;}
@font-face{font-family:'Fira Sans';font-style:normal;font-weight:300;font-display:swap;src:url('assets/fonts/FiraSans-300.woff2') format('woff2');unicode-range:U+0000-024F,U+0259,U+1E00-1EFF,U+2000-206F,U+2074,U+20A0-20CF,U+2113,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;}
@font-face{font-family:'Fira Sans';font-style:normal;font-weight:400;font-display:swap;src:url('assets/fonts/FiraSans-400.woff2') format('woff2');unicode-range:U+0000-024F,U+0259,U+1E00-1EFF,U+2000-206F,U+2074,U+20A0-20CF,U+2113,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;}
@font-face{font-family:'Fira Sans';font-style:normal;font-weight:500;font-display:swap;src:url('assets/fonts/FiraSans-500.woff2') format('woff2');unicode-range:U+0000-024F,U+0259,U+1E00-1EFF,U+2000-206F,U+2074,U+20A0-20CF,U+2113,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;}
@font-face{font-family:'Fira Sans';font-style:normal;font-weight:600;font-display:swap;src:url('assets/fonts/FiraSans-600.woff2') format('woff2');unicode-range:U+0000-024F,U+0259,U+1E00-1EFF,U+2000-206F,U+2074,U+20A0-20CF,U+2113,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;}
@font-face{font-family:'Fira Sans';font-style:normal;font-weight:700;font-display:swap;src:url('assets/fonts/FiraSans-700.woff2') format('woff2');unicode-range:U+0000-024F,U+0259,U+1E00-1EFF,U+2000-206F,U+2074,U+20A0-20CF,U+2113,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;}
@font-face{font-family:'Fira Sans';font-style:normal;font-weight:400;font-display:swap;src:url('assets/fonts/FiraSans-400-greek.woff2') format('woff2');unicode-range:U+0370-03FF,U+0384-0385,U+1F00-1FFF;}
@font-face{font-family:'Fira Sans';font-style:italic;font-weight:400;font-display:swap;src:url('assets/fonts/FiraSans-400i-greek.woff2') format('woff2');unicode-range:U+0370-03FF,U+0384-0385,U+1F00-1FFF;}
@font-face{font-family:'Fira Sans';font-style:normal;font-weight:500;font-display:swap;src:url('assets/fonts/FiraSans-500-greek.woff2') format('woff2');unicode-range:U+0370-03FF,U+0384-0385,U+1F00-1FFF;}
@font-face{font-family:'Fira Sans';font-style:normal;font-weight:700;font-display:swap;src:url('assets/fonts/FiraSans-700-greek.woff2') format('woff2');unicode-range:U+0370-03FF,U+0384-0385,U+1F00-1FFF;}
/* Fira Sans 300-italic is loaded from Google Fonts in partials/head.html
   until it is self-hosted (HANDOFF §4) — no woff2 for it ships in this
   package yet. Flagged as an open item for Phase 5. */


/* ── 2. Seasonal edition variable (HANDOFF §4) ───────────────────────────
   Only --season-accent is hand-edited; everything else derives from it.
   Defined globally (matching every prototype's own <style> block) but
   consumed ONLY inside .fm-season-* rules below, scoped to Season Menu
   S2/S3 markup — CLAUDE.md rule 5: never let it leak further than that.
   Summer/#cf7a3c are substituted at build
   time from edition.json (HANDOFF §23) — see EDITION.md to change either. */
:root{
  --season-name:"Summer";
  /* The four season accents (EDITION.md has the full runbook):
     Summer #cf7a3c ripe peach · Autumn #8a5a2b russet
     Spring #7f8f4e thyme      · Winter #5c6b74 slate */
  --season-accent:#cf7a3c;

  --season-accent-deep:color-mix(in oklab,var(--season-accent) 74%,#37200c);
  --season-accent-tint:color-mix(in oklab,var(--season-accent) 38%,var(--fm-oyster));
  --season-ground:color-mix(in oklab,var(--season-accent) 11%,var(--fm-oyster));
  --season-ground-deep:color-mix(in oklab,var(--season-accent) 19%,var(--fm-oyster));
  --season-voice:color-mix(in oklab,var(--season-accent) 52%,#2b1a34);
  --season-ink:var(--fm-charcoal);
  --season-ink-soft:var(--color-text-muted);
  --season-rule:var(--season-accent-tint);
  --season-mark:var(--season-accent-deep);
  --season-mark-tint:var(--season-rule);
  --season-label:var(--season-accent-deep);
}


/* ── 1b. Photo crops (HANDOFF §23 item 4) ─────────────────────────────────
   Every photograph's object-position lives here, one custom property per
   slot, and nowhere else — an <img> only ever carries a stable data-fm-crop
   slug in markup. Re-cropping a photo for a re-shoot or a new edition is
   changing one value below; it is never a markup edit. Three slots crop
   differently at a specific width — the override sits in the same media
   queries the rest of the responsive system already uses, right after. */
:root{
  --crop-landing-hero:55% 45%;
  --crop-landing-s3:50% 50%;
  --crop-season-menu-hero:60% 40%;
  --crop-season-menu-closing:50% 50%;
  --crop-low-slow-hero:50% 50%;
  --crop-low-slow-s3:50% 50%;
  --crop-low-slow-s5:50% 50%;
  --crop-experiences-s2:50% 50%;
  --crop-experiences-s3:50% 40%;
  --crop-experiences-s4:50% 40%;
  --crop-experiences-s5:50% 45%;
  --crop-bar-hero:50% 45%;
  --crop-bar-s3:50% 45%;
  --crop-story-hero:50% 62%;
  --crop-story-s3:50% 50%;
  --crop-story-s4:50% 45%;
  --crop-story-closing:50% 50%;
  --crop-restless-kitchen-hero:50% 50%;
  --crop-restless-kitchen-s3:50% 50%;
  --crop-restless-kitchen-s4:50% 40%;
  --crop-restless-kitchen-closing:50% 45%;
  --crop-day-in-life-hero:50% 56%;
  --crop-day-in-life-s3:50% 58%;
  --crop-day-in-life-s4:50% 50%;
  --crop-day-in-life-closing:50% 50%;
  --crop-elena-hero:50% 40%;
  --crop-elena-s3:50% 45%;
}
[data-fm-crop="landing-hero"]{object-position:var(--crop-landing-hero);}
[data-fm-crop="landing-s3"]{object-position:var(--crop-landing-s3);}
[data-fm-crop="season-menu-hero"]{object-position:var(--crop-season-menu-hero);}
[data-fm-crop="season-menu"]{object-position:var(--crop-season-menu-closing);}
[data-fm-crop="low-slow-hero"]{object-position:var(--crop-low-slow-hero);}
[data-fm-crop="low-slow-s3"]{object-position:var(--crop-low-slow-s3);}
[data-fm-crop="low-slow-s5"]{object-position:var(--crop-low-slow-s5);}
[data-fm-crop="experiences-s2"]{object-position:var(--crop-experiences-s2);}
[data-fm-crop="experiences-s3"]{object-position:var(--crop-experiences-s3);}
[data-fm-crop="experiences-s4"]{object-position:var(--crop-experiences-s4);}
[data-fm-crop="experiences-s5"]{object-position:var(--crop-experiences-s5);}
[data-fm-crop="bar-hero"]{object-position:var(--crop-bar-hero);}
[data-fm-crop="bar-s3"]{object-position:var(--crop-bar-s3);}
[data-fm-crop="story-hero"]{object-position:var(--crop-story-hero);}
[data-fm-crop="story-s3"]{object-position:var(--crop-story-s3);}
[data-fm-crop="story-s4"]{object-position:var(--crop-story-s4);}
[data-fm-crop="story-closing"]{object-position:var(--crop-story-closing);}
[data-fm-crop="restless-kitchen-hero"]{object-position:var(--crop-restless-kitchen-hero);}
[data-fm-crop="restless-kitchen-s3"]{object-position:var(--crop-restless-kitchen-s3);}
[data-fm-crop="restless-kitchen-s4"]{object-position:var(--crop-restless-kitchen-s4);}
[data-fm-crop="restless-kitchen-closing"]{object-position:var(--crop-restless-kitchen-closing);}
[data-fm-crop="day-in-life-hero"]{object-position:var(--crop-day-in-life-hero);}
[data-fm-crop="day-in-life-s3"]{object-position:var(--crop-day-in-life-s3);}
[data-fm-crop="day-in-life-s4"]{object-position:var(--crop-day-in-life-s4);}
[data-fm-crop="day-in-life-closing"]{object-position:var(--crop-day-in-life-closing);}
[data-fm-crop="elena-hero"]{object-position:var(--crop-elena-hero);}
[data-fm-crop="elena-s3"]{object-position:var(--crop-elena-s3);}
/* Desktop-only: Season Menu's closing LCM crops the plate lower once there
   is room for it (deliberate, HANDOFF §4/§5 — not a bug). */
@media (min-width:821px){
  :root{ --crop-season-menu-closing:50% 68%; }
}
/* Mobile-only: these two crop further into frame once the subject has less
   width to share with margin. */
@media (max-width:700px){
  :root{
    --crop-experiences-s2:22% 50%;
    --crop-low-slow-s5:50% 78%;
  }
}


/* ── 3. Reset and base ────────────────────────────────────────────────── */
html,body{margin:0;padding:0;background:var(--fm-oyster);}
/* background-color, not left to the browser's default white: a thin
   light-coloured line was showing at the very bottom of the footer —
   almost certainly a sub-pixel compositing seam at the edge of
   [data-fm-closing]'s opacity:0.999 layer (itself a deliberate Safari 26
   workaround, elsewhere in this file) briefly revealing whatever sits
   beneath body. With nothing set here, that was html's UA-default white. */
html{scroll-behavior:smooth;background-color:var(--fm-charcoal);}
/* Native scroll-snap, not a JS-driven scrollTo (former approach, HANDOFF
   §6): a JS glide() racing the browser's own touch-momentum physics is
   exactly what caused sections to visibly fight — lock, jitter, unlock —
   on a real phone. The compositor already reconciles snapping with
   momentum on its own. mandatory, not proximity: proximity leaves the
   browser latitude in exactly where it settles — "close enough" isn't
   pixel-exact. mandatory forces exact alignment to scroll-snap-align every
   time. Gated on reduced-motion, same as the JS version was.
   Snap precision was never the cause of the "sections not covering the
   viewport" bug seen separately on real devices — that persisted with
   reduced motion on (snap fully inactive) and turned out to be lvh itself
   under-reporting the true screen height; see the --vh comment in site.js
   and the calc(var(--vh,1lvh) * N) rules below.

   Gated on html.fm-snap-on, which partials/head.html adds on the reader's
   first touch/wheel/key — NOT from first paint. This is the confirmed
   cause of the "new page opens scrolled to where the last one was" bug on
   iOS, isolated by bisection on bare test pages: vanilla HTML carrying
   nothing but scroll-snap-type on the root reproduces it, with both
   mandatory and proximity, while the same pages without it (and with
   scroll-behavior:smooth, lvh-sized blocks, or the third-party scripts,
   each tried alone) never do. On a fresh navigation Safari appears to run
   a snap-to-nearest pass during early layout against the offset still
   held by the reused native scroll view, rather than the new, still-empty
   document — which is why it lands exactly on section boundaries, and why
   it is mobile-only. Deferring activation to the first real interaction
   means snap simply isn't on during page load, when that rescue happens
   and no user is involved; by the time anyone can scroll, the document is
   settled. A fixed delay and window 'load' were both tried on the same
   harness and both failed — a slow cold parse outruns a timer, and 'load'
   fires almost immediately on a light page. Interaction is the one
   trigger that doesn't depend on load timing at all. */
@media (prefers-reduced-motion:no-preference){
  html.fm-snap-on{scroll-snap-type:y mandatory;}
}
/* background-color, not left to default white: Safari 26 dropped
   theme-color support for tinting the status-bar/notch area — it now
   reads body's own background-color instead (or a fixed element's, if
   that element's background-color is itself opaque; this site's nav is
   deliberately transparent much of the time, so it can't be that
   source). A static charcoal, not tracked live with scroll (tried and
   reverted): Safari doesn't actually re-sample it after load anyway, so
   tracking the current section's tone did nothing for the status bar and
   only caused a real regression elsewhere — the one place body's own
   background becomes visible in practice is the small gap below the
   deliberately-undersized [data-fm-closing] box, which sits directly
   against the footer, always charcoal. Tracking hero tone there made
   that gap read as oyster against a charcoal footer instead of matching
   it. */
body{font-family:var(--font-body);font-weight:300;color:var(--color-text);-webkit-font-smoothing:antialiased;background-color:var(--fm-charcoal);}
img{display:block;max-width:100%;}
a{color:var(--fm-brass-dark);text-decoration:none;}
a:hover{color:var(--fm-brass);}
@media (max-width:700px){br[data-desk]{display:none;}}

/* Keyboard focus (HANDOFF §19 item 1) — nothing styled focus before this;
   the site was unusable by keyboard. Brass reads on the Oyster ground
   everywhere by default; the light-on-dark variant covers the footer,
   the menu overlay and every [data-nav="dark"] section (HANDOFF §6's own
   convention for "this section sits on Charcoal or over photography"). */
:focus-visible{outline:2px solid var(--fm-brass);outline-offset:3px;}
.fm-footer :focus-visible,.fm-overlay :focus-visible,[data-nav="dark"] :focus-visible{outline-color:var(--fm-oyster);}

/* Full-viewport sections read --vh, a px value site.js pins to
   screen.height — a static device property, not a live one. Every live
   viewport metric tried first (native lvh, native dvh, a visualViewport
   --vh, an innerHeight-max --vh, a clientHeight --vh) failed a real-device
   test differently each time; screen.height is the one number nothing
   here can get out of sync with. calc(var(--vh,1lvh) * N) keeps lvh only
   as the fallback for the moment before site.js's first run.

   --fm-toolbar-buffer: since screen.height is deliberately taller than
   whatever's currently visible whenever Safari's toolbar isn't at its
   most collapsed (117-157px measured on one real device), anything
   positioned near a screen.height-tall box's true bottom edge — the
   scroll indicator below, the closing section's footer (§5), the corner
   back-link — needs to sit at least this far up from it to reliably land
   inside the visible area rather than behind the toolbar.

   0 by default, 180px only on touch/phone (the same (hover:none) or
   max-width:700px test site.js uses for --vh itself, and that the LCM
   touch handling already uses). All of this exists for one thing — a
   phone browser's collapsing toolbar — and a desktop window has no such
   toolbar. Applied unconditionally it shrank every closing section and
   lifted the scroll indicator ~180px on desktop for nothing, and worse,
   site.js was sizing every section to screen.height there too — the
   monitor, not the window — leaving every section ~136px taller than the
   viewport on an 820px-tall window. Desktop gets plain lvh and no buffer,
   which is exactly the state that was reviewed and approved. */
:root{--fm-toolbar-buffer:0px;}
@media (hover:none), (max-width:700px){
  :root{--fm-toolbar-buffer:180px;}
}
.fm-vh{height:calc(var(--vh,1lvh) * 100);}

/* Image reveal on section entry (HANDOFF §6) — one generic rule, since CM,
   ITS, LCM and ECM photography all use the same class and site.js targets
   it globally. The first hero on a page never carries this class (it's
   visible from load, loading="eager"). */
.fm-reveal{opacity:0;transform:scale(1.04);transition:opacity 900ms ease,transform 1400ms cubic-bezier(.22,1,.36,1);}
@media (prefers-reduced-motion:reduce){
  /* HANDOFF §19 item 4 — the reveal ignored reduced motion; snap-scroll
     already gated itself the same way. site.js still sets the final
     opacity/transform inline either way, so removing the transition here
     is enough: the image appears in its resting state with no animation. */
  .fm-reveal{transition:none;}
}
.fm-min-vh{min-height:calc(var(--vh,1lvh) * 100);}


/* ── 4. Section types — six layouts carry the whole site (HANDOFF §5) ───
   One class set per type, reused everywhere; page content differs, the
   layout never does. Per-instance sizing that HANDOFF documents as unique
   per headline (the CM title-size table) is passed in as two custom
   properties on the element — --cm-title-size / --cm-title-measure — never
   as a new one-off class or a full inline style. */

/* ---- CM — Cinematic Minimal ------------------------------------------- */
.fm-cm{position:relative;height:calc(var(--vh,1lvh) * 100);min-height:var(--cm-min-height,520px);overflow:hidden;background:var(--fm-charcoal);}
/* Fills the whole [data-fm-closing] box (position:relative there) instead
   of sharing it via flex with the footer — see that rule's own comment
   for why: this keeps the photo covering the full box regardless of the
   footer, which is independently pinned near the bottom over it. */
.fm-cm[data-fm-last]{position:absolute;inset:0;height:auto;min-height:0;}
.fm-cm__media{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;}
.fm-cm__media[loading="eager"]{opacity:1;} /* first hero on the page: no reveal */
/* Prototype scrim values, kept. A two-thirds-strength trial was tried on
   device and reverted here — client preferred the original depth for
   the standard CM. The one exception is the Story hero, whose inline
   override in story/index.html keeps its lightened stops: that photo
   had by far the darkest scrim on the site and read as under-exposed. */
.fm-cm__scrim{position:absolute;inset:0;background:linear-gradient(to top,rgba(20,18,16,0.72) 0%,rgba(20,18,16,0.50) 42%,rgba(20,18,16,0.36) 68%,rgba(20,18,16,0.42) 100%);}
.fm-cm__overlay{position:absolute;inset:0;padding:clamp(96px,13vh,150px) clamp(20px,4vw,56px);display:flex;flex-direction:column;justify-content:center;gap:20px;box-sizing:border-box;}
.fm-cm__eyebrow{margin:0;font-family:var(--font-body);font-weight:600;font-size:clamp(11px,1.05vw,13px);letter-spacing:0.24em;text-transform:uppercase;color:var(--fm-brass-light);}
.fm-cm__title{margin:0;font-family:var(--font-display);font-style:italic;font-weight:400;line-height:var(--cm-title-line,1.08);letter-spacing:-0.005em;color:var(--fm-oyster);text-wrap:pretty;
  font-size:var(--cm-title-size,clamp(44px,7vw,104px));max-width:var(--cm-title-measure,min(1120px,80vw));}
.fm-cm__title span{display:block;}
/* Our Barbecue Story S3/S6 only: each phrase is its own span so the layout
   can force a line break per phrase, but that break is wanted on mobile
   only — desktop reads it as one running line. Overriding the per-span
   margin-left/margin-top here too: those inline styles stagger the phrases
   vertically for the forced-break layout and would otherwise show up as
   uneven gaps between words once the spans run inline. */
@media (min-width:701px){
  .fm-cm__title[data-fm-break="mobile"] span{display:inline;margin-left:0 !important;margin-top:0 !important;}
}
/* A single forced break at one exact point in the running desktop line —
   everywhere else the phrases flow and wrap naturally. A real <br> here
   doesn't work: setting it to display:inline (needed to toggle it off on
   mobile) disables its own break behaviour in every tested browser, so the
   line just wrapped wherever it naturally would instead. Hidden by default
   (mobile's own multi-line layout doesn't use it, HANDOFF §5 phrasing is
   unaffected). */
.fm-cm__title .fm-cm__linebreak{display:none;}
@media (min-width:701px){
  /* display:block: a block box splits the inline formatting context
     outright instead of occupying a line within it, so — unlike an
     inline-block, even one zeroed to height/line-height/font-size:0 — it
     carries none of that line's own "strut" (CSS2.1 §10.8, inherited from
     the *heading's* font/line-height regardless of the child's own
     properties), which is what doubled the gap between the two text lines
     when this was inline-block. */
  .fm-cm__title[data-fm-break="mobile"] .fm-cm__linebreak{display:block;height:0;}
  /* The second line hangs in by the width of the first line's own leading
     capital — measured per instance (Playfair Display italic capitals
     vary a lot: "W" is ~0.85em, "J" ~0.58em), not a shared guess. */
  .fm-cm__title[data-fm-break="mobile"] .fm-cm__break-indent{margin-left:var(--indent,0.6em) !important;}
}
/* Season Menu S1 only: the haiku rule (HANDOFF §5) — the opening quotation
   mark hangs off the first line, and each sentence gets breathing room
   above it. Scoped to [data-fm-haiku] since the ordinary multi-span CM
   titles (Landing S1/S3) do not carry this treatment. */
.fm-cm__title[data-fm-haiku] span:first-child{text-indent:-0.42em;}
.fm-cm__title[data-fm-haiku] span:not(:first-child){margin-top:0.42em;}
.fm-haiku-br{display:none;}
@media (max-width:700px){.fm-haiku-br{display:block;}}
/* Landing S1 only: the Bib Gourmand badge row beneath the CM title. */
.fm-cm__badges{display:flex;align-items:center;gap:16px;margin-top:clamp(16px,2.6vh,30px);}
.fm-cm__badge{width:clamp(58px,5.2vw,78px);height:auto;flex-shrink:0;}
/* bottom offset includes --fm-toolbar-buffer (§4): the hero's own box is
   screen.height tall on purpose — shrinking the section itself would
   reopen the exact sliver bug this site spent a whole night chasing
   down, since there's a genuinely different section right after it. 26px
   alone from screen.height's true bottom landed the icon in the part of
   the box that's often not actually on screen (Safari's toolbar not
   always being at its most collapsed), invisible until the first scroll
   pulled it into view. */
.fm-scroll-ind{position:absolute;bottom:calc(26px + var(--fm-toolbar-buffer) + env(safe-area-inset-bottom,0px));left:50%;transform:translateX(-50%);display:flex;flex-direction:column;align-items:center;gap:8px;opacity:var(--sc-o,1);transition:opacity 520ms ease;pointer-events:none;}
.fm-scroll-ind__rule{width:1px;height:34px;background:linear-gradient(to bottom,rgba(173,138,83,0) 0%,var(--fm-brass) 100%);display:block;}
.fm-scroll-ind__label{font-family:var(--font-body);font-size:10px;letter-spacing:0.24em;text-transform:uppercase;color:var(--fm-brass-light);}
@media (max-width:700px){
  /* One rule, one authored number: --cm-vw is the fitted mobile size, set
     per instance exactly like --cm-title-size above. See HANDOFF §5 note on
     recomputing it when the copy changes. */
  .fm-cm__title{font-size:clamp(44px,calc(var(--cm-vw,18.5) * 1vw),96px);line-height:1.02;letter-spacing:-0.02em;max-width:none;}
  /* Every CM title line flush left on a phone. The staggered indents are a
     desktop composition — per-instance inline margin-left/padding-left on
     individual spans, plus .fm-cm__break-indent's own hanging indent — and
     at phone widths they only push long words off the right edge instead of
     reading as a stagger. Desktop is untouched: this lives inside the
     max-width:700px block. Specificity has to match .fm-cm__title
     [data-fm-break="mobile"] .fm-cm__break-indent (0-3-0) above, which is
     itself !important, hence both here. */
  .fm-cm__title span,
  .fm-cm__title[data-fm-break="mobile"] .fm-cm__break-indent{margin-left:0 !important;padding-left:0 !important;}
  /* A closing CM ([data-fm-closing]) has a box 180px shorter than a
     normal one on a phone and shares it with the footer, yet its title
     was sized by the same --cm-vw as a full-height hero — A day in the
     life's "Feel like joining the team?" came to 440px of content in
     413px of room, measured. Title scaled by the closing box's own ratio
     (~672/852), and the overlay reserves the nav above and the footer
     below so centring lands between them. !important on the padding:
     that instance carries an inline padding-bottom for its desktop
     composition which would otherwise outrank this. */
  [data-fm-closing] .fm-cm__title{font-size:clamp(34px,calc(var(--cm-vw,18.5) * 0.78vw),72px);}
  .fm-cm[data-fm-last] .fm-cm__overlay{padding-top:calc(var(--fm-nav-h,93px) + 8px) !important;padding-bottom:calc(var(--fm-footer-h,176px) + 12px) !important;}
}

/* ---- PT — Pure Typographic --------------------------------------------
   Landing tier vs narrative/deep tier differ only in size (HANDOFF §5); set
   --pt-heading-size / --pt-heading-measure / --pt-body-size / --pt-body-measure
   on .fm-pt per instance, falling back to the narrative/deep tier. */
/* position:relative: Dining Experiences opens on a PT (the only PT hero)
   and carries the scroll indicator like every CM hero does. Without a
   positioned ancestor the indicator's bottom offset resolved against the
   initial containing block instead of the section — innerHeight on a
   phone, not the section's screen.height box — and sat ~117px too high. */
.fm-pt{position:relative;min-height:calc(var(--vh,1lvh) * 100);display:flex;align-items:center;background:var(--fm-oyster);padding:clamp(80px,12vh,120px) clamp(20px,4vw,56px);
  /* Clearance is measured off the fixed nav's real height (site.js), not a
     flat floor — the old 80px minimum sat just under the nav's own ~88-93px
     at the short end of its clamp, so a locked section's heading could
     start partly behind the bar. */
  padding-top:calc(var(--fm-nav-h,88px) + clamp(24px,4vh,56px));
  box-sizing:border-box;}
/* This variant shares its 100dvh box with the footer (§5, [data-fm-closing])
   instead of getting a full viewport to itself, so — unlike every other PT
   instance — its available height genuinely varies independently of width.
   The heading/body sizes elsewhere are width-driven (vw) only, which is
   correct when the section always gets 100dvh, but left this one free to
   overflow its own (frequently shorter) box on a wide-but-short viewport,
   pushing its top behind the nav and its bottom behind the footer. Re-uses
   the --pt-heading-size/--pt-body-size hooks every .fm-pt__heading/__body
   already read from (see below), so it wins regardless of which tier's
   font-size rule would otherwise apply, and takes whichever of the width-
   or height-based figure is smaller. */
/* Fills [data-fm-closing]'s full box — see that rule's own comment for
   why this replaced sharing the space via flex with the footer. */
.fm-pt[data-fm-last]{position:absolute;inset:0;min-height:0;
  --pt-heading-size:clamp(22px,min(4.2vw,5.5vh),62px);
  --pt-body-size:clamp(14px,min(2.8vw,3.4vh),39px);
}
.fm-pt[data-fm-last] .fm-pt__body{gap:clamp(8px,min(1.75vw,1.8vh),25px);}
.fm-pt__inner{max-width:1080px;margin:0 auto;width:100%;}
/* Dining Experiences S1 only so far: an optional eyebrow above the PT
   heading, Brass Dark (not Brass Light — this PT sits on Oyster, not
   photography). */
.fm-pt__eyebrow{display:block;margin:0 0 clamp(14px,1.8vh,22px);font-family:var(--font-body);font-weight:600;font-size:clamp(11px,1.05vw,13px);letter-spacing:0.24em;text-transform:uppercase;color:var(--fm-brass-dark);}
.fm-pt__heading{margin:0 0 clamp(24px,3.4vh,42px);font-family:var(--font-display);font-style:italic;font-weight:500;line-height:1.16;letter-spacing:-0.005em;color:var(--fm-brass-dark);text-wrap:pretty;
  font-size:var(--pt-heading-size,clamp(30px,3.6vw,52px));max-width:var(--pt-heading-measure,min(900px,84vw));}
.fm-pt__body{display:flex;flex-direction:column;gap:clamp(15px,1.75vw,25px);}
.fm-pt__body p{margin:0;font-family:var(--fm-pt-body);font-style:italic;font-weight:400;letter-spacing:-0.003em;color:var(--fm-charcoal);text-wrap:pretty;
  font-size:var(--pt-body-size,clamp(19px,2vw,28px));line-height:1.5;max-width:var(--pt-body-measure,min(860px,82vw));}
.fm-pt[data-fm-tier="landing"] .fm-pt__heading{font-size:var(--pt-heading-size,clamp(34px,4.2vw,62px));max-width:var(--pt-heading-measure,min(960px,86vw));}
/* FLAGGED: HANDOFF §5's PT table gives the landing-tier body as
   clamp(22px,2.5vw,35px), but the actual prototype markup (It is a very
   Fine Mess.dc.html, S2 and S5) sets clamp(24px,2.8vw,39px). Prototype wins
   per CLAUDE.md's resolution rule — it is the working code. */
.fm-pt[data-fm-tier="landing"] .fm-pt__body p{font-size:var(--pt-body-size,clamp(24px,2.8vw,39px));line-height:1.42;}
.fm-pt__link{display:inline-flex;align-items:center;gap:11px;margin-top:clamp(28px,4vh,44px);font-family:var(--font-body);font-weight:600;font-size:13px;letter-spacing:0.16em;text-transform:uppercase;color:var(--fm-brass-dark);border-bottom:1px solid var(--fm-brass);padding-bottom:5px;transition:color 200ms ease,border-color 200ms ease;}
.fm-pt__link:hover{color:var(--fm-brass);border-color:var(--fm-brass);}
@media (max-width:700px){
  /* padding-bottom reserves the live --dead-zone (site.js; 0 on desktop)
     so the flex centring lands content in the band that's actually
     visible above Safari's toolbar, not in the full screen.height box —
     the same treatment .fm-ptl/.fm-itl get. Without it the tallest
     narrative PT (Restless Kitchen S2) centred low and its last lines
     ran under the toolbar, and the section overflowing the visible band
     is what made its snap feel wrong. */
  .fm-pt{padding-top:var(--fm-nav-h,93px);padding-bottom:calc(24px + var(--dead-zone,0px) + env(safe-area-inset-bottom,0px));}
  .fm-pt__inner{padding-right:0;max-width:none;padding-bottom:24px;}
  /* Full inner width on a phone — the desktop measures (82vw body, 84-86vw
     heading) exist to cap line length on wide screens and only throw
     away ~30px of a 350px column here, adding lines to already-long
     copy. Specificity matches the landing-tier heading rule (0-3-1) so
     this wins on order rather than losing to it. */
  .fm-pt__heading,.fm-pt[data-fm-tier="landing"] .fm-pt__heading,.fm-pt__body p{max-width:none;}
  /* Narrative/deep-tier PT on a phone. This rule is what those tiers
     actually render at (the landing tier's own rule outranks it), and a
     flat 15px was well below legible next to the landing tier's 24px on
     the same device. 18px is the largest the longest narrative PT on the
     site (Call us Old Fashioned S4, ~500 chars over three paragraphs)
     still fits inside one screen at. Heading floor lifted to match. */
  .fm-pt__body p{font-size:18px;line-height:1.55;}
  .fm-pt__heading{font-size:32px;}
  /* The [data-fm-last] size override above is min(vw,vh) — built for a
     wide-but-SHORT desktop window, where the vh term is the small one. On a
     narrow, tall phone that inverts: the vw term collapses (4.2vw is 16px at
     390px wide) and the clamp floors, 22px/14px, are what actually render —
     against 34px/24px for every other landing-tier PT, which is the
     inconsistency. Explicit mobile sizes instead.

     The box is shared with the footer overlaying its bottom: padding-bottom
     reserves the footer's own measured height (--fm-footer-h, site.js) so
     the centred content sits above it, and padding-top reserves the fixed
     nav so centred content can't drift up behind that either. A first pass
     added env(safe-area-inset-bottom) on top of a flat footer guess — but
     the footer's own padding already includes the safe area, so that
     double-counted ~54px and is why the text had to stay smaller than it
     needed to. 32px/22px is what fits within that box once the reserve is
     measured rather than guessed; 34/24 (S2's) would still put the closing
     lines behind the footer with this much copy. */
  .fm-pt[data-fm-last]{
    --pt-heading-size:32px;
    --pt-body-size:22px;
    padding-top:calc(var(--fm-nav-h,93px) + 12px);
    padding-bottom:calc(var(--fm-footer-h,176px) + 12px);
  }
  .fm-pt[data-fm-last] .fm-pt__inner{padding-bottom:0;}
  .fm-pt[data-fm-last] .fm-pt__body{gap:12px;}
  /* Every ECM's text overlay is anchored bottom:0 of a box sized to
     screen.height (Dining Experiences S2-S4) — on a phone that puts the
     title, body and CTA in the toolbar gap, below what's actually visible.
     Lifted by the live --dead-zone (site.js; 0 on desktop), the same
     treatment .fm-its's text strip and the Low & Slow sections get.
     Absolute-in-a-section, so this DOES need the gap term — unlike the
     fixed corner link, which measures against the visible viewport.
     .fm-ecm .fm-ecm__overlay, not the bare class: this block precedes the
     ECM base rules in the file, and at equal specificity the later
     bottom:0 there would win on source order. */
  .fm-ecm .fm-ecm__overlay{bottom:calc(var(--dead-zone,0px) + env(safe-area-inset-bottom,0px));}
  /* Closing-section CTAs (LCM's "Reserve", ECM's) are anchored bottom:0 of
     a box the footer now overlays — lifted to sit clear of it. Outranks
     the rule above for the closing ECM by specificity. */
  .fm-lcm[data-fm-last] .fm-lcm__overlay,
  .fm-ecm[data-fm-last] .fm-ecm__overlay{bottom:var(--fm-footer-h,176px);}
}

/* ---- ITS — Image + Text Strip ------------------------------------------ */
/* height, not min-height: .fm-its__media below is flex:1, relying on this
   container having *distributable free space* to grow into. A min-height
   floor is ambiguous about whether it counts as that free space — Safari
   has documented history of not reliably treating it as such, unlike a
   definite height. Matches .fm-cm's own pattern, which never had this bug
   because it was already a definite height, not a floor. */
/* padding-bottom reserves a content-free strip at the very bottom of the
   box: --vh (screen.height) is deliberately taller than the *currently*
   visible area whenever Safari's toolbar isn't fully collapsed — real
   device data showed innerHeight sitting 117-157px short of screen.height
   at different moments on the same device. The section's own background
   safely fills the whole box regardless (that's the point of screen.height
   — no sliver), but flex content (media+strip below) only gets to lay out
   in the box's shorter, safe portion, so text/links can't land in the part
   that may currently be hidden under the toolbar or below the fold. */
.fm-its{height:calc(var(--vh,1lvh) * 100);padding-bottom:calc(var(--dead-zone,0px) + env(safe-area-inset-bottom,0px));display:flex;flex-direction:column;background:var(--fm-oyster);box-sizing:border-box;}
/* min-height default dropped from 50% to 30%: in flexbox, an explicit
   min-height is a hard floor the algorithm cannot shrink below, no matter
   how much room the text sibling needs — on the two longest-copy ITS
   instances (Entertain Your Curiosity, the menu page's own ITS), 50% left
   too little room for .fm-its__strip's text, which then overflowed the
   fixed-height section straight into the toolbar area instead of being
   fit inside it. --its-media-min can still override this per instance
   for a page whose copy is short enough to spare a taller photo. */
.fm-its__media{flex:1;min-height:var(--its-media-min,calc(var(--vh,1lvh) * 30));position:relative;overflow:hidden;background:var(--fm-charcoal);}
.fm-its__media img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;}
/* flex-shrink:0: text always gets its natural size in full; .fm-its__media
   (flex:1) absorbs whatever's left, even below its own min-height default
   on the rare page whose copy genuinely needs more than 70% of the
   section — the alternative is exactly the overflow this is fixing. */
.fm-its__strip{flex-shrink:0;padding:clamp(34px,5vh,54px) clamp(20px,4vw,56px) calc(clamp(38px,6vh,60px) + env(safe-area-inset-bottom,0px));}
.fm-its__grid{max-width:1080px;margin:0 auto;display:grid;grid-template-columns:clamp(130px,18vw,210px) minmax(0,1fr);gap:clamp(16px,3vw,44px);align-items:start;}
.fm-its__heading{margin:0;font-family:var(--font-display);font-weight:500;font-size:clamp(23px,2.5vw,33px);line-height:1.16;color:var(--fm-charcoal);text-wrap:pretty;}
.fm-its__body{display:flex;flex-direction:column;gap:22px;}
.fm-its__body p{margin:0;font-size:clamp(15px,1.4vw,19px);line-height:1.65;color:var(--fm-charcoal);max-width:740px;text-wrap:pretty;}
.fm-its__links{display:flex;flex-wrap:wrap;gap:10px 26px;}
.fm-its__links a{font-family:var(--font-body);font-weight:600;font-size:13px;letter-spacing:0.13em;text-transform:uppercase;color:var(--fm-brass-dark);border-bottom:1px solid var(--fm-brass);padding-bottom:4px;transition:color 200ms ease,border-color 200ms ease;}
.fm-its__links a:hover{color:var(--fm-brass);border-color:var(--fm-brass);}
@media (max-width:700px){
  .fm-its__grid{grid-template-columns:minmax(0,1fr);gap:14px;}
  .fm-its__body p{max-width:none;}
}

/* ---- LCM — Linked Cinematic --------------------------------------------
   The entire section is a single <a>. Hover/focus and touch-viewport
   response are wired generically in site.js against these class hooks. */
.fm-lcm{position:relative;display:block;height:calc(var(--vh,1lvh) * 74);min-height:420px;overflow:hidden;background:var(--fm-charcoal);color:var(--fm-oyster);}
/* min-height:0, not the base rule's 420px floor: this variant shares its
   box with the footer in one fixed-height flex column ([data-fm-closing]),
   so it's meant to flex-shrink as needed to keep the footer's now-larger
   safe-area-aware padding fully inside that one screen. The 420px floor
   is real for a standalone LCM but was overflowing this pair past a
   single viewport, breaking the scroll-snap lock on the closing section. */
/* Fills [data-fm-closing]'s full box — see that rule's comment for why
   this replaced sharing the space via flex with the footer. */
.fm-lcm[data-fm-last]{position:absolute;inset:0;height:auto;min-height:0;}
.fm-lcm__media{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;}
.fm-lcm__scrim{position:absolute;inset:0;background:linear-gradient(to top,rgba(20,18,16,0.80) 0%,rgba(20,18,16,0.26) 52%,rgba(20,18,16,0.14) 100%);}
/* Season Menu S4's closing-LCM crop lives in the consolidated photo-crops
   block (§1b, HANDOFF §23 item 4) as --crop-season-menu-closing. A data
   attribute, not a shared #id — three pages each have their own closing
   LCM, and an id-scoped rule would leak across all of them. */
.fm-lcm__frame{position:absolute;pointer-events:none;transition:border-color 300ms ease,inset 300ms ease,top 300ms ease;display:block;border-width:1px;border-style:solid;border-color:var(--lcm-border,rgba(232,227,221,0.34));inset:var(--lcm-inset,16px);}
/* Modifier for the closing-wrapper LCM variant (Season Menu S4, Call us Old
   Fashioned S5, The Restless Kitchen S5, HANDOFF §4/§5): frame is transparent
   by default and its top edge clears the fixed nav band. It still answers
   --lcm-border from the generic hover/touch rule below. Phase 2 adds the
   extra geometry re-check (mousemove + scroll) HANDOFF asks for on these
   three instances, so a partly-scrolled section never shows a frame with
   its top edge cut off. */
.fm-lcm[data-fm-frame-armed] .fm-lcm__frame{border-color:var(--lcm-border,transparent);top:calc(89px + var(--lcm-inset,16px));}
.fm-lcm__overlay{position:absolute;left:0;right:0;bottom:0;padding:0 clamp(20px,4vw,56px) clamp(40px,7vh,72px);display:flex;flex-direction:column;gap:18px;max-width:820px;}
.fm-lcm__eyebrow{margin:0;font-family:var(--font-body);font-weight:600;font-size:clamp(11px,1vw,13px);letter-spacing:0.24em;text-transform:uppercase;color:var(--fm-brass-light);}
.fm-lcm__title{margin:0;font-family:var(--font-body);font-weight:600;font-size:clamp(26px,3.4vw,46px);line-height:1.16;letter-spacing:-0.01em;color:var(--fm-oyster);text-wrap:pretty;}
.fm-lcm__cta{display:inline-flex;align-items:center;gap:var(--lcm-gap,12px);font-family:var(--font-body);font-weight:600;font-size:13px;letter-spacing:0.16em;text-transform:uppercase;transition:gap 260ms ease,color 260ms ease;color:var(--lcm-cta,var(--fm-brass-light));}
.fm-lcm:hover,.fm-lcm:focus-visible,.fm-lcm[data-fm-hover]{--lcm-inset:10px;--lcm-border:var(--fm-brass-light);--lcm-gap:20px;--lcm-cta:var(--fm-oyster);}
.fm-lcm:hover .fm-lcm__media,.fm-lcm:focus-visible .fm-lcm__media,.fm-lcm[data-fm-hover] .fm-lcm__media{transform:scale(1.03);}
/* The LCM media image always carries .fm-reveal too (like every other
   photograph on the site) — its opacity/transform transition timing
   governs the hover-scale above as well, matching the prototype exactly:
   there is no separate, faster hover transition. */

/* ---- ECM — Experiential Cinematic (Dining Experiences only) ----------- */
.fm-ecm{position:relative;height:calc(var(--vh,1lvh) * 100);min-height:520px;overflow:hidden;background:var(--fm-charcoal);}
/* Fills [data-fm-closing]'s full box — see that rule's comment for why
   this replaced sharing the space via flex with the footer. */
.fm-ecm[data-fm-last]{position:absolute;inset:0;height:auto;min-height:0;}
.fm-ecm__media{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;}
.fm-ecm__scrim{position:absolute;inset:0;background:linear-gradient(to top,rgba(20,18,16,0.80) 0%,rgba(20,18,16,0.50) 38%,rgba(20,18,16,0.30) 66%,rgba(20,18,16,0.38) 100%);}
.fm-ecm__overlay{position:absolute;left:0;bottom:0;padding:0 clamp(20px,4vw,56px) clamp(52px,9vh,96px);max-width:min(720px,76vw);display:flex;flex-direction:column;align-items:flex-start;gap:12px;}
.fm-ecm__title{margin:0;font-family:var(--font-display);font-style:italic;font-weight:400;font-size:clamp(40px,5vw,74px);line-height:1.08;letter-spacing:-0.005em;color:var(--fm-oyster);text-wrap:pretty;}
.fm-ecm__body{margin:0;font-family:var(--font-body);font-weight:300;font-size:clamp(17px,1.6vw,23px);line-height:1.62;color:var(--fm-oyster);max-width:min(640px,70vw);text-wrap:pretty;}
.fm-ecm__cta{display:inline-flex;align-items:center;gap:11px;margin-top:12px;font-family:var(--font-body);font-weight:600;font-size:13px;letter-spacing:0.16em;text-transform:uppercase;color:var(--fm-charcoal);background:var(--fm-brass-light);padding:13px 22px;transition:background-color 220ms ease,gap 220ms ease;}
.fm-ecm__cta:hover{background:var(--fm-oyster);gap:18px;color:var(--fm-charcoal);}
/* Dining Experiences S2's mobile crop lives in the consolidated photo-crops
   block (§1b, HANDOFF §23 item 4) as --crop-experiences-s2's mobile value. */

/* No dot navigation: HANDOFF §5/§6 describe one, but it was an earlier
   design-stage idea, dropped before any prototype and confirmed abandoned
   by the client — not built here. */

/* ---- DOC — Document (footer utility pages) ----------------------------- */
.fm-doc{background:var(--fm-oyster);padding:clamp(104px,14vh,150px) clamp(20px,4vw,56px) clamp(70px,10vh,110px);box-sizing:border-box;}
.fm-doc__inner{max-width:1080px;margin:0 auto;}
.fm-doc__header{padding-bottom:26px;border-bottom:1px solid var(--fm-brass);}
.fm-doc__title{margin:0;font-family:var(--font-body);font-weight:600;font-size:clamp(28px,3.2vw,44px);line-height:1.14;letter-spacing:-0.01em;color:var(--fm-charcoal);}
.fm-doc__lang{display:flex;align-items:center;gap:10px;margin-top:18px;}
/* Active/inactive state is driven by the --lang-en-ink/--lang-el-ink (etc.)
   custom properties site.js's languageSwitch() sets on click (BUG fixed
   here: this rule originally keyed off [aria-pressed="true"], which
   nothing ever sets — aria-pressed itself is Phase 4's job, HANDOFF §19
   item 3 — so the buttons would switch content but never visually update
   which one is active). English is active by default, matching every
   prototype. */
.fm-doc__lang-btn{font-family:var(--font-body);font-weight:600;font-size:11px;letter-spacing:0.14em;text-transform:uppercase;padding:0;border:none;background:none;text-underline-offset:3px;cursor:pointer;transition:color 200ms ease;}
.fm-doc__lang-btn[data-lang="en"]{color:var(--lang-en-ink,var(--fm-charcoal));text-decoration-line:var(--lang-en-dec,none);}
.fm-doc__lang-btn[data-lang="el"]{color:var(--lang-el-ink,var(--fm-brass-dark));text-decoration-line:var(--lang-el-dec,underline);}
.fm-doc__lang-sep{font-family:var(--font-body);font-size:11px;color:var(--fm-brass);}
.fm-doc__row{display:grid;grid-template-columns:clamp(120px,16vw,190px) minmax(0,1fr);gap:clamp(14px,2.5vw,36px);padding:20px 0;border-top:1px solid var(--fm-brass-light);}
.fm-doc__row:first-of-type{padding-top:30px;border-top:none;}
.fm-doc__label-col{position:sticky;top:96px;align-self:start;}
.fm-doc__label{margin:0;font-family:var(--font-body);font-weight:600;font-size:12px;letter-spacing:0.18em;text-transform:uppercase;color:var(--fm-brass-dark);}
.fm-doc__body-col{display:flex;flex-direction:column;gap:12px;}
.fm-doc__body-col p{margin:0;font-size:clamp(15px,1.35vw,18px);line-height:1.72;color:var(--fm-charcoal);max-width:760px;text-wrap:pretty;}
.fm-doc__subhead{margin:0;font-family:var(--font-body);font-weight:600;font-size:clamp(20px,2.1vw,27px);color:var(--fm-charcoal);}
.fm-doc__closing{font-family:var(--font-body);font-weight:500;font-size:clamp(16px,1.5vw,20px);line-height:1.6;color:var(--fm-charcoal);margin:0;}
/* Legal only so far: a clause row's body can hold a whole document section
   (title + "Last updated", then numbered clauses, some with a list) rather
   than plain paragraphs. Reuses .fm-doc__row/.fm-doc__label for the outer
   grid; these classes are what's inside .fm-doc__body-col in that case. */
.fm-doc__sections{display:flex;flex-direction:column;gap:34px;max-width:760px;}
.fm-doc__section-head{display:flex;flex-direction:column;gap:8px;}
.fm-doc__section-title{margin:0;font-family:var(--font-body);font-weight:600;font-size:clamp(21px,2.1vw,28px);line-height:1.2;letter-spacing:-0.005em;color:var(--fm-charcoal);}
.fm-doc__updated{margin:0;font-size:13px;letter-spacing:0.02em;color:var(--color-text-muted);}
.fm-doc__clause{display:flex;flex-direction:column;gap:14px;}
.fm-doc__clause-heading{margin:0;font-family:var(--font-body);font-weight:600;font-size:clamp(16px,1.4vw,19px);line-height:1.3;color:var(--fm-charcoal);}
.fm-doc__clause-number{color:var(--fm-brass-dark);margin-right:10px;}
.fm-doc__clause-body{margin:0;font-size:clamp(15px,1.35vw,18px);line-height:1.72;color:var(--fm-charcoal);text-wrap:pretty;}
.fm-doc__list{display:flex;flex-direction:column;}
.fm-doc__list-item{margin:0;padding:10px 0;border-top:1px solid var(--fm-brass-light);font-size:clamp(15px,1.35vw,18px);line-height:1.6;color:var(--fm-charcoal);text-wrap:pretty;}
/* Contact only so far: rows whose body is a set of icon/arrow link buttons
   (Reserve, Maps, Socials) rather than paragraphs. */
.fm-doc__link-row{display:flex;flex-wrap:wrap;gap:6px 26px;}
.fm-doc__link-btn{display:inline-flex;align-items:center;gap:9px;min-height:44px;font-family:var(--font-body);font-weight:600;font-size:12px;letter-spacing:0.14em;text-transform:uppercase;color:var(--fm-brass-dark);transition:color 200ms ease;}
.fm-doc__link-btn:hover{color:var(--fm-brass);}
.fm-doc__link-btn i{font-size:16px;}
.fm-doc__cta-btn{display:inline-flex;align-items:center;gap:10px;padding:12px 20px;border:1px solid var(--fm-brass);font-family:var(--font-body);font-weight:600;font-size:12px;letter-spacing:0.14em;text-transform:uppercase;color:var(--fm-brass-dark);transition:background-color 200ms ease,color 200ms ease;}
.fm-doc__cta-btn:hover{background:var(--fm-brass);color:var(--fm-charcoal);}

/* ── 404 — one page, one-off classes (not a repeating type) ─────────── */
.fm-404{position:relative;flex:1;display:flex;align-items:center;overflow:hidden;background:var(--fm-charcoal);padding:clamp(120px,16vh,180px) clamp(20px,4vw,56px) clamp(80px,11vh,120px);box-sizing:border-box;}
.fm-404__media{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;}
.fm-404__scrim{position:absolute;inset:0;background:linear-gradient(180deg,rgba(24,22,20,0.80) 0%,rgba(24,22,20,0.62) 42%,rgba(24,22,20,0.78) 100%);}
.fm-404__inner{position:relative;z-index:1;max-width:1080px;margin:0 auto;width:100%;display:flex;flex-direction:column;}
.fm-404__eyebrow{margin:0 0 clamp(18px,2.6vh,28px);font-family:var(--font-body);font-weight:600;font-size:13px;letter-spacing:0.22em;text-transform:uppercase;color:var(--fm-brass-light);}
.fm-404__title{margin:0;max-width:15ch;font-family:var(--font-display);font-style:italic;font-weight:400;font-size:clamp(40px,6.4vw,92px);line-height:1.06;letter-spacing:-0.012em;color:var(--fm-oyster);text-wrap:pretty;}
.fm-404__rule{width:64px;height:1px;background:var(--fm-brass);margin:clamp(26px,4vh,44px) 0;}
.fm-404__body{margin:0;max-width:56ch;font-family:var(--font-body);font-weight:300;font-size:clamp(17px,1.5vw,21px);line-height:1.7;color:var(--fm-oyster);text-wrap:pretty;}
.fm-404__cta{align-self:flex-start;margin-top:clamp(30px,4.4vh,48px);display:inline-flex;align-items:center;gap:14px;font-family:var(--font-body);font-weight:600;font-size:13px;letter-spacing:0.18em;text-transform:uppercase;color:var(--fm-oyster);padding:14px 26px;border:1px solid var(--fm-brass);transition:color 200ms ease,background-color 200ms ease,border-color 200ms ease;}
.fm-404__cta:hover{background-color:var(--fm-brass);border-color:var(--fm-brass);color:var(--fm-charcoal);}
.fm-404__more{margin-top:clamp(44px,7vh,78px);padding-top:22px;border-top:1px solid rgba(218,193,143,0.28);display:flex;flex-direction:column;gap:16px;}
.fm-404__more-label{margin:0;font-family:var(--font-body);font-weight:600;font-size:11px;letter-spacing:0.18em;text-transform:uppercase;color:var(--fm-brass-light);}
.fm-404__more-links{display:flex;flex-wrap:wrap;gap:12px clamp(22px,3.4vw,44px);}
.fm-404__more-links a{font-family:var(--font-body);font-weight:400;font-size:clamp(16px,1.4vw,19px);color:var(--fm-oyster);transition:color 200ms ease;}
.fm-404__more-links a:hover{color:var(--fm-brass-light);}
/* FLAGGED and fixed, not just carried over: HANDOFF §3 documents this exact
   fix ("[data-lang-block="el"] sets font-weight:400 explicitly... Greek
   headings use 700, not 600") as already applied to the three bilingual
   pages — House Rules, Reservations Policy, Legal. It isn't; none of the
   three actually sets it, so Greek body copy inherits the page's 300 (a
   Latin-only weight, per HANDOFF's own warning) and Greek labels/headings
   stay at 600. Applied here rather than faithfully reproducing the bug,
   since HANDOFF is explicit and detailed about exactly what's wrong and
   why — this isn't a silent pick, it's the documented fix.
   CLIENT OVERRIDE: headings/labels moved from 700 to 500 — one weight
   lighter, per direct client instruction after review. 500 is the only
   step available between Greek's shipped local 400/500/700; there is no
   local 600. Body copy now matches Latin's 300 exactly: the "no 300-weight
   Greek font exists" assumption behind the earlier 400 fallback was wrong
   — Fira Sans ships Greek in every weight, the local assets/fonts/ package
   was just never given a 300 Greek TTF. partials/head.html's Fira Sans
   Google Fonts request now includes weight 300 (previously italic-300
   only), which covers Greek 300 until it's self-hosted (Phase 5). */
[data-lang-block="el"]{font-weight:300;}
[data-lang-block="el"] .fm-doc__label,
[data-lang-block="el"] .fm-doc__subhead,
[data-lang-block="el"] .fm-doc__section-title,
[data-lang-block="el"] .fm-doc__clause-heading{font-weight:500;}
/* No mobile override: none of the four DOC prototypes (House Rules,
   Reservations Policy, Contact, Legal) collapse this to one column at any
   width — clamp(120px,16vw,190px) already shrinks the label column down to
   a 120px floor on its own, which is the prototypes' actual mobile
   behaviour. A stacked single column here was an unspecified deviation. */

/* ---- Season Menu S2 — the menu document (Season Menu page only) -------
   Not one of the six repeating section types — a one-off long-form layout,
   built here because it genuinely is new content this page introduces
   (CLAUDE-CODE.md Phase 2 rule). Runs taller than one viewport, so it never
   carries .fm-snap (HANDOFF §4). Colour throughout is the seasonal pair —
   --season-mark for the accent, --season-voice for the narrative type —
   never Brass, and never used outside this page (CLAUDE.md rule 5). */
/* scroll-snap-align:start on the one section deliberately taller than the
   viewport — the opposite of what "disable snap here" sounds like it wants,
   and the reason it works. html is scroll-snap-type:y mandatory: a section
   with NO snap position at all can't be rested in — every scroll ends on a
   neighbour's start edge, so this one gets flung past, which read as it
   being "locked". Making it a snap AREA fixes that: per CSS Scroll Snap, an
   area taller than the snapport must allow free scrolling anywhere inside
   it and only snaps at its edges — the reader stops wherever they like in
   the menu, and the section still lands cleanly on entry and exit. Not
   .fm-snap, because that class carries other full-viewport assumptions
   elsewhere in this file. */
.fm-season-doc{scroll-snap-align:start;background:var(--season-ground);padding:clamp(90px,14vh,150px) clamp(20px,4vw,56px) clamp(80px,12vh,130px);box-sizing:border-box;}
.fm-season-doc__inner{max-width:1020px;margin:0 auto;}
.fm-season-doc__eyebrow{display:flex;align-items:center;gap:18px;margin:0 0 clamp(30px,4vh,44px);}
.fm-season-doc__eyebrow-label{font-family:var(--font-body);font-weight:600;font-size:clamp(12px,1.15vw,14px);letter-spacing:0.24em;text-transform:uppercase;color:var(--season-mark);white-space:nowrap;}
.fm-season-doc__eyebrow-rule{flex:1;height:2px;background:var(--season-mark);display:block;}
.fm-season-doc__intro{display:flex;flex-direction:column;gap:clamp(13px,1.5vw,21px);margin:0 0 clamp(44px,6vh,70px);}
.fm-season-doc__intro p{margin:0;font-family:var(--fm-pt-body);font-style:italic;font-weight:400;font-size:clamp(21px,2.5vw,33px);line-height:1.48;color:var(--season-voice);max-width:840px;text-wrap:pretty;}
.fm-season-doc__menu-row{display:grid;grid-template-columns:clamp(120px,17vw,190px) minmax(0,1fr);gap:clamp(16px,3vw,44px);margin:0 0 clamp(22px,3vh,34px);}
/* FLAGGED: HANDOFF §4 describes a "MENU title" at Fira 600
   clamp(24px,2.7vw,36px), 0.08em caps — a large display heading between
   the intro and the first stage. The actual prototype styles this "Menu"
   heading identically to the small stage-row labels below it (12-14px,
   0.24em caps) — same class here for both. Prototype wins per CLAUDE.md. */
.fm-season-doc__label{margin:0;font-family:var(--font-body);font-weight:600;font-size:clamp(12px,1.15vw,14px);line-height:1.1;letter-spacing:0.24em;text-transform:uppercase;color:var(--season-mark);}
.fm-season-doc__stages{display:flex;flex-direction:column;gap:clamp(46px,7vh,76px);}
.fm-season-doc__stage{display:grid;grid-template-columns:clamp(120px,17vw,190px) minmax(0,1fr);gap:clamp(16px,3vw,44px);border-top:1px solid var(--season-mark-tint);padding-top:22px;}
.fm-season-doc__stage:first-child{border-top:2px solid var(--season-mark);}
.fm-season-doc__stage-label{margin:0;font-family:var(--font-body);font-weight:600;font-size:13px;letter-spacing:0.2em;text-transform:uppercase;color:var(--season-mark);position:sticky;top:96px;align-self:start;}
.fm-season-doc__dishes{display:flex;flex-direction:column;gap:26px;}
.fm-season-doc__dish{display:flex;flex-direction:column;gap:5px;}
.fm-season-doc__dish-name{margin:0;font-family:var(--font-display);font-weight:500;font-size:clamp(23px,2.5vw,33px);line-height:1.18;color:var(--season-voice);}
.fm-season-doc__dish-detail{margin:0;font-size:clamp(15px,1.35vw,18px);line-height:1.55;color:var(--fm-charcoal);}
/* The Privé line: the one dish note that carries the accent, not Charcoal
   (HANDOFF §4's S2 colour-roles table). */
.fm-season-doc__dish-note{margin:2px 0 0;font-family:var(--font-body);font-weight:600;font-size:12px;letter-spacing:0.14em;text-transform:uppercase;color:var(--season-mark);}
.fm-season-doc__table{margin-top:clamp(56px,8vh,90px);padding-top:26px;border-top:1px solid var(--season-mark-tint);display:grid;grid-template-columns:clamp(120px,17vw,190px) minmax(0,1fr);gap:clamp(16px,3vw,44px);}
.fm-season-doc__table-body{display:flex;flex-direction:column;gap:14px;max-width:660px;}
/* RESOLVED: HANDOFF §4 describes these as "Fira italic, Charcoal"; the
   prototype had them plain. Client confirmed italic is correct — HANDOFF
   wins here, overriding the earlier "prototype wins" flag. */
.fm-season-doc__table-body p{margin:0;font-style:italic;font-weight:300;font-size:clamp(15px,1.35vw,18px);line-height:1.55;color:var(--fm-charcoal);}
@media (max-width:700px){
  .fm-season-doc__menu-row,.fm-season-doc__stage,.fm-season-doc__table{grid-template-columns:minmax(0,1fr);}
  .fm-season-doc__stage-label{position:static;}
}

/* ── PTL / ITL — the numbered narrative pattern ──────────────────────────
   Our way of Low & Slow only so far. Not one of the six repeating types:
   PTL is a numbered PT with a giant ghost numeral behind the heading; ITL
   is the same heading/numeral/body paired with a full-height image, left
   or right. Both run on Charcoal — every section on this page does; HANDOFF
   §5 describes S3/S5 Charcoal against S2/S4/S6/S7 Oyster, but the prototype
   has all seven sections Charcoal throughout, Brass Light headings on
   Oyster body text, no alternation anywhere. Used what's actually built. */
.fm-ptl{position:relative;min-height:calc(var(--vh,1lvh) * 100);display:flex;align-items:center;background:var(--fm-charcoal);padding:clamp(80px,12vh,120px) clamp(20px,4vw,56px);box-sizing:border-box;}
/* Fills [data-fm-closing]'s full box — see that rule's own comment for
   why this replaced sharing the space via flex with the footer. */
.fm-ptl[data-fm-last]{position:absolute;inset:0;min-height:0;}
.fm-ptl__inner{position:relative;box-sizing:border-box;max-width:720px;margin:0 auto;width:100%;padding-bottom:58px;padding-right:clamp(0px,calc((1180px - 100vw) * 999),190px);}
/* Heading, numeral and body are one shared declaration for PTL and ITL, so
   the two layouts cannot drift apart — reuse these three classes verbatim
   inside .fm-itl__text-inner too; only the body size differs (below). */
.fm-ptl__heading{position:relative;z-index:1;padding-left:clamp(26px,3.2vw,60px);margin:clamp(112px,12.5vw,215px) 0 clamp(28px,4.4vh,52px);font-family:var(--font-display);font-style:italic;font-weight:500;font-size:clamp(28px,3vw,44px);line-height:1.2;letter-spacing:-0.005em;color:var(--fm-brass-light);text-wrap:pretty;}
.fm-ptl__numeral{position:absolute;top:-0.675em;left:0;z-index:-1;font-family:var(--font-display);font-weight:900;font-size:clamp(182px,21vw,364px);line-height:1;color:var(--fm-charcoal-soft);pointer-events:none;user-select:none;}
.fm-ptl__body{position:relative;z-index:1;display:flex;flex-direction:column;gap:clamp(16px,1.5vw,26px);}
.fm-ptl__body p{margin:0;font-family:var(--font-body);font-style:italic;font-weight:300;color:var(--fm-oyster);text-wrap:pretty;font-size:clamp(17px,1.4vw,21px);line-height:1.75;}
.fm-itl__text-inner .fm-ptl__body p{font-size:clamp(15px,1.15vw,18px);line-height:1.8;}
.fm-ptl__link{display:inline-flex;align-items:center;gap:11px;margin-top:clamp(28px,4vh,44px);font-family:var(--font-body);font-weight:600;font-size:13px;letter-spacing:0.16em;text-transform:uppercase;color:var(--fm-brass-light);border-bottom:1px solid var(--fm-brass-light);padding-bottom:5px;transition:color 200ms ease,border-color 200ms ease;}
.fm-ptl__link:hover{color:var(--fm-oyster);border-color:var(--fm-oyster);}

/* background set here, not only on .fm-itl__img: the prototype's own CSS
   left the grid container transparent, which would show the page's Oyster
   body colour behind .fm-itl__text — invisible against its own Oyster body
   text. Real bug, caught testing; fixed regardless of source fidelity. */
.fm-itl{display:grid;grid-template-columns:1fr 1fr;min-height:calc(var(--vh,1lvh) * 100);align-items:stretch;background:var(--fm-charcoal);}
.fm-itl__img{position:relative;overflow:hidden;background:var(--fm-charcoal);}
.fm-itl__img img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;}
/* S5's mobile crop lives in the consolidated photo-crops block (§1b,
   HANDOFF §23 item 4) as --crop-low-slow-s5's mobile value. */
.fm-itl[data-img="right"] .fm-itl__img{order:2;}
.fm-itl__text{display:flex;align-items:center;padding:clamp(80px,12vh,120px) clamp(20px,4vw,56px) calc(clamp(80px,12vh,120px) + 58px + env(safe-area-inset-bottom,0px));box-sizing:border-box;}
.fm-itl__text-inner{position:relative;width:100%;max-width:500px;}
@media (max-width:700px){
  /* Centred in what's actually VISIBLE, not in the screen.height-tall box.
     On a phone the bottom --dead-zone of every section (site.js: the live
     gap between screen.height and what's on screen above Safari's
     toolbar, 117-157px measured) is not visible at rest, so content
     centred in the full box sat low, its last lines under the toolbar.
     Reserving that gap as padding-bottom makes the flex centring above
     land content in the visible band, with its last line well clear of
     the fixed corner back-link parked just above the toolbar. */
  .fm-ptl{padding-top:clamp(40px,6vh,72px);padding-bottom:calc(24px + var(--dead-zone,0px) + env(safe-area-inset-bottom,0px));}
  /* The closing instance shares its box with the footer instead: reserve
     that (measured, --fm-footer-h). 40px top, not the nav's full height —
     only the ghost numeral reaches up there, and it's decorative; the
     heading text itself still clears the nav. Inner padding-bottom
     dropped: at 672px minus footer this box is the tightest on the site.
     !important because Elena's closing instance (eb-4) carries an inline
     style="padding:…" for its desktop composition, which would otherwise
     outrank this and centre its content in the full box — 90px of it
     under the footer, measured. */
  .fm-ptl[data-fm-last]{padding-top:40px !important;padding-bottom:var(--fm-footer-h,176px) !important;}
  /* Elena's closing (eb-4) is composed entirely inline for desktop —
     section padding, inner padding, heading margin, numeral size, link
     margin — so every one of these needs !important to take on a phone.
     Its copy is three full paragraphs (locked), and at 672px minus the
     footer it was still 27px under the footer with everything else
     reserved; the room comes from the ghost numeral: smaller, and its
     heading margin cut to 30px — the numeral then hangs from ~3px inside
     the box rather than needing 92px cleared above the heading. Ghost,
     decorative, z-index:-1: nothing legible is lost. */
  .fm-ptl[data-fm-last] .fm-ptl__inner{padding-bottom:0 !important;}
  .fm-ptl[data-fm-last] .fm-ptl__numeral{font-size:100px !important;}
  .fm-ptl[data-fm-last] .fm-ptl__heading{margin-top:30px !important;margin-bottom:14px !important;}
  .fm-ptl[data-fm-last] .fm-ptl__link{margin-top:14px !important;}
  .fm-ptl__inner{padding-right:0;max-width:none;padding-bottom:24px;}
  .fm-ptl__body{gap:12px;}
  .fm-ptl__body p{font-size:15px;line-height:1.6;}

  /* The photo starts directly below the nav (padding-top is the nav's
     measured height) and runs a 30vh band — image row first (order:-1
     below). An earlier pass sat it at 18vh, which read as a short
     placeholder; a later one ran it behind the nav like a hero, which
     the client didn't want here. The text takes the remaining row (1fr —
     which is minmax(auto,1fr), so it never clips) and centres within it;
     a section whose copy needs more than that grows via min-height into
     a snap area taller than the viewport, which the scroll-snap spec
     handles by design — in at its top, free inside, out at the next
     section — while one that fits stays one screen. */
  .fm-itl{grid-template-columns:1fr;height:auto;min-height:calc(var(--vh,1lvh) * 100);box-sizing:border-box;padding-top:var(--fm-nav-h,93px);padding-bottom:calc(24px + var(--dead-zone,0px) + env(safe-area-inset-bottom,0px));grid-template-rows:minmax(180px,30vh) 1fr;align-content:start;}
  /* Must match [data-img="right"] .fm-itl__img's specificity too — the
     desktop order:2 rule isn't width-scoped, so a plain .fm-itl__img here
     loses to it on the right-image instances at mobile widths. Caught
     testing: lns-5 (data-img="right") kept its image second on a phone. */
  .fm-itl__img,.fm-itl[data-img="right"] .fm-itl__img{order:-1;height:auto;min-height:0;}
  .fm-itl__text{padding:clamp(10px,1.4vh,16px) clamp(20px,4vw,56px) 16px;align-items:center;overflow:visible;}
  .fm-itl__text-inner{padding-right:0;max-width:none;}
  .fm-ptl__heading{margin-top:max(clamp(30px,6vw,46px),calc(0.675 * clamp(120px,34vw,180px) + 2px));margin-bottom:clamp(14px,2vh,22px);font-size:clamp(28px,7.4vw,34px);}
  .fm-ptl__numeral{font-size:clamp(120px,34vw,180px);top:-0.675em;}
  .fm-itl__text-inner .fm-ptl__body{gap:12px;}
  .fm-itl__text-inner .fm-ptl__body p{font-size:13.5px;line-height:1.45;}
}


/* ── 5. Scroll units — snap sections and the closing viewport ──────────
   .fm-snap carries the native scroll-snap target (scroll-snap-type is set
   on html, above); [data-fm-closing] wraps the last section and the
   footer as one flex column so the legal band lands inside the final
   viewport (HANDOFF §6). */
.fm-snap,[data-fm-closing]{box-sizing:border-box;}
.fm-snap{scroll-snap-align:start;}
/* opacity:0.999, not the visually-identical 1 — a confirmed Safari 26
   rendering bug: at exactly opacity:1, Safari paints a fully opaque
   full-bleed background as a plain fill and clips it near the dynamic
   toolbar's screen region, instead of compositing it normally. Any value
   under 1 routes the same layer through the compositor and it paints
   edge-to-edge correctly. Matches this site's own symptom exactly — a
   real device's Web Inspector confirmed every .fm-snap section's own
   layout box already covered the full viewport pixel-for-pixel while the
   next section's photo still visibly painted through at the bottom edge.
   Costs nothing visible; 0.999 is indistinguishable from 1. */
.fm-snap,[data-fm-closing]{opacity:0.999;}
/* Fixed, not a minimum: the last section and the footer are meant to add
   up to exactly one viewport (HANDOFF §6). A min-height let the pair grow
   past 100dvh whenever their natural sizes overshot it, pushing the
   footer's bottom below the fold instead of forcing the last section to
   flex-shrink and make room for it. */
/* Unlike every other full-bleed section, this one's fallback if it comes
   up short isn't a different section's mismatched content — it's just
   body's own background, always charcoal, matching the footer it sits
   directly against. That makes it safe to size this box smaller than
   screen.height on purpose: subtracting --fm-toolbar-buffer (§4,
   comfortably past the largest screen.height-vs-visible gap measured,
   157px) instead of
   using the full screen.height like every mid-page section does. Two
   dynamic approaches were tried and both landed on effectively the same
   number every time regardless: reaching the true end of the page is
   exactly when Safari tends to re-expand its toolbar, so the "currently
   visible" area at the one moment the footer is actually checked is
   reliably close to the worst case anyway — there was never a smaller
   live number to chase here. Footer sits at this smaller box's own true
   bottom (position:relative below, no separate offset needed), so
   whatever photo is still showing above it just ends a bit sooner; a
   matching-colour strip beneath it if the toolbar happens to be more
   collapsed than expected is a minor cosmetic trade against the box
   being wrong outright. */
[data-fm-closing]{height:calc((var(--vh,1lvh) * 100) - var(--fm-toolbar-buffer));position:relative;box-sizing:border-box;}
[data-fm-closing] .fm-footer{position:absolute;left:0;right:0;bottom:calc(0px + env(safe-area-inset-bottom,0px));}
/* Desktop: the last section ends where the footer begins, so the two add
   up to exactly one viewport the way HANDOFF §6 lays them out and every
   overlay anchored to the section's bottom lands above the footer, not
   under it (measured at 1440×820: the ECM/LCM overlays and Elena's link
   all sat inside the footer's 203px). Phones keep the full-box photo and
   reserve the footer inside the overlays instead (--fm-footer-h rules in
   §4) because their closing box is deliberately undersized. */
@media (min-width:701px){
  [data-fm-closing] > [data-fm-last]{bottom:var(--fm-footer-h,203px);}
}

/* Solo corner back-link — Our way of Low & Slow, A day in the life,
   Elena's bio (HANDOFF §5 deviations). */
/* Two link instances cross-fade (site.js cornerBackLink): --hero sits inline
   in the opening CM's overlay and shows first; the fixed one takes over once
   the reader scrolls past the hero, and is clamped off the footer via
   --fbar-y so it never overlaps the legal band. */
/* Plain bottom offset — NO --dead-zone / --fm-toolbar-buffer term here,
   unlike the scroll indicator and the closing section. Those are
   position:absolute inside boxes sized to screen.height, so they had to
   climb above the toolbar gap; this is position:fixed, and a real-device
   measurement settled which viewport that resolves against: with
   bottom:139px the link's bottom edge sat at 596px on a 735px-tall
   visible viewport — 735 − 139 exactly. Fixed elements measure against
   what's VISIBLE, so a 22px offset already sits 22px above the toolbar.
   Two attempts added a gap term on the assumption fixed shared the
   sections' 852px frame; both parked the link mid-screen. It didn't, and
   the original offset was right. */
.fm-corner-link{position:fixed;right:clamp(20px,4vw,56px);bottom:calc(clamp(18px,3vh,30px) + env(safe-area-inset-bottom,0px));z-index:55;transform:var(--fbar-y,translateY(0));opacity:var(--back-b,0);pointer-events:var(--back-bp,none);display:inline-flex;align-items:center;gap:9px;font-family:var(--font-body);font-weight:600;font-size:11px;letter-spacing:0.16em;text-transform:uppercase;color:var(--fm-oyster);transition:color 200ms ease,transform 200ms ease,opacity 260ms ease 200ms;}
.fm-corner-link:hover{color:var(--fm-brass);}
.fm-corner-link[data-fm-tone="light"]{color:var(--fm-brass-dark);}
.fm-corner-link[data-fm-tone="light"]:hover{color:var(--fm-brass);}
.fm-corner-link--hero{position:static;align-self:flex-start;opacity:var(--back-a,1);pointer-events:var(--back-ap,auto);display:inline-flex;align-items:center;gap:9px;font-family:var(--font-body);font-weight:600;font-size:11px;letter-spacing:0.16em;text-transform:uppercase;color:var(--fm-oyster);transition:color 200ms ease,opacity 140ms ease;}
.fm-corner-link--hero:hover{color:var(--fm-brass);}
/* No back-links at all on a phone (Low & Slow, A day in the life, Elena's
   bio) — client call. A fixed element competing with centred content and
   a footer on a viewport this small is more trouble than it's worth, and
   the swipe-back gesture already does the job; site.js's scrollMemory
   restores the previous page's position on that route too (pageshow).
   Desktop keeps both. */
@media (max-width:700px){
  .fm-corner-link,.fm-corner-link--hero{display:none;}
}


/* ── The menu reader — Tasting Menu, À la Carte, Wine & Whiskey List ────
   HANDOFF §21: "the three pages share one reader: same shell, same tab
   bar, same item row; only the JSON path and the hero differ." One class
   set, generated from menu-data/*.json by build.mjs at build time — no
   dish data in markup, no client-side fetch (the prototypes fetch the
   JSON at runtime; production embeds it as real HTML so the page works
   with JS off and is crawlable, per HANDOFF §1/§12).

   Unification note: the prototypes are not quite identical to each other.
   À la Carte and Wine & Whiskey List render a chapter's `part` as a small
   eyebrow above the chapter title; Tasting Menu instead renders it as a
   closing price note below the items. HANDOFF's own "one reader" sentence
   is the tie-breaker: every chapter here uses the eyebrow position
   (2-of-3 prototypes, and it reads cleanly for Tasting Menu's per-person
   price too — "€36 PER PERSON / +PRIVÉ, €40 PER PERSON" sits fine as a tag
   above "Wine Pairing"). Flagged for the client to confirm on the built
   page.

   Price is rendered exactly as menu-data/*.json has it — no currency
   symbol added. A € prefix was tried and reverted on client direction:
   nothing goes in beyond what the content itself already has. */

.fm-reader-hero{position:relative;min-height:calc(var(--vh,1lvh) * 62);display:flex;align-items:flex-end;overflow:hidden;background:var(--fm-charcoal);padding:clamp(120px,18vh,190px) clamp(20px,4vw,56px) clamp(48px,7vh,72px);box-sizing:border-box;}
.fm-reader-hero__media{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;}
.fm-reader-hero__scrim{position:absolute;inset:0;background:linear-gradient(180deg,rgba(24,22,20,0.74) 0%,rgba(24,22,20,0.44) 45%,rgba(24,22,20,0.86) 100%);}
.fm-reader-hero__inner{position:relative;z-index:1;max-width:1080px;margin:0 auto;width:100%;}
.fm-reader-hero__title{margin:0;max-width:16ch;font-family:var(--font-display);font-style:italic;font-weight:400;font-size:clamp(38px,5.6vw,80px);line-height:1.06;letter-spacing:-0.012em;color:var(--fm-oyster);text-wrap:pretty;}
.fm-reader-hero__subtitle{margin:clamp(24px,3.6vh,38px) 0 0;max-width:26ch;font-family:var(--font-display);font-style:italic;font-weight:400;font-size:clamp(19px,1.9vw,26px);line-height:1.5;color:var(--fm-brass-light);text-wrap:pretty;}

/* Sticky jump bar: a horizontal tab rail on tablet/desktop, a native
   <select> picker below 700px (HANDOFF §5's ITS-style collapse pattern,
   applied here). --fm-nav-h is measured, not hardcoded — site.js. */
.fm-reader-jump{position:sticky;top:var(--fm-nav-h,88px);z-index:40;background:var(--fm-oyster);border-bottom:1px solid var(--fm-brass);padding:0 clamp(20px,4vw,56px);box-sizing:border-box;}
.fm-reader-jump__inner{max-width:1080px;margin:0 auto;display:flex;align-items:center;gap:clamp(16px,3vw,40px);padding:15px 0;}
.fm-reader-jump__rail{display:flex;gap:clamp(16px,2.4vw,32px);overflow-x:auto;min-width:0;flex:1 1 auto;}
.fm-reader-jump__tab{flex-shrink:0;display:flex;flex-direction:column;align-items:flex-start;gap:6px;padding-bottom:2px;transition:color 200ms ease;font-family:var(--font-body);font-weight:600;font-size:11px;letter-spacing:0.16em;text-transform:uppercase;white-space:nowrap;color:var(--fm-brass-dark);}
.fm-reader-jump__tab-rule{display:block;width:100%;height:2px;background:transparent;}
.fm-reader-jump__tab[aria-current="true"]{color:var(--fm-charcoal);}
.fm-reader-jump__tab[aria-current="true"] .fm-reader-jump__tab-rule{background:var(--fm-brass-dark);}
.fm-reader-jump__select{display:none;position:relative;flex:1 1 auto;min-width:0;align-items:center;}
.fm-reader-jump__select select{appearance:none;-webkit-appearance:none;width:100%;box-sizing:border-box;min-height:44px;padding:11px 40px 11px 14px;background:transparent;border-width:1px;border-style:solid;border-color:var(--fm-brass);border-radius:0;font-family:var(--font-body);font-weight:600;font-size:12px;letter-spacing:0.14em;text-transform:uppercase;color:var(--fm-charcoal);cursor:pointer;}
.fm-reader-jump__select i{position:absolute;right:14px;font-size:15px;color:var(--fm-brass-dark);pointer-events:none;}
.fm-reader-jump__back{flex:0 0 auto;align-self:flex-start;display:inline-flex;align-items:center;gap:8px;font-family:var(--font-body);font-weight:600;font-size:11px;letter-spacing:0.16em;text-transform:uppercase;color:var(--fm-brass-dark);white-space:nowrap;transition:color 200ms ease;}
.fm-reader-jump__back:hover{color:var(--fm-charcoal);}
@media (max-width:700px){
  .fm-reader-jump__rail{display:none;}
  .fm-reader-jump__select{display:flex;}
  .fm-reader-jump__back{display:none;}
}

.fm-reader-content{background:var(--fm-oyster);padding:clamp(52px,8vh,84px) clamp(20px,4vw,56px) clamp(80px,11vh,120px);box-sizing:border-box;}
.fm-reader-content__inner{max-width:1080px;margin:0 auto;display:flex;flex-direction:column;gap:clamp(56px,8vh,92px);}
.fm-reader-chapter[hidden]{display:none;}
.fm-reader-chapter{scroll-margin-top:calc(var(--fm-nav-h,88px) + 64px);display:flex;flex-direction:column;gap:clamp(22px,3vh,34px);}
.fm-reader-chapter__head{display:flex;flex-direction:column;gap:9px;padding-bottom:18px;border-bottom:1px solid var(--fm-brass);}
.fm-reader-chapter__part{margin:0;font-family:var(--font-body);font-weight:600;font-size:11px;letter-spacing:0.2em;text-transform:uppercase;color:var(--fm-brass-dark);}
.fm-reader-chapter__title{margin:0;font-family:var(--font-display);font-style:italic;font-weight:400;font-size:clamp(30px,3.6vw,48px);line-height:1.1;letter-spacing:-0.008em;color:var(--fm-charcoal);}
.fm-reader-chapter__note{margin:6px 0 0;font-family:var(--font-body);font-weight:400;font-size:clamp(14px,1.2vw,16px);line-height:1.6;color:var(--fm-brass-dark);max-width:60ch;}
.fm-reader-group{display:flex;flex-direction:column;gap:clamp(14px,2vh,20px);}
.fm-reader-group__name{margin:0;font-family:var(--font-body);font-weight:600;font-size:12px;letter-spacing:0.18em;color:var(--fm-brass-dark);}
.fm-reader-items{display:flex;flex-direction:column;}
.fm-reader-item{display:flex;flex-direction:column;gap:5px;padding:clamp(15px,2.1vh,20px) 0;border-top:1px solid var(--fm-brass-light);}
.fm-reader-item__row{display:flex;align-items:baseline;justify-content:space-between;gap:clamp(16px,3vw,40px);}
.fm-reader-item__name{margin:0;font-family:var(--font-serif-text);font-weight:400;font-size:clamp(17px,1.6vw,22px);line-height:1.25;letter-spacing:-0.004em;color:var(--fm-charcoal);text-wrap:pretty;}
.fm-reader-item__price{margin:0;flex-shrink:0;font-family:var(--font-body);font-weight:600;font-size:clamp(15px,1.3vw,18px);line-height:1.25;color:var(--fm-brass-dark);font-variant-numeric:tabular-nums;}
.fm-reader-item__detail{margin:0;font-family:var(--font-body);font-weight:300;font-size:clamp(14px,1.15vw,16px);line-height:1.6;color:var(--fm-charcoal);}
.fm-reader-item__note{margin:0;font-family:var(--font-body);font-weight:300;font-size:clamp(14px,1.15vw,16px);line-height:1.6;color:var(--color-text-muted);}
/* Tasting Menu Pairings only: the per-person price sits as a closing note
   after the last group, not as the chapter-head eyebrow (that slot is a
   category label elsewhere, e.g. À la Carte/Wine & Whiskey's "Wine List" —
   this chapter's price isn't a category). */
.fm-reader-chapter__closing-note{margin:clamp(10px,1.6vh,18px) 0 0;padding-top:clamp(14px,2vh,20px);border-top:1px solid var(--fm-brass-light);font-family:var(--font-body);font-weight:600;font-size:12px;letter-spacing:0.14em;text-transform:uppercase;color:var(--fm-brass-dark);}


/* ── 6. Navigation bar — permanent, two tones (HANDOFF §6) ───────────── */
/* padding-top adds env(safe-area-inset-top) on top of the usual 24px, not
   instead of it — viewport-fit=cover (head) lets the page draw under the
   notch/Dynamic Island, so without this the logo/buttons would sit
   partly behind it. border-bottom:0 solid, not 1px solid transparent: a
   1px-wide border painted fully transparent is a known WebKit trigger for
   a faint phantom hairline at the compositing-layer edge (this nav is
   already backdrop-filter'd, itself flagged elsewhere in this file as
   bug-prone) — animating the width via --nav-border-w to 0 avoids ever
   asking WebKit to paint that edge at all when no hairline should show. */
.fm-nav{position:fixed;top:0;left:0;right:0;z-index:60;display:flex;align-items:center;justify-content:space-between;padding:calc(24px + env(safe-area-inset-top,0px)) clamp(20px,4vw,56px) 24px;box-sizing:border-box;transition:background-color 320ms ease,backdrop-filter 320ms ease,border-color 320ms ease;background-color:var(--nav-bg,transparent);background-image:var(--nav-scrim,linear-gradient(to bottom,rgba(16,14,12,0.58) 0%,rgba(16,14,12,0.30) 55%,rgba(16,14,12,0) 100%));backdrop-filter:var(--nav-blur,none);border-bottom:var(--nav-border-w,0px) solid var(--nav-border,transparent);}
.fm-nav__brand{display:flex;align-items:center;flex-shrink:0;}
.fm-nav__lockup{position:relative;display:block;height:34px;}
.fm-nav__lockup img{height:34px;width:auto;display:block;transition:opacity 240ms ease;}
/* Each lockup is a pair: the accessible Oyster image stays in normal flow
   (so the span has real width to centre on at mobile) and the decorative
   Ember image — aria-hidden, alt="" — overlays it absolutely. NOTE: this
   must key off [aria-hidden], not `img + img` — with four <img>s in the
   span (wide pair, then mark pair) a sibling-combinator rule catches the
   third image too and collapses the mark lockup's width to 0. Cost this a
   real bug on the mobile nav; caught testing Phase 2's first page. */
.fm-nav__lockup img:not([aria-hidden]){opacity:var(--nav-logo-o,1);}
.fm-nav__lockup img[aria-hidden="true"]{position:absolute;top:0;left:0;opacity:var(--nav-logo-e,0);}
/* The wide/mark swap is the <picture>'s own <source media> (nav.html);
   display:contents keeps the <img> a direct flex/positioning child of the
   span so the pair rules above are unchanged. */
.fm-nav__lockup picture{display:contents;}
.fm-nav__actions{display:flex;align-items:center;gap:clamp(16px,3vw,34px);}
.fm-nav__reserve{display:inline-flex;align-items:center;justify-content:center;gap:9px;font-family:var(--font-body);font-weight:600;font-size:13px;letter-spacing:0.18em;text-transform:uppercase;padding:11px 20px;border:1px solid currentColor;color:var(--nav-ink,var(--fm-oyster));transition:color 240ms ease,background-color 200ms ease,border-color 240ms ease;}
.fm-nav__reserve:hover{background:var(--fm-brass);border-color:var(--fm-brass);color:var(--fm-charcoal);}
/* Inline SVG icons, sized like the Phosphor font glyphs they replaced: a
   1em square inline-block on the line box, colour from currentColor, so
   every existing `i{font-size:…}` rule below keeps working unchanged. */
.fm-ico{display:inline-block;width:1em;height:1em;line-height:1;font-style:normal;vertical-align:top;flex-shrink:0;}
.fm-ico svg{display:block;width:100%;height:100%;fill:currentColor;}
.fm-nav__reserve i{display:none;font-size:18px;line-height:1;}
@media (max-width:560px){
  .fm-nav{justify-content:flex-end;}
  .fm-nav__brand{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);}
  .fm-nav__actions{flex:1 1 auto;justify-content:space-between;}
  .fm-nav__reserve{padding:0;box-sizing:border-box;width:44px;height:44px;flex-shrink:0;border-color:transparent;}
  .fm-nav__reserve span{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;}
  .fm-nav__reserve i{display:block;font-size:23px;}
}
/* HANDOFF §6: the overlay's close row explicitly "repeats the MENU button's
   metrics (padding:8px 0, Fira 600/14px/0.18em caps, gap:11px, a 22px glyph
   column holding the ✕ where the burger sat)" — i.e. this button carries a
   visible MENU label next to the icon, the same shape as .fm-overlay__close-btn
   below, not an icon standing alone. */
.fm-nav__menu-btn{background:none;border:none;box-sizing:border-box;padding:8px 0;cursor:pointer;color:var(--nav-ink,var(--fm-oyster));display:flex;align-items:center;gap:11px;font-family:var(--font-body);font-weight:600;font-size:14px;letter-spacing:0.18em;text-transform:uppercase;transition:color 240ms ease;}
.fm-nav__menu-glyph{width:22px;display:flex;justify-content:center;flex-shrink:0;}
.fm-nav__menu-btn i{font-size:26px;line-height:1;}
/* Two-line (ph-equals) on desktop/tablet, the classic three-line hamburger
   (ph-list) on mobile — same wide/mark swap pattern as .fm-nav__lockup. */
.fm-nav__menu-icon--mark{display:none;}
@media (max-width:560px){
  .fm-nav__menu-icon--wide{display:none;}
  .fm-nav__menu-icon--mark{display:block;}
}
/* Same issue as .fm-overlay__close-btn above: closing the overlay returns
   focus here programmatically (site.js), and browsers' :focus-visible
   heuristics inconsistently treat that as visible — the ring then sits on
   the icon until the next click elsewhere, reading as a permanent frame. */
.fm-nav__menu-btn:focus-visible{outline:none;}
@media (max-width:560px){
  /* Matches .fm-nav__reserve's own collapse just above: the label stays in
     the accessible name (visually hidden, not display:none) while only the
     icon shows, back to a 44x44 tap target. */
  .fm-nav__menu-btn{width:44px;height:44px;padding:0;justify-content:center;gap:0;}
  .fm-nav__menu-label{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;}
}


/* ── 7. Full-screen menu overlay (HANDOFF §6) ───────────────────────── */
.fm-overlay{position:fixed;inset:0;z-index:80;background:var(--fm-charcoal);transition:opacity 340ms ease;display:var(--ov-d,none);opacity:var(--ov-o,0);pointer-events:var(--ov-p,none);}
.fm-overlay__panel{position:absolute;inset:0;display:flex;flex-direction:column;transform:var(--ov-y,translateY(16px));transition:transform 560ms cubic-bezier(.16,.84,.3,1);}
.fm-overlay__close-row{display:flex;align-items:center;justify-content:flex-end;min-height:41px;padding:24px clamp(20px,4vw,56px);flex-shrink:0;box-sizing:border-box;}
.fm-overlay__close-btn{background:none;border:none;cursor:pointer;font-family:var(--font-body);font-weight:600;font-size:14px;letter-spacing:0.18em;text-transform:uppercase;color:var(--fm-brass-light);display:flex;align-items:center;gap:11px;padding:8px 0;transition:color 200ms ease;}
.fm-overlay__close-btn:hover{color:var(--fm-oyster);}
/* This button is auto-focused every time the overlay opens (site.js), not
   tabbed to — browsers' :focus-visible heuristics don't agree on whether
   that counts as "visible", so the ring flashed inconsistently. Suppressed
   here specifically; every other focusable element in the overlay keeps
   its ring for real keyboard navigation. */
.fm-overlay__close-btn:focus-visible{outline:none;}
.fm-overlay__close-glyph{width:22px;display:flex;justify-content:center;font-size:16px;line-height:1;}
.fm-overlay__list{flex:1;min-height:0;display:flex;flex-direction:column;justify-content:safe center;overflow-y:auto;padding:clamp(10px,3vh,40px) clamp(20px,4vw,56px);max-width:1180px;width:100%;box-sizing:border-box;margin:0 auto;}
.fm-overlay__brand{display:inline-block;align-self:center;margin-bottom:clamp(24px,7vh,76px);}
.fm-overlay__brand img{height:clamp(44px,11.5vh,133px);width:auto;display:block;}
@media (min-width:700px),(orientation:landscape){
  .fm-overlay__brand{align-self:flex-start;}
}
.fm-overlay__rule{width:44px;height:1px;background:var(--fm-brass);margin-bottom:clamp(12px,1.8vh,20px);flex-shrink:0;}
.fm-overlay__group{display:flex;flex-direction:column;}
.fm-overlay__group + .fm-overlay__rule{margin-top:clamp(18px,5vh,54px);}
.fm-overlay__item{display:flex;align-items:baseline;justify-content:space-between;gap:32px;color:var(--fm-oyster);transition:color 200ms ease;border-bottom:1px solid rgba(218,193,143,0.16);}
.fm-overlay__item:last-child{border-bottom:none;}
.fm-overlay__item:hover{color:var(--fm-brass-light);}
.fm-overlay__item span:first-child{font-family:var(--font-display);font-weight:400;line-height:1.15;letter-spacing:-0.005em;}
.fm-overlay__item span:last-child{color:var(--fm-brass);flex-shrink:0;}
.fm-overlay__group--primary .fm-overlay__item{padding:clamp(12px,2vh,22px) 0;}
/* min(vw,vh) on both item font-sizes, not vw alone: on a narrow-but-short
   phone viewport (Safari's collapsed address/tab bars still eat real
   height even after 100dvh), a pure-vw clamp never engages its own floor's
   shrink — width is plenty, height isn't — which is what pushed Reserve
   below the fold and forced a scroll to reach it. */
.fm-overlay__group--primary .fm-overlay__item span:first-child{font-size:clamp(22px,min(3.3vw,5.4vh),42px);}
.fm-overlay__group--primary .fm-overlay__item span:last-child{font-size:20px;}
.fm-overlay__group--secondary .fm-overlay__item{padding:clamp(8px,1.35vh,14px) 0;}
.fm-overlay__group--secondary .fm-overlay__item span:first-child{font-size:clamp(18px,min(2.05vw,3.6vh),26px);line-height:1.25;}
.fm-overlay__group--secondary .fm-overlay__item span:last-child{font-size:16px;}
/* Mobile only (HANDOFF §7): below 560px the nav's own Reserve collapses to
   an icon with no visible label, so the overlay carries the full-word
   fallback. Above that the nav's bordered, labelled Reserve is already
   reachable without opening the menu, so this duplicate stayed hidden on
   desktop/tablet — and it isn't, hence the section it sat in ran taller
   than the viewport and needed a scrollbar to reach every link. */
.fm-overlay__reserve{display:none;align-self:center;margin-top:clamp(18px,4vh,44px);align-items:center;gap:11px;padding:14px 26px;border:1px solid var(--fm-brass);font-family:var(--font-body);font-weight:600;font-size:13px;letter-spacing:0.18em;text-transform:uppercase;color:var(--fm-oyster);transition:background-color 200ms ease,color 200ms ease;}
.fm-overlay__reserve:hover{background:var(--fm-brass);color:var(--fm-charcoal);}
@media (max-width:560px){
  .fm-overlay__reserve{display:inline-flex;margin-top:clamp(24px,4.2vh,44px);padding:11px 22px;}
  /* iOS Safari's address/tab bars still eat real height after 100dvh
     accounts for their live state — on a short phone viewport this whole
     card needs to compress further than the vh floors below were letting
     it, which is what pushed Reserve below the fold. */
  .fm-overlay__brand{margin-bottom:clamp(18px,5vh,60px);}
  .fm-overlay__brand img{height:clamp(40px,9vh,110px);}
  .fm-overlay__rule{margin-bottom:clamp(8px,1.6vh,16px);}
  .fm-overlay__group + .fm-overlay__rule{margin-top:clamp(12px,3.2vh,40px);}
  .fm-overlay__group--primary .fm-overlay__item{padding:clamp(8px,1.8vh,18px) 0;}
  .fm-overlay__group--secondary .fm-overlay__item{padding:clamp(5px,1.1vh,10px) 0;}
  .fm-overlay__list{padding-top:clamp(8px,2vh,30px);padding-bottom:clamp(8px,2vh,30px);}
}


/* ── 8. Footer (HANDOFF §6, §22) ─────────────────────────────────────── */
/* Bottom padding clears env(safe-area-inset-bottom) only — no extra flat
   buffer on top of it (there was one; removed). That buffer predated
   [data-fm-closing] being deliberately undersized to keep the whole
   closing section inside the visible area on its own (§5); once that
   fix landed, the extra padding just made the footer noticeably taller
   than it needed to be, for a safety margin the outer box now already
   guarantees.
   Hairline along the top edge, every viewport: where the closing section
   is Charcoal too (Low & Slow, Elena, the closing CMs) the two ran
   together with no edge at all. Charcoal Soft on request (brass first,
   too loud). */
.fm-footer{flex-shrink:0;border-top:0.75px solid var(--fm-charcoal-soft);background:var(--fm-charcoal);padding:clamp(26px,3.4vh,38px) clamp(20px,4vw,56px) calc(16px + env(safe-area-inset-bottom,0px));box-sizing:border-box;}
.fm-footer__inner{max-width:1080px;margin:0 auto;}
.fm-foot-grid{display:grid;grid-template-columns:1.35fr 1.3fr 0.95fr 1.1fr;gap:clamp(20px,2.4vw,44px);align-items:start;padding-bottom:20px;border-bottom:1px solid rgba(173,138,83,0.34);}
.fm-foot-col{display:flex;flex-direction:column;gap:6px;}
.fm-foot-heading{margin:0 0 3px;font-family:var(--font-body);font-weight:600;font-size:12px;letter-spacing:0.18em;text-transform:uppercase;color:var(--fm-brass);}
.fm-foot-col p{margin:0;font-family:var(--font-body);font-weight:300;font-size:14px;line-height:1.42;color:var(--fm-oyster);}
.fm-foot-col a{color:var(--fm-oyster);transition:color 200ms ease;}
.fm-foot-col a:hover{color:var(--fm-brass);}
.fm-foot-maps{display:flex;flex-wrap:wrap;gap:8px;margin-top:6px;}
.fm-foot-maps a{display:inline-flex;align-items:center;white-space:nowrap;padding:6px 9px;border:1px solid rgba(173,138,83,0.55);font-family:var(--font-body);font-weight:600;font-size:10px;letter-spacing:0.1em;text-transform:uppercase;transition:background-color 200ms ease,color 200ms ease;}
.fm-foot-maps a:hover{background:var(--fm-brass);color:var(--fm-charcoal);}
.fm-foot-social{display:flex;align-items:center;gap:14px;margin-top:10px;}
.fm-foot-social a{display:inline-flex;font-size:21px;line-height:1;}
.fm-foot-signup{font-family:var(--font-display);font-style:italic;font-weight:400;font-size:18px;line-height:1.35;}
.fm-foot-legal{display:flex;flex-wrap:wrap;align-items:baseline;gap:6px 24px;padding-top:13px;}
.fm-foot-legal p{margin:0;font-family:var(--font-body);font-weight:300;font-size:12px;line-height:1.5;color:var(--fm-oyster);}
.fm-foot-compact{display:none;max-width:1080px;margin:0 auto;align-items:center;justify-content:space-between;text-align:left;gap:clamp(20px,4vw,48px);}
.fm-foot-compact__id{display:flex;flex-direction:column;align-items:flex-start;gap:3px;}
.fm-foot-compact__name{margin:0;font-family:var(--font-body);font-weight:600;font-size:12px;letter-spacing:0.18em;text-transform:uppercase;line-height:1.4;color:var(--fm-oyster);}
.fm-foot-compact__addr{margin:0;font-family:var(--font-body);font-weight:300;font-size:14px;line-height:1.5;color:var(--fm-oyster);}
.fm-foot-compact__addr a{font-weight:600;font-size:12px;letter-spacing:0.16em;text-transform:uppercase;color:var(--fm-oyster);transition:color 200ms ease;padding:15px 0;margin:-15px 0;}
.fm-foot-compact__addr a:hover{color:var(--fm-brass-light);}
.fm-foot-compact__sep{color:var(--fm-brass);padding:0 5px;}
.fm-foot-compact__pin{font-size:15px;vertical-align:-3px;}
.fm-foot-compact__contact{display:inline-flex;align-items:center;gap:7px;font-family:var(--font-body);font-weight:600;font-size:12px;letter-spacing:0.16em;text-transform:uppercase;color:var(--fm-oyster);border:1px solid var(--fm-brass);box-sizing:border-box;min-height:44px;padding:11px 26px;flex-shrink:0;transition:color 200ms ease,background-color 200ms ease;}
.fm-foot-compact__contact:hover{background:var(--fm-brass);color:var(--fm-charcoal);}
@media (max-width:820px){
  .fm-foot-grid,.fm-foot-legal{display:none;}
  .fm-foot-compact{display:flex;}
  .fm-footer{padding-top:clamp(12px,1.4vh,16px);padding-bottom:calc(clamp(12px,1.4vh,16px) + env(safe-area-inset-bottom,0px));}
}
@media (max-width:560px){
  .fm-foot-compact{flex-direction:column;align-items:center;text-align:center;gap:10px;}
  .fm-foot-compact__id{order:2;align-items:center;text-align:center;}
  .fm-foot-compact__contact{order:1;}
}


/* ── 9. Playfair optical size handover (HANDOFF §3) ──────────────────────
   MUST stay LAST in this file: the token block above declares --font-display
   and --font-serif-text on :root unconditionally, so with equal specificity
   document order decides which wins. --font-serif-text is Playfair (the
   running-text cut) at every width; --font-display/-headline hand over only
   below their own breakpoints. 700px is also br[data-desk]'s breakpoint —
   if one moves, both move. */
:root{ --font-serif-text:'Playfair',"Times New Roman",serif; }
@media (max-width:1024px){
  :root{ --font-display-headline:'Playfair',"Times New Roman",serif; }
}
@media (max-width:700px){
  :root{
    --font-display:'Playfair',"Times New Roman",serif;
    --font-display-headline:'Playfair',"Times New Roman",serif;
  }
}

/* ============================================================================
   RESOLVED — items flagged during Phase 1, settled by the client directly

   1. --font-greek / Literata (tokens/typography.css, tokens/fonts-literata.css):
      confirmed correct as a Playfair substitute for Greek script specifically,
      not a general disagreement with HANDOFF. Token restored above; the font
      itself stays unloaded until a page actually sets Greek inside a Playfair
      context — none does yet, DOC pages are Fira-only (HANDOFF §5).
   2. ECM / narrative-page dot navigation (HANDOFF §5, §6): confirmed an
      abandoned earlier design-stage idea, never built into any prototype.
      Not implemented — removed from this file and from site.js.
   3. Dining Experiences booking URLs (HANDOFF §5 table vs. the prototype's
      actual hrefs): client will audit all links once the site is built.
      Still open — carry the prototype's URLs through Phase 2 and revisit.
   4. assets/menus/*.pdf: confirmed abandoned — the three menus are HTML
      pages only. Files stay on disk but out of the build (Phase 0 .gitignore).
   ========================================================================= */
