/* theme — generated from theme.json (the one swappable place) */

@font-face { font-family: "Chinook"; src: url("fonts/Chinook-ExtraBold.woff2") format("woff2"); font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: "Cabinet Grotesk"; src: url("fonts/CabinetGrotesk-Light.woff2") format("woff2"); font-weight: 300; font-style: normal; font-display: swap; }
@font-face { font-family: "Cabinet Grotesk"; src: url("fonts/CabinetGrotesk-Regular.woff2") format("woff2"); font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: "Cabinet Grotesk"; src: url("fonts/CabinetGrotesk-Bold.woff2") format("woff2"); font-weight: 700; font-style: normal; font-display: swap; }

:root{
  --bg: #FFFFFF;
  --surface: #FFFFFF;
  --surface-alt: #F4E6DC;
  --ink: #2A1E1B;
  --ink-muted: #6E5450;
  --line: #B0B6DC;
  --primary: #313A78;
  --primary-ink: #FFFFFF;
  --primary-ink-dim: #D3D7EE;
  --secondary: #8A93C7;
  --accent: #C4442A;
  --accent-ink: #FFFFFF;
  --warn: #B85400;
  --hero-dim: 0;
  --ink-invert: #FFFFFF;
  --ink-invert-dim: #E5D8CE;
  --font-display: "Chinook", "Trebuchet MS", Verdana, sans-serif;
  --font-body: "Cabinet Grotesk", system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif;
  --font-accent: "Cabinet Grotesk", system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif;
  --track-display: -0.01em;
  --track-accent: 0.15em;
}


/* structure — template.css */

/* ===========================================================================
   coffeebyhand v2 — strawberry-and-matcha
   The Beachside Hotel's anatomy (gutter wordmark + hairline cell nav, one
   photo hero, a checkerboard photo grid, a lone plate band, split bands with
   a terminal bar, one solid accent band) on a pastel pink ground, with a
   slightly darker matcha green carrying every border, button and accent.
   No marquee. Every value comes from _build-spec.md.
   =========================================================================== */

:root {
  --t-word: clamp(38px, 8.6vw, 118px);
  --maxw: 1320px;
  --gut: 24px;

  --t-display: clamp(44px, 6.4vw, 92px);
  --t-big: clamp(30px, 3.6vw, 52px);
  --t-mid: clamp(20px, 1.9vw, 26px);
  --t-body: clamp(18px, 1.25vw, 19px);
  --t-lbl: 14.5px;

  --ease-out: cubic-bezier(.16, .84, .44, 1);
  --ease-wipe-hard: cubic-bezier(.7, 0, .2, 1);
}

/* ---- reset --------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--t-body);
  font-weight: 400;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}
img, svg, video { display: block; max-width: 100%; }
h1, h2, h3, h4 { margin: 0; font-family: var(--font-display); font-weight: 400; letter-spacing: var(--track-display); line-height: 1.02; }
p, ul, ol, address { margin: 0; }
ul { padding: 0; list-style: none; }
a { color: inherit; text-decoration: none; }
address { font-style: normal; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }

.wrap { max-width: var(--maxw); margin: 0 auto; padding: 0 var(--gut); }

/* small tracked uppercase label — the page's only non-display voice */
.lbl, .index__k, .board__k, .deliver__k, .rooms__k,
.tile__cap, .cell, .btn, .rooms__cta, .link, .gf__h,
.deliver__list li, .plate__cap, .col__tag {
  font-family: var(--font-body);
  font-size: var(--t-lbl);
  font-weight: 700;
  letter-spacing: var(--track-accent);
  text-transform: uppercase;
}

.arw-ne { flex: none; }

/* ---- reveal contract — nothing outside html.js-reveal hides anything ----- */
:where(html.js-reveal) .reveal {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity .65s var(--ease-out), transform .65s var(--ease-out);
}
:where(html.js-reveal) .reveal.in { opacity: 1; transform: none; }
:where(html.js-reveal) .reveal[data-d="1"] { transition-delay: .07s; }
:where(html.js-reveal) .reveal[data-d="2"] { transition-delay: .14s; }

:where(html.js-reveal) .stag > * {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity .6s var(--ease-out), transform .6s var(--ease-out);
}
:where(html.js-reveal) .stag.in > * { opacity: 1; transform: none; }
:where(html.js-reveal) .stag.in > *:nth-child(2) { transition-delay: .07s; }
:where(html.js-reveal) .stag.in > *:nth-child(3) { transition-delay: .14s; }

/* chimac's wipe: a single hard left-to-right clip pass. The script triggers a
   whole ROW at once when that row comes into view, and the per-column delay
   below walks the cells across it, so rows arrive one at a time and stack. */
:where(html.js-reveal) .rv {
  clip-path: inset(0 100% 0 0);
  transition: clip-path .62s var(--ease-wipe-hard);
}
:where(html.js-reveal) .rv.in { clip-path: inset(0 0 0 0); }
:where(html.js-reveal) .rv[data-col="1"] { transition-delay: .11s; }
:where(html.js-reveal) .rv[data-col="2"] { transition-delay: .22s; }
/* the menu cards use the same left-to-right wipe as the index grid; the photo
   inside each card follows a beat behind its own card */
:where(html.js-reveal) .rail__track .mgroup:nth-child(2) { transition-delay: .09s; }
:where(html.js-reveal) .rail__track .mgroup:nth-child(3) { transition-delay: .18s; }
:where(html.js-reveal) .rail__track .mgroup:nth-child(4) { transition-delay: .27s; }
:where(html.js-reveal) .rail__track .mgroup:nth-child(5) { transition-delay: .36s; }
:where(html.js-reveal) .rail__track .mgroup:nth-child(6) { transition-delay: .45s; }
/* The photo wipes on its OWN arrival, not the card's: the same observer that
   fetches it adds `is-shown`, so the picture opens as you swipe onto its box
   rather than all of them running at once when the menu scrolls into view. */
:where(html.js-reveal) .mgroup__img {
  clip-path: inset(0 100% 0 0);
  transition: clip-path .62s var(--ease-wipe-hard);
}
:where(html.js-reveal) .mgroup__img.is-shown { clip-path: inset(0 0 0 0); }
:where(html.js-reveal) .gram__i { transition-delay: 0s; }
:where(html.js-reveal) .gram__i[data-rv="1"] { transition-delay: .06s; }
:where(html.js-reveal) .gram__i[data-rv="2"] { transition-delay: .12s; }
:where(html.js-reveal) .gram__i[data-rv="3"] { transition-delay: .18s; }
:where(html.js-reveal) .gram__i[data-rv="4"] { transition-delay: .24s; }

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .001ms !important; animation-iteration-count: 1 !important;
      transition-duration: .001ms !important; }
}

/* ========================================================== MASTHEAD ===== */
.mast { background: var(--bg); }
.mast__plate { padding-top: 30px; padding-bottom: 20px; }
.mast__wm {
  display: block;
  width: 100%;
  font-family: var(--font-display);
  line-height: .9;
  color: var(--primary);
  white-space: nowrap;
}
/* "The" rides above the stretched wordmark, small and centred, the way the
   shop's own sign carries it */
.mast__the {
  display: block;
  text-align: center;
  font-size: clamp(13px, 1.6vw, 22px);
  letter-spacing: .2em;
  text-indent: .2em;
  text-transform: uppercase;
  line-height: 1;
  margin-bottom: .5em;
}
.mast__letters {
  display: flex;
  justify-content: space-between;
  width: 100%;
  font-size: var(--t-word);
}
/* a space between words is its own flex item; without a min width it collapses
   and the wordmark reads as one long word */
.mast__letters span { display: inline-block; min-width: .06em; transition: transform .22s var(--ease-out); }
.mast__wm:hover .mast__letters span:nth-child(3n)   { transform: translateY(-6px); }
.mast__wm:hover .mast__letters span:nth-child(3n+1) { transform: translateY(-3px); }
.mast__wm:hover .mast__letters span:nth-child(3n+2) { transform: translateY(-5px); }

.cellbar { position: sticky; top: 0; z-index: 60; background: var(--bg); }
.cells {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  width: 100%;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
.cell {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 56px;
  padding: 0 8px;
  text-align: center;
  border-right: 1px solid var(--line);
  white-space: nowrap;
  color: var(--primary);
  transition: background .2s var(--ease-out), color .2s var(--ease-out);
}
.cell:last-child { border-right: 0; }
.cell:hover { background: var(--primary); color: var(--primary-ink); }
/* white like the rest of the bar — the cell dividers are the only frame it needs */
.cell--solid { background: var(--bg); color: var(--primary); }
.cell--solid:hover { background: var(--primary); color: var(--primary-ink); }
.cell--burger { display: none; flex-direction: column; gap: 4px; }
.cell--burger i { display: block; width: 18px; height: 2px; background: currentColor; }

.drawer { display: none; }

/* ============================================================== HERO ===== */
.hero {
  position: relative;
  height: clamp(440px, 74vh, 760px);
  overflow: hidden;
  background: var(--ink);
}
.hero__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform: scale(1.06);
  transition: transform 1.6s var(--ease-out);
}
:where(html.js-reveal) .hero.in .hero__img { transform: scale(1); }
:where(html:not(.js-reveal)) .hero__img { transform: none; }
.hero__scrim {
  position: absolute;
  inset: 0;
  /* --hero-dim in theme.json scales the whole veil: 1 is as shipped, 1.3 is
     darker, 0.7 is lighter. It is the only number to touch. */
  background: linear-gradient(to right,
    rgba(42, 30, 27, calc(.72 * var(--hero-dim))) 0%,
    rgba(42, 30, 27, calc(.42 * var(--hero-dim))) 42%,
    rgba(42, 30, 27, calc(.06 * var(--hero-dim))) 100%);
}
.hero__txt {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  bottom: 0;
  width: 100%;
  padding-bottom: 52px;
}
/* A live open/closed readout, not decoration — so it gets a solid plate to sit
   on rather than relying on the scrim to hold it off the photograph. */
.hero__stat {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 18px;
  padding: 8px 14px;
  border-radius: 999px;
  background: var(--surface);
  color: var(--ink-muted);
  font-family: var(--font-body);
  font-size: calc(var(--t-lbl) - 2px);
  font-weight: 700;
  letter-spacing: var(--track-accent);
  text-transform: uppercase;
}
.hero__stat .status__dot { width: 9px; height: 9px; }
.hero__stat.is-open { color: var(--primary); }
.hero__stat.is-closed { color: var(--accent); }
.hero__stat.is-soon { color: var(--warn); }
.hero__stat.is-open .status__dot,
.hero__stat.is-closed .status__dot,
.hero__stat.is-soon .status__dot { animation: statusBlink 1.8s ease-in-out infinite; }
.hl__line { display: block; }
/* the typed line: each one wipes open left to right on its own beat, and a
   block caret blinks at the end of the last — sandbar's opening, rebuilt */
:where(html.js-reveal) .hero__h .hl__line { clip-path: inset(0 100% 0 0); }
:where(html.js-reveal) .hero.in .hero__h .hl__line {
  clip-path: inset(0 0 0 0);
  transition: clip-path .62s var(--ease-wipe-hard) .18s;
}
:where(html.js-reveal) .hero.in .hero__h .hl__line:nth-child(2) { transition-delay: .58s; }
:where(html.js-reveal) .hero.in .hero__h .hl__line:nth-child(3) { transition-delay: .98s; }
/* The wipe clips to this element's box, which sliced the shadow off in a
   straight line under every row. The padding enlarges the clip box enough for
   the widest blur to live inside it, and the matching negative margins take
   the space back, so nothing moves. */
.hero__h .hl__line {
  white-space: nowrap;
  padding: 58px 60px 78px;
  margin: -58px -60px -78px;
}
.hero__h .hl__line:last-child::after {
  content: "";
  display: inline-block;
  width: .48ch;
  height: .74em;
  margin-left: .2ch;
  vertical-align: -.04em;
  background: currentColor;
  animation: caret 1.06s steps(1) infinite 1.5s;
}
@keyframes caret { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
  .hero__h .hl__line:last-child::after { animation: none; }
}

/* Those negative margins hang the last line's box 78px BELOW the headline,
   straight over the two hero buttons, which swallowed every tap on them. The
   headline stays selectable; the buttons and the status line simply take the
   layer above it. */
.hero__acts, .hero__stat { position: relative; z-index: 2; }

/* a flex column, so the lines' negative margins cannot collapse into each
   other and swallow the padding that gives the shadow its room */
.hero__h {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  font-size: var(--t-display);
  line-height: .96;
  color: var(--ink-invert);
  /* Three layers stepping outward rather than one hard one, so the darkness
     falls off gradually and follows the glyph shapes instead of reading as a
     slab behind them. The video's brightness changes shot to shot, so the type
     cannot rely on the scrim alone. */
  text-shadow:
    0 1px 3px rgba(34, 31, 23, .58),
    0 3px 9px rgba(34, 31, 23, .46),
    0 8px 26px rgba(34, 31, 23, .38),
    0 16px 54px rgba(34, 31, 23, .30);
  max-width: 17ch;
}
.hero__acts { margin-top: 28px; display: flex; flex-wrap: wrap; gap: 10px; }

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 13px 24px;
  border-radius: 999px;
  border: 1px solid transparent;
  transition: background .2s var(--ease-out), color .2s var(--ease-out), border-color .2s var(--ease-out);
}
.btn--solid { background: var(--primary); color: var(--primary-ink); }
.btn--solid:hover { background: var(--accent); color: var(--accent-ink); }
.btn--ghost { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.btn--ghost:hover { background: var(--ink-invert); border-color: var(--ink-invert); color: var(--ink); }

/* =========================================================== MARQUEE ===== */
/* Canopy's two-row counter-scroll band. Each track carries its content TWICE
   and animates to translateX(-50%), so the second copy lands exactly where the
   first began — that is what makes it seamless rather than snapping. Row B runs
   in reverse at a different duration so the two never lock into step. */
.marquee {
  background: var(--primary);
  color: var(--primary-ink);
  overflow: hidden;
  padding-top: clamp(1.6rem, 3vw, 2.6rem);
  padding-bottom: clamp(.8rem, 1.5vw, 1.2rem);
  border-bottom: 1px solid var(--line);
}
.marq__row { display: flex; overflow: hidden; }
.marq__row--a { align-items: center; }
.marq__track {
  display: flex;
  align-items: center;
  gap: clamp(1.4rem, 3vw, 3rem);
  width: max-content;
  animation: marq 46s linear infinite;
}
.marquee:hover .marq__track { animation-play-state: paused; }
.marq__word {
  font-family: var(--font-display);
  letter-spacing: var(--track-display);
  font-size: clamp(2.1rem, 6vw, 5rem);
  line-height: 1;
  color: var(--primary-ink);
  display: inline-flex;
  align-items: center;
  gap: clamp(1.4rem, 3vw, 3rem);
  white-space: nowrap;
  word-spacing: .34em;
}
.marq__word::before,
.marq__word::after {
  content: "";
  width: .34em; height: .34em;
  background: var(--accent);
  transform: rotate(45deg);
  flex: 0 0 auto;
}
.marq__ph {
  cursor: zoom-in;
  flex: 0 0 auto;
  width: clamp(92px, 11vw, 148px);
  aspect-ratio: 1;
  overflow: hidden;
  border: 1px solid color-mix(in srgb, var(--primary-ink) 45%, transparent);
}
.marq__ph img { width: 100%; height: 100%; object-fit: cover; filter: saturate(.9) contrast(1.03) brightness(.95); }

/* row 2 — receipt tape, opposite direction, slower */
.marq__row--b {
  margin-top: clamp(.9rem, 1.8vw, 1.4rem);
  border-top: 1px solid color-mix(in srgb, var(--primary-ink) 20%, transparent);
  padding-top: clamp(.7rem, 1.4vw, 1.1rem);
}
.marq__track--tape { gap: 0; animation-duration: 34s; animation-direction: reverse; }
.tape__item {
  display: inline-flex;
  align-items: center;
  font-family: var(--font-accent);
  text-transform: uppercase;
  font-weight: 700;
  letter-spacing: .18em;
  font-size: clamp(.8rem, .74rem + .22vw, .95rem);
  color: var(--primary-ink);
  white-space: nowrap;
}
.tape__item::after {
  content: "";
  width: 4px; height: 4px;
  border-radius: 50%;
  background: var(--accent);
  margin: 0 clamp(1rem, 2.4vw, 2.2rem);
  flex: 0 0 auto;
}
@keyframes marq { to { transform: translateX(-50%); } }

/* ============================================================= INTRO ===== */
/* ============================================================ STATUS ===== */
.status { background: var(--surface-alt); padding: 88px 0 72px; }
.status__media { position: relative; aspect-ratio: 20 / 9; overflow: hidden; border: 1px solid var(--line); }
.status__media img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }

/* the ledger sits in a small centred slip below the photo — deliberately
   denser than the visit block's list (tighter padding + line-height, no
   address/near/link riding along with it), so its total height reads
   noticeably shorter even though it repeats the same seven rows. */
/* the open-now panel, now living in the visit block. The head is a tinted
   plate so the status reads as a state rather than a line of copy, and the
   ledger sits under it. */
.status__panel {
  margin-top: 22px;
  border: 1px solid var(--line);
  background: var(--surface);
}
.status__line, .status__sub { padding-inline: 20px; }
.status__hours { margin: 0 20px 18px; }
.status__hours { border-top: 1px solid var(--line); }
/* the owner read this column as undersized — the ledger now runs at the same
   19px as the rest of the visit block rather than shrinking inside its panel */
.status__hours .hours__row { padding: 8px 0; font-size: 19px; line-height: 1.3; }
.status__hours .hours__row b { width: 50px; font-size: 17px; }
/* the tint used to run out to the panel's edges, past the rules above and
   below it; it now stops where they stop and closes itself with matching
   verticals */
.status__hours .hours__row.is-today {
  background: color-mix(in srgb, var(--primary) 16%, transparent);
  border: 1px solid var(--primary);
  color: var(--ink);
  font-weight: 700;
}
.status__hours .hours__row.is-today b { color: var(--primary); }

/* the status is the headline of this block, so it sits ABOVE the ledger and
   carries display scale — not a footnote under it */
.status__line {
  display: flex;
  align-items: center;
  gap: 12px;
  padding-top: 20px;
  color: var(--ink-muted);
  font-family: var(--font-display);
  font-size: clamp(30px, 2.8vw, 40px);
  line-height: 1;
  letter-spacing: var(--track-display);
}
.status__sub {
  padding-top: 6px;
  padding-bottom: 18px;
  margin-bottom: 18px;
  border-bottom: 1px solid var(--line);
  color: var(--ink-muted);
  font-size: 19px;
}
.status__line.is-open   ~ .status__sub { color: var(--primary); }
.status__line.is-closed ~ .status__sub { color: var(--accent); }
/* the hour before opening is neither: amber, and it blinks like the others */
.status__line.is-soon   ~ .status__sub { color: var(--warn); }
.status__dot {
  width: 14px;
  height: 14px;
  flex: none;
  border-radius: 50%;
  background: currentColor;
}
.status__line.is-open { color: var(--primary); }
.status__line.is-closed { color: var(--accent); }
.status__line.is-soon { color: var(--warn); }
.status__line.is-open .status__dot,
.status__line.is-closed .status__dot,
.status__line.is-soon .status__dot { animation: statusBlink 1.8s ease-in-out infinite; }
@keyframes statusBlink { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }

.board__jump {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px;
  margin-bottom: 34px;
}
.jump {
  padding: 11px 22px;
  border-radius: 999px;
  background: var(--surface);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--t-lbl);
  font-weight: 700;
  letter-spacing: var(--track-accent);
  text-transform: uppercase;
  transition: background .2s var(--ease-out), color .2s var(--ease-out);
}
.jump:hover { background: var(--accent); color: var(--accent-ink); }
.jump__short { display: none; }
/* Photograph at the foot of the cards that have room for one. The frame is a
   thick matcha border with a hairline of the same green held off it — the
   double line is this section's own signature, used nowhere else. */
.mgroup__img {
  display: block;
  position: relative;
  margin: 18px 4px 4px;
  aspect-ratio: 16 / 10;
  overflow: hidden;
  border: 2px solid var(--primary);
  outline: 1px solid var(--primary);
  outline-offset: 4px;
}
.mgroup__img img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* the photographs open in the lightbox, so they read as controls */
.mgroup__img[data-img] { cursor: pointer; }
.mgroup__img[data-img]:focus-visible { outline-color: var(--accent); outline-width: 2px; }

/* Two pictures in one card. The wrapper takes the place the single photo held —
   same margins, same share of the leftover column — so no card changes height;
   only what sits inside the space is different.

   --duo  a square photograph with a second square overlapping its lower right,
          the way a print layout drops an inset over a plate. Both are sized off
          the wrapper's HEIGHT (a % height resolves against it, and aspect-ratio
          then gives the width), which is what keeps them square whatever height
          the rail hands the card.
   --pair two frames side by side, each keeping the full height, so a wide
          landscape slot becomes two upright-ish ones. */
/* Every rule here carries the .mgroup prefix on purpose: .mgroup--grow
   .mgroup__img sets aspect-ratio:auto and a 150px floor, and at equal
   specificity it comes later in the file and would win.

   Here, in the wide layout, both kinds are simply two frames side by side —
   the card is a grid column with no height to divide up, so the overlapping
   arrangement has nothing to size itself against. The phone gets that one. */
.mimgs { display: flex; gap: 10px; margin: 18px 4px 4px; }
.mgroup .mimgs .mgroup__img {
  /* relative, never static: the <img> inside is absolutely positioned and
     would otherwise escape the frame and hunt for an ancestor */
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  min-height: 0;
  max-height: none;
  margin: 0;
  aspect-ratio: 16 / 10;
}

/* A card with no room at its foot takes its picture at the top instead, beside
   the heading — the rule under the heading shortens to clear it, and the rows
   that run alongside it keep to the remaining width. */
.mgroup--corner { position: relative; }
/* The corner cluster: one photograph, optionally with a smaller one dropped on
   its lower-left corner. The cluster carries the position now and the prints
   inside fill it, which is what lets the wide layout re-lay the same two
   pictures side by side without touching the markup. */
.mgroup--corner .mcorner {
  position: absolute;
  top: 20px;
  right: 20px;
  width: 104px;
  aspect-ratio: 1;
  z-index: 2;
}
.mgroup--corner .mcorner .mgroup__img--corner {
  position: absolute;
  inset: 0;
  width: auto;
  aspect-ratio: auto;
  min-height: 0;
  margin: 0;
}
/* Hung off the first print's lower-LEFT corner so it covers as little of it as
   possible. The left offset lands exactly on the gutter those two narrow rows
   already reserve for the picture, so it can never intrude on the 8oz line no
   matter what that line says — and that line has only 20px of slack at 390 and
   none at 360, so there is no room to widen the gutter for it. The bottom
   offset stops one pixel above the Yogurt row's price. */
.mgroup--corner .mcorner .mgroup__img--corner2 {
  position: absolute;
  left: -8px;
  right: auto;
  bottom: -14px;
  width: 52%;
  aspect-ratio: 1;
  min-height: 0;
  margin: 0;
  z-index: 2;
}
.mgroup--corner .mgroup__h { max-width: calc(100% - 118px); }
/* the two narrow rows that run alongside the picture. Padding rather than a
   max-width, so each row's own rule still runs the full width of the card —
   only the heading's rule is meant to stop short. The width left over is set by
   the longest of them, the 8oz line, which must not wrap: a second line there
   pushes the card, and the rail, 17px taller. */
.mgroup--corner .mlist > .msub:first-child,
.mgroup--corner .mlist > .mline:nth-child(2) { padding-right: 116px; }
/* the picture is worth more than the empty band this card ends on, so the list
   gives up the top of its space and takes it back at the bottom */
.mgroup--corner .mlist { margin-top: 18px; }
/* a short crop of a tall bowl loses the mound; bias up, but not hard against
   the top edge — flush to 0 cut the bowl off entirely */
.mgroup__img--top img { object-position: 50% 22%; }
/* the iced coffee sits small in a wide room shot; crop in on the cup */
.mgroup__img--zoom img { transform: scale(1.7); transform-origin: 50% 64%; }

/* A card marked --grow gives its photograph whatever height is left over in the
   column instead of a fixed 16/10 slot. The rails already stretch to equal
   height, so without this the shortest card ends in a band of empty ground —
   the "gap under the bingsu photo". The <img> is absolutely positioned and
   cover-fitted, so the picture simply crops as the frame gets taller. */
.mgroup--grow { display: flex; flex-direction: column; flex: 1 1 auto; }
/* the tag is a shrink-to-fit pill; as a flex child it would stretch to the card */
/* a flex child would stretch the whole width; the flag is only as wide as its
   own words */
.mgroup__tag { align-self: flex-start; }
.mgroup--grow .mgroup__img {
  flex: 1 1 auto;
  aspect-ratio: auto;
  min-height: 150px;
  max-height: 340px;
}

/* Three size columns, for the one group on the board that carries a 12oz price.
   Its rows always emit all three cells, empty ones included, so a drink sold
   only in 16/20 leaves the 12oz column blank instead of shifting left. */
.mgroup--tri .msizes,
.mgroup--tri .msub,
.mgroup--tri .mrow { grid-template-columns: 1fr 52px 52px 52px; }
.mgroup--tri .msizes span:nth-child(1) { grid-column: 2; }
.mgroup--tri .msizes span:nth-child(2) { grid-column: 3; }
.mgroup--tri .msizes span:nth-child(3) { grid-column: 4; }

/* a "+0.5 for caramel" line — indented under the drink it belongs to */
.mrow--mod .mrow__n {
  padding-left: 14px;
  font-family: var(--font-body);
  font-size: 15.5px;
  font-weight: 400;
  color: var(--ink-muted);
  position: relative;
}
.mrow--mod .mrow__n::before {
  content: "";
  position: absolute;
  left: 2px; top: .62em;
  width: 5px; height: 1px;
  background: var(--line);
}
.mrow--mod .mrow__p { font-size: 15.5px; font-weight: 400; color: var(--ink-muted); }
.mrow--mod { padding-top: 0; }

/* The board's red NEW flag. It sits on its own line ABOVE the dish rather than
   in front of it — inline it stole 40px from the name column and pushed long
   names onto a second line, which is the one thing the menu cannot do. */
/* Sits after the dish rather than above it, centred on the name's own line.
   `vertical-align: middle` on an inline-block is what keeps it centred without
   dragging the row's baseline — which is what pulled the price out of line when
   the badge led the row. */
.mrow__new {
  display: inline-block;
  vertical-align: middle;
  margin-left: 8px;
  width: max-content;
  padding: 1px 4px;
  border-radius: 2px;
  background: var(--accent);
  color: var(--accent-ink);
  font-family: var(--font-body);
  font-size: 8.5px;
  font-weight: 700;
  letter-spacing: .1em;
  line-height: 1.5;
  text-transform: uppercase;
}
/* the name itself, so a suffix can ride on the same line */
.mrow__t { display: block; }
/* "(Iced only)" / "6 pcs" / "M" — rides after the name, never wraps it */
.mrow__sfx {
  font-family: var(--font-body); font-style: normal;
  font-size: 13.5px; font-weight: 400;
  letter-spacing: .01em;
  color: var(--ink-muted);
  white-space: nowrap;
}
.mrow__sfx::before { content: "  "; white-space: pre; }

/* a plain small line inside the list — "cup, cone or food container" */
.mline {
  padding: 5px 0 7px;
  font-family: var(--font-body);
  font-size: 14.5px; line-height: 1.35;
  color: var(--ink-muted);
}
.mrow + .mline { margin-top: -3px; }

/* a run of flavours on one line, dots drawn in CSS rather than typed, so no
   font has to carry a separator glyph */
.mflav {
  display: grid;
  grid-template-columns: 1fr 62px 62px;
  align-items: baseline;
  gap: 0 8px;
  padding: 2px 0 8px;
}
.mgroup--one .mflav { grid-template-columns: 1fr 62px; }
.mflav__l {
  grid-column: 1 / -1;
  font-family: var(--font-body);
  font-size: 14.5px; line-height: 1.4;
  color: var(--ink-muted);
}
.mflav__l { word-spacing: 11px; }
.mflav__l span { position: relative; white-space: nowrap; word-spacing: normal; }
.mflav__l span:not(:first-child)::before {
  content: "";
  position: absolute; right: calc(100% + 5px); top: 50%; margin-top: -1.5px;
  width: 3px; height: 3px; border-radius: 50%;
  background: currentColor;
}
.mflav .mrow__p { font-size: 16px; }

/* the board's blue "add a slice / add cheese" line, above the list */
.maddon {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  margin-top: 10px;
  font-family: var(--font-body);
  font-size: 15.5px;
  color: var(--accent);
}
.maddon__p { flex: none; font-weight: 700; }

/* the sticky cell bar would otherwise sit over the rail you just jumped to —
   the cards carry their own ids too, so a footer link lands on the box */
.rail { scroll-margin-top: 76px; }
.mgroup { scroll-margin-top: 84px; }

/* ============================================================= INDEX ===== */
.index { background: var(--bg); padding: 88px 0 0; }
.index__head { padding-bottom: 26px; }
.index__k { color: var(--primary); margin-bottom: 10px; }
.index__h { font-size: var(--t-big); max-width: 20ch; }

/* the grid is a direct child of the full-width section, so it spans the
   viewport without the 100vw/negative-margin trick that risks a scrollbar */
/* three across at EVERY width — twelve cells, four rows, no reflow */
.index__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  width: 100%;
}
.cellsq { position: relative; min-width: 0; aspect-ratio: 1 / 1; overflow: hidden; }

.tile__media { display: block; position: absolute; inset: 0; overflow: hidden; }
.tile__media img {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  transition: transform .5s var(--ease-out);
}
.tile__cap {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  z-index: 2;
  padding: 12px 14px;
  background: linear-gradient(to top, rgba(42, 30, 27, .84), rgba(42, 30, 27, 0));
  color: var(--ink-invert);
  opacity: 0;
  transform: translateY(8px);
  transition: opacity .35s var(--ease-out), transform .35s var(--ease-out);
}
.tile { cursor: zoom-in; }
.tile:hover .tile__media img { transform: scale(1.06); }
.tile:hover .tile__cap,
.tile:focus-visible .tile__cap { opacity: 1; transform: none; }

/* the colour cells — one word, no photograph */
.word {
  display: grid;
  place-items: center;
  padding: 10px;
  text-align: center;
  background: var(--primary);
  color: var(--primary-ink);
  cursor: pointer;
  transition: transform .3s var(--ease-out);
}
.word--alt { background: var(--accent); color: var(--accent-ink); }
.word__t {
  font-family: var(--font-display);
  text-transform: uppercase;
  font-size: clamp(20px, 2.3vw, 33px);
  line-height: 1.05;
  letter-spacing: var(--track-display);
  transition: transform .3s var(--ease-out);
}
/* tapping a colour cell draws a ring inward and settles — a focus beat, not a
   state change; there is nothing behind it to reveal */
.word::after {
  content: "";
  position: absolute;
  inset: 10px;
  border: 2px solid currentColor;
  opacity: 0;
  transform: scale(1.06);
  transition: opacity .3s var(--ease-out), transform .3s var(--ease-out);
  pointer-events: none;
}
.word:hover::after,
.word:focus-visible::after,
.word.is-on::after { opacity: .55; transform: none; }
.word.is-on .word__t { transform: scale(.94); }

/* ============================================================= PLATE ===== */
.plate {
  position: relative;
  aspect-ratio: 21 / 9;
  max-height: 62vh;
  overflow: hidden;
  background: var(--ink);
}
.plate__el { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.plate__cap {
  position: absolute;
  left: 22px;
  bottom: 22px;
  padding: 8px 14px;
  background: var(--primary);
  color: var(--primary-ink);
}
.plate__btn {
  position: absolute;
  right: 22px;
  bottom: 22px;
  width: 42px;
  height: 42px;
  border-radius: 999px;
  background: var(--primary);
  display: grid;
  place-items: center;
}
.plate__btn svg { width: 17px; height: 17px; fill: var(--primary-ink); }
.plate__btn.is-paused svg rect:nth-child(2) { display: none; }

/* ============================================================= BOARD ===== */
.board { background: var(--primary); color: var(--primary-ink); padding: 88px 0; }
.board__head { text-align: center; margin-bottom: 30px; }
.board__k { color: var(--primary-ink-dim); margin-bottom: 10px; }
.board__h { font-size: clamp(40px, 5.4vw, 76px); }
.board__note { color: var(--primary-ink-dim); font-size: 17px; max-width: 44ch; margin: 12px auto 0; }
.board__foot { margin-top: 26px; color: var(--primary-ink-dim); font-size: 16px; }

/* Three slides. Desktop lays them out as three equal columns; a phone turns the
   same markup into a snap scroller. Both use stretch, so every slide is exactly
   as tall as the tallest one — the client's "same height" requirement holds at
   every width without measuring anything in JS. */
/* one column per rail, however many the board has — a fixed count of three left
   an empty column the day the menu went from three categories to two */
.rails {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  gap: 20px;
  align-items: stretch;
}
.rail { display: flex; flex-direction: column; min-width: 0; }
.rail__head {
  text-align: center;
  border-bottom: 1px solid var(--primary-ink-dim);
  padding-bottom: 10px;
  margin-bottom: 20px;
}
.rail__h {
  font-size: clamp(26px, 2.8vw, 38px);
  color: var(--primary-ink);
}
/* what is in this rail, so the swipe has a table of contents */
.rail__cats {
  margin-top: 7px;
  font-size: 15.5px;
  line-height: 1.5;
  color: var(--primary-ink-dim);
}
/* the separator is DRAWN, not typed: a dot glyph is a font-coverage gamble and
   the house rule bans dot characters as separators outright */
/* held to 70% so a long category list breaks into two balanced lines rather
   than filling the width and leaving a stub on the second */
.rail__cats { margin-inline: auto; max-width: 70%; }
/* the word and the dot that follows it are one unbreakable unit — otherwise
   the line can break between them and the dot starts the next line */
.rail__cats { word-spacing: 13px; }
.rail__cats span { position: relative; white-space: nowrap; word-spacing: normal; }
/* a separator that would end a wrapped line is dropped — the dot belongs
   BETWEEN two items on one line, not hanging off the end of one. The class is
   set by script, so with JS off the trailing dot simply stays. */
/* The separator is drawn in the gap BEFORE its item and is taken out of flow,
   so the gap itself is a plain space: at a line break that space collapses the
   way trailing whitespace always does, and dropping a dot costs no width. An
   in-flow separator fed its own removal back into the line breaking — hide the
   dot, the line got shorter, the wrap moved, the marks went stale. */
.rail__cats span.no-dot::before,
.mflav__l span.no-dot::before { content: none; }
.rail__cats span:not(:first-child)::before {
  content: "";
  position: absolute;
  right: calc(100% + 6px);
  top: 50%;
  margin-top: -1.5px;
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: currentColor;
}
.rail__vp { position: relative; flex: 1; }
.rail__track { display: flex; flex-direction: column; gap: 20px; height: 100%; }

.mgroup {
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--line);
  padding: 20px 20px 22px;
}
.mgroup__tag {
  display: inline-block; background: var(--accent); color: var(--accent-ink);
  padding: 4px 10px; margin-bottom: 10px;
}
.mgroup__h {
  font-size: clamp(24px, 2.2vw, 30px);
  border-bottom: 1px solid var(--line);
  padding-bottom: 10px;
}
.mgroup__note { margin-top: 12px; font-size: 16px; line-height: 1.45; color: var(--ink-muted); }

/* the size header sits directly over the price columns it labels */
/* a section over a LIST repeats its whole block, so the spans have to live
   inside one div, not the div inside the section */
.msizes:empty { display: none; }
.msizes {
  display: grid;
  grid-template-columns: 1fr 62px 62px;
  gap: 0 8px;
  padding: 8px 0 2px;
  font-family: var(--font-body);
  font-size: 13.5px; font-weight: 700;
  letter-spacing: var(--track-accent); text-transform: uppercase;
  color: var(--primary);
}
/* size labels are small caps sitting in a 62px column — at label size with
   full accent tracking "DOUBLE" ran straight out of its cell */
.msizes span, .msub__s span { font-size: 12px; letter-spacing: .06em; }
/* the size label centres over its price column */
.msizes span { text-align: center; }
/* explicit placement: auto-flow after an explicitly-placed item wraps the
   second label onto its own row instead of sitting beside the first */
.msizes span:nth-child(1) { grid-column: 2; }
.msizes span:nth-child(2) { grid-column: 3; }
.mgroup--one .msizes { grid-template-columns: 1fr 62px; }

/* a merged box (Bakery & Croffle, Waffle & Panini) keeps its parts labelled */
/* It runs the same track list as .mrow so a sub-heading can carry its own size
   columns (ESPRESSO / SOLO / DOPPIO) sitting exactly over the prices below. The
   heading's own text is an anonymous grid item and lands in column 1. */
.msub {
  display: grid;
  grid-template-columns: 1fr 62px 62px;
  align-items: baseline;
  gap: 0 8px;
  font-family: var(--font-body);
  font-size: 13.5px; font-weight: 700;
  letter-spacing: var(--track-accent); text-transform: uppercase;
  color: var(--accent);
  padding: 14px 0 4px;
}
.mgroup--one .msub { grid-template-columns: 1fr 62px; }
.mlist > .msub:first-child { padding-top: 6px; }
/* the wrapper is transparent to the grid so its two spans can be placed in the
   price columns individually rather than sharing one cell */
.msub__s { display: contents; }
.msub__s span { text-align: center; color: var(--primary); }
/* two headings inside one box — bingsu and waffles — at box-title scale */
.msub--big {
  font-family: var(--font-display);
  font-size: clamp(24px, 2.2vw, 30px);
  letter-spacing: var(--track-display);
  text-transform: none;
  color: var(--ink);
  margin-top: 0;
  padding-top: 12px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--line);
}
.msub--big:first-child { padding-top: 0; }
/* explicit placement, same reason as .msizes — and a lone label belongs over
   the p1 column, not the p2 one it would auto-flow into */
.msub__s span:nth-child(1) { grid-column: 2; }
.msub__s span:nth-child(2) { grid-column: 3; }

.mrow {
  display: grid;
  grid-template-columns: 1fr 62px 62px;
  align-items: baseline;
  gap: 0 8px;
  padding: 7px 0;
  border-bottom: 1px solid var(--line);
}
.mgroup--one .mrow { grid-template-columns: 1fr 62px; }
.mrow:last-child { border-bottom: 0; }
/* Soft serve and toppings put their one price at the far right, so the names
   and flavour runs get the width of both middle columns to stay on one line. */
.mrow--far .mrow__p, .mflav--far .mrow__p { grid-column: 3; }
.mflav--far .mflav__l { grid-column: 1 / 3; }
/* A trailing line or flavour run is part of the item above it, so the item's
   rule steps down to sit under the whole block rather than through it. */
.mrow:has(+ .mline), .mrow:has(+ .mflav) { border-bottom: 0; padding-bottom: 2px; }
.mline, .mflav { border-bottom: 1px solid var(--line); }
.mline:last-child, .mflav:last-child { border-bottom: 0; }
.mflav + .mflav { border-top: 0; }
.mrow__n {
  grid-column: 1;
  font-family: var(--font-display);
  font-size: 20px; line-height: 1.2;
}
/* Its own cell on a second grid row, spanning the price columns too — inside
   the name cell it was boxed into one column and wrapped early. */
.mrow__d {
  grid-column: 1 / -1;
  display: block; margin-top: 2px;
  font-family: var(--font-body); font-style: normal;
  font-size: 15px; line-height: 1.35; color: var(--ink-muted);
}
.mrow__p {
  font-family: var(--font-display);
  font-size: 19px; text-align: right;
  font-variant-numeric: tabular-nums;
  color: var(--primary);
}

/* =========================================================== DELIVER ===== */
.deliver { background: var(--surface-alt); padding: 0 0 0; }
.deliver__row { display: grid; grid-template-columns: 1fr 1fr; }
.deliver__media { position: relative; aspect-ratio: 4 / 3; overflow: hidden; border: 1px solid var(--line); }
.deliver__media img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.deliver__txt {
  padding: 56px 56px 56px 60px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  border: 1px solid var(--line);
  border-left: 0;
}
.deliver__k { color: var(--primary); margin-bottom: 12px; }
.deliver__h { font-size: var(--t-big); max-width: 15ch; }
.deliver__p { margin-top: 16px; max-width: 42ch; color: var(--ink-muted); }
.deliver__list { margin-top: 26px; border-top: 1px solid var(--line); }
.deliver__list li { padding: 12px 0; border-bottom: 1px solid var(--line); color: var(--ink); }
/* A white plate with the label set small and wide, and a matcha keyline drawn
   inside its edge — the same double-frame language the menu photographs and the
   room cards use. The green band below starts flush against its lower edge. */
.deliver__bar {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 76px;
  margin-top: -1px;
  background: var(--bg);
  color: var(--primary);
  font-family: var(--font-body);
  font-size: var(--t-lbl);
  font-weight: 700;
  letter-spacing: var(--track-accent);
  text-transform: uppercase;
  outline: 1px solid var(--primary);
  outline-offset: -9px;
  transition: background .2s var(--ease-out), color .2s var(--ease-out), outline-color .2s var(--ease-out);
}
.deliver__bar:hover {
  background: var(--primary);
  color: var(--primary-ink);
  outline-color: var(--primary-ink);
}

/* Smooth in-page travel for the three menu jump buttons and the nav anchors.
   Gated on the same reduced-motion preference the reveal contract honours, so
   nobody who asked for stillness gets a moving page. */
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

/* ============================================================= ROOMS ===== */
.rooms {
  background: var(--primary);
  color: var(--primary-ink);
  padding: 46px 0 84px;
  margin-top: 0;
  /* the rail below is pulled out to the gutter; clipping in x only means that
     bleed can never reach the page scrollbar, while the mark's negative top
     margin still hangs out of the band vertically */
  overflow-x: clip;
}
/* the mark is drawn in the band's own cream now, not punched out of it on a
   white plate — nothing straddles the seam any more */
.rooms__mark {
  width: 54px;
  height: 54px;
  margin: 0 auto 26px;
  color: var(--primary-ink);
  position: relative;
}
.rooms__head { text-align: center; margin-bottom: 32px; }
.rooms__k { color: var(--primary-ink-dim); margin-bottom: 10px; }
.rooms__h { font-size: var(--t-big); max-width: 22ch; margin: 0 auto; }

/* A snap rail, not a grid: only about three of the four spaces fit at once, so
   the card cut off at the right edge is what tells you there is more to scroll.
   A grid of four would have shrunk every photograph to fit and said nothing. */
.rooms__vp {
  position: relative;
  /* run to the gutter, then hand the same value back as track padding, so the
     first card still lines up with the heading above it */
  margin-inline: calc(var(--gut) * -1);
}
.rooms__track {
  display: flex;
  gap: 18px;
  padding-inline: var(--gut);
  /* both axes allowed: the rail takes a sideways drag, the page takes a
     vertical one. `pan-x` alone would forbid vertical panning outright. */
  touch-action: pan-x pan-y pinch-zoom;
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: var(--gut);
  -webkit-overflow-scrolling: touch;
  /* the bar would cut across the green band; the rail keeps its keyboard focus
     ring and its touch drag either way */
  scrollbar-width: none;
}
.rooms__track::-webkit-scrollbar { display: none; }

/* 3.2 cards across — the fraction is the point */
.rcard {
  flex: 0 0 calc((100% - 40px) / 3.2);
  min-width: 0;
  scroll-snap-align: start;
  display: flex;
  flex-direction: column;
}
/* the photograph sits inside a cream mat with a hairline drawn around it, so
   the card reads as a mounted print rather than a bleed image */
.rcard__media {
  display: block;
  position: relative;
  aspect-ratio: 1 / 1;
  overflow: hidden;
  background: var(--ink);
  border: 7px solid var(--bg);
  box-shadow: inset 0 0 0 1px var(--line), 0 0 0 1px var(--line);
}
.rcard__media img {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  transition: transform .5s var(--ease-out);
}
.rcard:hover .rcard__media img { transform: scale(1.05); }
/* the card body sits straight on the green — no plate, so the photographs read
   as a row of pictures rather than a row of boxes */
.rcard__n {
  margin-top: 14px;
  font-family: var(--font-display);
  font-size: clamp(20px, 2vw, 26px);
  line-height: 1.1;
  letter-spacing: var(--track-display);
  color: var(--primary-ink);
}
.rcard__d {
  margin-top: 6px;
  font-family: var(--font-body);
  font-size: 17px;
  line-height: 1.45;
  color: var(--primary-ink-dim);
}

/* a --bg plate is the only thing that pops off this much green */
.rooms__cta {
  display: flex;
  align-items: center;
  gap: 8px;
  width: max-content;
  margin: 34px auto 0;
  padding: 15px 34px;
  background: var(--bg);
  color: var(--primary);
  transition: background .2s var(--ease-out), color .2s var(--ease-out);
}
.rooms__cta:hover { background: var(--accent); color: var(--accent-ink); }

/* ================================================ VISIT + INSTAGRAM ===== */
.visit { padding: 88px 0; }
/* 400px, not 340: the address plate below carries display-scale type now and
   340 forced it to wrap. The second track is minmax(0, 1fr), so the instagram
   grid absorbs the difference instead of overflowing. */
.visit__wrap { display: grid; grid-template-columns: 400px minmax(0, 1fr); gap: 52px; align-items: start; }

.kick { display: flex; align-items: center; gap: 14px; font-size: clamp(26px, 2.8vw, 38px); }
.kick__rule { flex: 1; height: 1px; background: var(--line); }

/* The address, the directions button and the phone number are one plate rather
   than three stray lines — and the button rides beside the address at the top
   of it, above the hours panel, because that is the thing people came for. */
.visit__card {
  margin-top: 18px;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 10px 18px;
  align-items: start;
  padding: 20px 22px;
  border: 1px solid var(--line);
  background: var(--surface);
}
/* `display: contents` so the street and the city place themselves on the
   card's own grid: the street gets the full width, the city shares its row
   with the directions button. */
.visit__addr { display: contents; }
/* the street is a title, not body copy */
.visit__street {
  grid-column: 1 / -1; grid-row: 1;
  font-family: var(--font-display);
  font-size: clamp(23px, 2.1vw, 28px);
  line-height: 1.05;
  letter-spacing: var(--track-display);
  color: var(--ink);
}
.visit__city {
  grid-column: 1; grid-row: 2;
  align-self: center;
  white-space: nowrap;
  font-family: var(--font-body); font-size: 18px; line-height: 1.35; color: var(--ink-muted);
}
.visit__dir {
  grid-column: 2; grid-row: 2;
  justify-self: end;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 12px 17px;
  border-radius: 999px;
  background: var(--primary);
  color: var(--primary-ink);
  font-family: var(--font-body);
  font-size: 13.5px;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  white-space: nowrap;
  transition: background .2s var(--ease-out), color .2s var(--ease-out);
}
.visit__dir:hover { background: var(--accent); color: var(--accent-ink); }
.visit__tel {
  grid-column: 1 / -1; grid-row: 3;
  padding-top: 14px;
  border-top: 1px solid var(--line);
  font-size: 19px;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  transition: color .2s var(--ease-out);
}
.visit__tel:hover { color: var(--accent); }

#hours { margin-top: 22px; border-top: 1px solid var(--line); }
.hours__row {
  display: flex;
  align-items: baseline;
  gap: 14px;
  padding: 9px 0;
  border-bottom: 1px solid var(--line);
  color: var(--ink-muted);
  font-size: 19px;
  font-variant-numeric: tabular-nums;
}
.hours__row b { width: 48px; flex: none; font-weight: 700; }
.hours__row.is-today { color: var(--ink); }
.hours__row.is-today b { color: var(--accent); }
.link {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  margin-top: 18px;
  color: var(--accent);
  border-bottom: 1px solid currentColor;
  padding-bottom: 2px;
}

/* Nothing here but the handle and the clips. Each clip carries a double frame
   whose two rules differ in weight, and the weights differ from clip to clip,
   so the rail reads as a hand-cut set rather than a template. */
.gram__head {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  margin: 0 auto 34px;
  justify-content: center;
}
/* the glyph is a solid green tile with the camera knocked out of it — the
   inverse of the outline mark it replaced */
.gram__ico {
  width: 34px; height: 34px; flex: none;
  background: var(--primary);
  color: var(--primary-ink);
  border-radius: 11px;
  padding: 6px;
}
.gram__handle { font-family: var(--font-display); font-size: clamp(25px, 2.5vw, 35px); color: var(--primary); }
.gram { min-width: 0; }
.gram__vp { display: block; min-width: 0; overflow: hidden; }
.gram__rail {
  display: flex;
  gap: 26px;
  /* both axes allowed: the rail takes a sideways drag, the page takes a
     vertical one. `pan-x` alone would forbid vertical panning outright. */
  touch-action: pan-x pan-y pinch-zoom;
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  padding-inline: calc((100% - min(340px, 30vw)) / 2);
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.gram__rail::-webkit-scrollbar { display: none; }
/* Three rules, not one: a matcha frame, a cream gap, a red keyline, another
   gap — and four weight sets, so no two neighbours are framed alike. */
/* One frame, every clip: a hairline in matcha, a band of matcha-and-cream
   dashes running round all four edges, and another hairline inside. The band is
   four repeating gradients — one per edge — because no border style alternates
   two colours. */
.clip {
  position: relative;
  flex: 0 0 min(340px, 30vw);
  aspect-ratio: 9 / 16;
  scroll-snap-align: center;
  min-width: 0;
  padding: 13px;
  border: 2px solid var(--primary);
  background-color: var(--bg);
  /* The band is the full 17px of padding, so every square runs the whole way
     from the outer hairline to the inner one. The four corner marks are 14px
     square, filling the band corner to corner so they meet both hairlines the
     way the edge squares do. Each edge run is inset by a
     square's width at both ends so it never paints into a corner cell, and it
     starts on a gap so the pattern alternates out of the corner. */
  background-image:
    linear-gradient(var(--primary), var(--primary)),
    linear-gradient(var(--primary), var(--primary)),
    linear-gradient(var(--primary), var(--primary)),
    linear-gradient(var(--primary), var(--primary)),
    repeating-linear-gradient(90deg,  transparent 0 13px, var(--primary) 13px 26px),
    repeating-linear-gradient(270deg, transparent 0 13px, var(--primary) 13px 26px),
    repeating-linear-gradient(90deg,  transparent 0 13px, var(--primary) 13px 26px),
    repeating-linear-gradient(270deg, transparent 0 13px, var(--primary) 13px 26px),
    repeating-linear-gradient(180deg, transparent 0 13px, var(--primary) 13px 26px),
    repeating-linear-gradient(0deg,   transparent 0 13px, var(--primary) 13px 26px),
    repeating-linear-gradient(180deg, transparent 0 13px, var(--primary) 13px 26px),
    repeating-linear-gradient(0deg,   transparent 0 13px, var(--primary) 13px 26px);
  background-size:
    13px 13px, 13px 13px, 13px 13px, 13px 13px,
    calc(50% - 13px) 13px, calc(50% - 13px) 13px,
    calc(50% - 13px) 13px, calc(50% - 13px) 13px,
    13px calc(50% - 13px), 13px calc(50% - 13px),
    13px calc(50% - 13px), 13px calc(50% - 13px);
  background-position:
    left 0 top 0, right 0 top 0, left 0 bottom 0, right 0 bottom 0,
    left 13px top 0,    right 13px top 0,
    left 13px bottom 0, right 13px bottom 0,
    left 0 top 13px,    left 0 bottom 13px,
    right 0 top 13px,   right 0 bottom 13px;
  background-repeat: no-repeat;
}
.clip__in {
  display: block;
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
  border: 2px solid var(--primary);
  background: var(--ink);
  cursor: pointer;
}
.clip video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* held: a still frame is a deliberate state, so it says so */
.clip.is-held .clip__in::after { content: ""; position: absolute; inset: 0; background: rgba(34, 31, 23, .34); }
.clip__snd {
  position: absolute;
  right: 9px;
  bottom: 9px;
  z-index: 3;
  width: 38px;
  height: 38px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: var(--primary);
  color: var(--primary-ink);
  cursor: pointer;
  transition: background .2s var(--ease-out);
}
.clip__snd:hover { background: var(--accent); color: var(--accent-ink); }
.clip__snd svg { width: 21px; height: 21px; }
.clip__snd .wave { display: none; }
.clip.is-loud .clip__snd .wave { display: block; }
.clip.is-loud .clip__snd .slash { display: none; }

/* ============================================================ FOOTER ===== */
.grandfoot { background: var(--surface); border-top: 1px solid var(--line); padding: 80px 0 52px; }
.gf__grid { display: grid; grid-template-columns: 250px repeat(4, minmax(0, 1fr)); gap: 36px; align-items: start; }
.gf__mark { display: flex; flex-direction: column; gap: 16px; }
/* the shop's own seal sitting inside the spoked wheel, which turns around it —
   the wheel's hole is cut wide enough to leave a hairline of ground between the
   two, so the seal reads as a separate object rather than a hub */
.gf__seal { position: relative; display: block; width: 134px; height: 134px; }
.gf__ring { position: absolute; inset: 0; width: 100%; height: 100%; }
.gf__logo {
  position: absolute;
  top: 50%; left: 50%;
  width: 86.7%; height: 86.7%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  object-fit: cover;
}
@media (prefers-reduced-motion: no-preference) {
  .gf__ring { animation: sealSpin 26s linear infinite; }
}
@keyframes sealSpin { to { transform: rotate(360deg); } }
.gf__wm { font-family: var(--font-display); font-size: 26px; line-height: 1.05; color: var(--primary); }
.gf__h { color: var(--ink-muted); margin-bottom: 12px; }
.gf__col ul { display: flex; flex-direction: column; gap: 8px; }
.gf__col a { font-size: 18px; }
.gf__col a:hover { color: var(--accent); }
.icosprite { position: absolute; width: 0; height: 0; overflow: hidden; }
.gf__col .gf__list--ico { flex-direction: column; align-items: center; gap: 14px; }
.gf__svg { display: block; width: 24px; height: 24px; color: var(--primary); transition: color .2s var(--ease-out); }
.gf__list--ico a:hover .gf__svg { color: var(--accent); }

.gf__base {
  border-top: 1px solid var(--line);
  margin-top: 36px;
  padding-top: 20px;
  display: flex;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  color: var(--ink-muted);
  font-size: 16px;
}
.gf__addr { display: flex; flex-wrap: wrap; gap: 4px 16px; }

/* =========================================================== TABLET ===== */
@media (max-width: 1023px) {
  .board__masonry { columns: 2; }
  .visit__wrap { grid-template-columns: 1fr; gap: 40px; }
  .deliver__txt { padding: 40px 32px; }
  /* 2.2 across — the peek survives the narrower band */
  .rcard { flex: 0 0 calc((100% - 22px) / 2.2); }
}

/* =========================================================== MOBILE ===== */
@media (max-width: 767px) {
  :root { --gut: 14px; }

  /* masthead: only three cells fit — Menu | Order | burger. The rest go in
     the drawer. The wordmark still spans gutter to gutter. */
  .mast__plate { padding-top: 18px; padding-bottom: 12px; }
  .mast__letters { font-size: 28px; }
  .mast__the { font-size: 11px; margin-bottom: 8px; }
  /* the CTA needs more than a third to keep "ORDER DELIVERY" on one line */
  .cells { grid-template-columns: .85fr 1.55fr .6fr; }
  .cell { height: 46px; }
  /* Menu | Order Delivery | burger. Home and the rest live in the drawer. */
  .cells .cell:nth-child(1),
  .cells .cell:nth-child(n+3):nth-child(-n+6) { display: none; }
  .cell--burger { display: flex; }
  .drawer {
    display: block;
    background: var(--bg);
    max-height: 0;
    overflow: hidden;
    transition: max-height .3s var(--ease-out);
  }
  body.drawer-open .drawer { max-height: 320px; }
  .drawer a {
    display: block;
    padding: 13px 0;
    font-family: var(--font-display);
    font-size: 22px;
    color: var(--primary);
    border-bottom: 1px solid var(--line);
  }

  /* + the 33px of the tape marquee below it, as asked */
  /* 1.5x the tape marquee's 33px taken back off the hero */
  .hero { height: calc(clamp(400px, 68vh, 560px) - 17px); }
  /* the owner read the opening screen as too dark — the scrim now does only
     the work the headline needs and lets the room show through above it */
  .hero__scrim {
    background: linear-gradient(to top,
      rgba(42, 30, 27, calc(.74 * var(--hero-dim))) 0%,
      rgba(42, 30, 27, calc(.34 * var(--hero-dim))) 48%,
      rgba(42, 30, 27, calc(.05 * var(--hero-dim))) 100%);
  }
  .hero__txt { padding-bottom: 30px; }
  .hero__h { max-width: none; font-size: 38px; }
  .hero__acts { margin-top: 20px; }
  .hero__acts .btn { flex: 1; font-size: 13.5px; letter-spacing: .05em; padding: 12px 8px; white-space: nowrap; }


  .status { padding: 56px 0 48px; }
  .status__panel { max-width: none; padding: 16px 18px; }

  /* on a phone the white band above the grid is a box of its own — the eyebrow
     and the headline sit centred in it both ways, and the headline runs larger */
  .index { padding: 0; }
  .index__head {
    display: grid;
    place-content: center;
    min-height: 148px;
    padding: 18px 14px;
    text-align: center;
  }
  .index__k { margin-bottom: 12px; }
  .index__h { max-width: none; font-size: 34px; }
  .tile__cap { padding: 9px 10px; }

  .plate { aspect-ratio: 4 / 3; max-height: none; }
  .plate__cap { left: 12px; bottom: 12px; padding: 7px 11px; }
  .plate__btn { right: 12px; bottom: 12px; width: 38px; height: 38px; }

  .board { padding: 56px 0; }
  .board__head { display: block; margin-bottom: 24px; }
  .board__note { max-width: none; }
  /* Each group becomes its own horizontal rail, so a swipe only ever moves
     within that group. Cards sit at 88% so the next one peeks just enough to
     signal the swipe without stealing the width the item names need. */
  .rails { display: block; grid-template-columns: none; }
  .rail { margin-bottom: 30px; }
  .rail:last-child { margin-bottom: 0; }
  .rail__head { margin-bottom: 14px; }
  .rail__h { font-size: 27px; }
  .rail__cats { font-size: 15px; }
  .rail__vp { margin-inline: calc(var(--gut) * -1); }
  /* the viewport is pulled wider than the screen, so the arrows have to come
     back in by that same gutter or they hang off the edge */
  .rail__track {
    flex-direction: row;
    gap: 8px;
    height: auto;
    align-items: stretch;
    /* both axes allowed: the rail takes a sideways drag, the page takes a
     vertical one. `pan-x` alone would forbid vertical panning outright. */
    touch-action: pan-x pan-y pinch-zoom;
    overflow-x: auto;
  overflow-y: hidden;
    scroll-snap-type: x mandatory;
    padding-inline: 4%;
    scrollbar-width: none;
  }
  .rail__track::-webkit-scrollbar { display: none; }
  /* narrower on purpose: a wider sliver of the next card is the clearest
     signal that the row scrolls. The width it gives up is taken back from
     the padding and the price columns, so the names still hold one line. */
  .mgroup { flex: 0 0 93.5%; scroll-snap-align: center; }
  .board__jump { flex-wrap: nowrap; gap: 6px; }
  .jump {
    flex: 1 1 0; min-width: 0;
    padding: 10px 4px;
    text-align: center;
    font-size: 12px; letter-spacing: .06em;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  /* two categories, not three — the full names fit and read better */
  .jump__long { display: inline; }
  .jump__short { display: none; }
  /* Equal air above and below every photo: the gap over it matches the card's
     own bottom padding, and the photo then takes whatever height is left. The
     sizes differ card to card — the margins do not. */
  .mgroup { display: flex; flex-direction: column; padding-bottom: 20px; }
  .mgroup__img {
    /* the gap above the photo is the one piece of height that can move to the
       photo without changing the card's total — the photo absorbs whatever the
       margin gives up, so the rail's height is untouched */
    margin: 6px 0 0;
    flex: 1 1 auto;
    aspect-ratio: auto;
    min-height: 96px;
    outline-offset: 3px;
  }
  /* The wrapper takes the place the single photo held — same margins, same
     share of the leftover column — so no card changes height; only what sits
     inside the space is different. Both pictures are sized off the wrapper's
     HEIGHT (a % height resolves against it, and aspect-ratio then gives the
     width), which is what keeps them square whatever height the rail hands
     the card. */
  .mimgs { display: block; position: relative; margin: 6px 0 0; flex: 1 1 auto; min-height: 96px; }
  .mgroup .mimgs .mgroup__img { margin: 0; flex: none; min-height: 0; }
  /* --pair: two frames side by side, each keeping the full height, so one wide
     landscape slot becomes two upright-ish ones */
  .mimgs--pair { display: flex; gap: 9px; }
  .mgroup .mimgs--pair .mgroup__img { flex: 1 1 0; min-width: 0; aspect-ratio: auto; }
  /* --duo: a square photograph with a second square overlapping its lower
     right, the way a print layout drops an inset over a plate. The wrapper's
     WIDTH follows its height rather than filling the card: in a short, wide
     slot the two squares are small, and anchored to the slot's edges they
     drift apart until they read as two loose photographs instead of one
     composition. align-self keeps the flex column from stretching it back. */
  .mimgs--duo { align-self: center; width: auto; aspect-ratio: 1.12; max-width: 100%; }
  .mgroup .mimgs--duo .mgroup__img { position: absolute; aspect-ratio: 1; }
  .mgroup .mimgs--duo .mgroup__img:not(.mgroup__img--b) { left: 0; top: 0; height: 94%; }
  .mgroup .mimgs--duo .mgroup__img--b { right: 0; bottom: 0; height: 50%; z-index: 2; }
  /* --tuck: one photograph carrying the card, with a square tucked into its
     bottom-right corner. Unlike --duo the lead picture is not square, so it
     takes the slot's width and the wrapper is left to fill the card. The lead
     sits to the LEFT so the square has the right-hand strip to hang into. */
  .mimgs--tuck { align-self: stretch; width: auto; aspect-ratio: auto; max-width: none; }
  .mgroup .mimgs--tuck .mgroup__img { position: absolute; }
  .mgroup .mimgs--tuck .mgroup__img:not(.mgroup__img--b) {
    left: 0; top: 0; width: 80%; height: 90%; aspect-ratio: auto;
  }
  .mgroup .mimgs--tuck .mgroup__img--b { right: 0; bottom: 0; height: 62%; aspect-ratio: 1; z-index: 2; }
  .mgroup--corner .mcorner { top: 18px; right: 12px; width: 100px; }
  .mgroup--corner .mgroup__h { max-width: calc(100% - 110px); }
  .mgroup--corner .mlist > .msub:first-child,
  .mgroup--corner .mlist > .mline:nth-child(2) { padding-right: 108px; }
  /* the Coffee card's content already fills its box, so its photo sits on the
     floor — raising the floor is the only thing that lifts it, and only that
     one card grows as a result */
  .mgroup--grow .mgroup__img { flex: 1 1 auto; min-height: 150px; }
  /* these two sat near 100px against 150-250px neighbours; a third again taller */
  .mgroup__img--tall { min-height: 140px; }
  /* the rails are horizontal scrollers here and the cards are already sized by
     the swipe, so a grow photo takes a fixed floor rather than absorbing the

  /* wider cards mean the name column can hold a full line */
  .msizes, .msub, .mrow, .mflav { grid-template-columns: 1fr 44px 44px; gap: 0 5px; }
  .mgroup--one .msizes, .mgroup--one .msub, .mgroup--one .mrow, .mgroup--one .mflav { grid-template-columns: 1fr 44px; }
  /* One name on the board is longer than the column: "Strawberry Dubai
     Chocolate" plus its New badge wants 283px of a 260px column. Rather than
     let it take two lines — the one thing the whole row geometry exists to
     prevent — that row alone drops a step in size. */
  .mrow--fit .mrow__t { font-size: 16px; }
  .mrow__n { font-size: 18px; letter-spacing: -.014em; }
  .mrow__sfx { font-size: 12px; }
  /* the badge is the only thing sharing the dish's line, so it gives back
     the few pixels that decide whether the longest name wraps */
  .mrow__new { margin-left: 5px; letter-spacing: .05em; padding: 1px 3px; }
  /* "Single"/"Double" are wider than the price column they head; at this
     size they sit inside it instead of running into each other */
  .msub__s span { font-size: 9.5px; letter-spacing: .03em; }
  .msizes span { font-size: 10px; letter-spacing: .04em; }
  /* the flavour runs are the longest strings on the card; every point here
     is another name that stays on the line */
  .mflav__l, .mline { font-size: 12.5px; }
  .mflav__l { word-spacing: 9px; }
  .mflav__l span:not(:first-child)::before { right: calc(100% + 4px); }
  .mrow__p { font-size: 16px; }

  .mgroup { padding: 18px 12px 20px; }
  .msizes, .msub, .mrow, .mflav { grid-template-columns: 1fr 44px 44px; }
  .mgroup--tri .msizes, .mgroup--tri .msub, .mgroup--tri .mrow { grid-template-columns: 1fr 44px 44px 44px; }
  .mrow--mod .mrow__n, .mrow--mod .mrow__p, .maddon { font-size: 14.5px; }
  .mgroup--one .msizes, .mgroup--one .msub, .mgroup--one .mrow, .mgroup--one .mflav { grid-template-columns: 1fr 44px; }

  .deliver__row { grid-template-columns: 1fr; }
  .deliver__media { aspect-ratio: 16 / 9; }
  .deliver__txt { padding: 28px 18px 32px; border-left: 1px solid var(--line); border-top: 0; }
  .deliver__h { max-width: none; }
  .deliver__p { max-width: none; }
  /* the photograph sat 16px under the green band above it, so its own matcha
     hairline read as part of that band rather than as a frame — give it real
     pink above and draw the frame heavy enough to see */
  .deliver { padding-top: 34px; }
  .deliver__media { border-width: 2px; }
  .deliver__bar { height: 68px; margin-inline: calc(var(--gut) * -1); outline-offset: -8px; }

  /* the two bands butt together and the 48px mark straddles the join, 24px into
     the delivery band above and 24px into the green below — which is why the
     delivery band now carries 30px of padding under its button */
  .rooms { padding: 36px 0 56px; margin-top: 0; }
  .rooms__mark { margin: 0 auto 20px; width: 46px; height: 46px; }
  .rooms__head { margin-bottom: 22px; }
  .rooms__h { max-width: none; }
  /* one card at a time with the next one showing, the same swipe language the
     menu rails use */
  .rooms__track { gap: 12px; }
  .rcard { background: var(--surface); color: var(--ink); flex: 0 0 78%; }
  .rcard__n { color: var(--ink); margin: 14px 15px 0; font-size: 23px; }
  .rcard__d { color: var(--ink-muted); margin: 6px 15px 16px; font-size: 15.5px; }
  .rooms__cta { margin-top: 26px; padding: 14px 26px; }

  .visit { padding: 56px 0; }
  /* one column: address, then the button at full width, then the number */
  /* the owner wanted this heading to carry like "Our Menu" — bigger, centred,
     with a rule running out to each side */
  .kick { justify-content: center; text-align: center; font-size: 34px; gap: 12px; }
  .kick::before { content: ""; flex: 1; height: 1px; background: var(--line); }
  .visit__txt { text-align: center; }
  .visit__card { grid-template-columns: minmax(0, 1fr); gap: 14px 0; padding: 16px 16px 18px; justify-items: center; }
  .status__line { justify-content: center; }
  /* the pair is centred, but both tracks are fixed, so every day starts on one
     vertical line and every time range starts on another */
  .status__hours .hours__row {
    display: grid;
    grid-template-columns: 46px 176px;
    justify-content: center;
    gap: 0 12px;
    align-items: baseline;
  }
  .status__hours .hours__row b { width: auto; text-align: left; }
  .status__hours .hours__row span { text-align: left; }
  .visit__street { grid-column: 1; grid-row: 1; }
  .visit__city { grid-column: 1; grid-row: 2; }
  .visit__dir { grid-column: 1; grid-row: 3; justify-self: stretch; justify-content: center; }
  .visit__tel { grid-column: 1; grid-row: 4; justify-self: stretch; text-align: center; }
  .gram__head { display: flex; margin-bottom: 28px; }
  .gram__vp { margin-inline: calc(var(--gut) * -1); }
  /* as wide as a menu card, with the same sliver of the next one showing */
  .gram__rail { gap: 22px; padding-inline: 8vw; }
  .clip { flex: 0 0 84vw; }

  .grandfoot { padding: 52px 0 36px; }
  .gf__grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 26px 6px;
    justify-items: center;
    text-align: center;
  }
  .gf__mark { grid-column: 1 / -1; align-items: center; }
  .gf__h { font-size: 11px; letter-spacing: .07em; margin-bottom: 10px; }
  .gf__col ul { gap: 7px; }
  .gf__col a { font-size: 14.5px; }
  .gf__base { margin-top: 28px; flex-direction: column; align-items: center; text-align: center; gap: 8px; }
  .gf__addr { justify-content: center; flex-wrap: wrap; }
}

/* the studio credit, the last line on the page */
.gf__credit {
  margin-top: 22px;
  padding-top: 18px;
  border-top: 1px solid var(--line);
  text-align: center;
}
.gf__credit a {
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: var(--track-accent);
  text-transform: uppercase;
  color: var(--ink-muted);
  transition: color .2s var(--ease-out);
}
.gf__credit a b { color: var(--primary); font-weight: 700; }
.gf__credit a:hover b { color: var(--accent); }



/* ========================================================== BACK TO TOP ===== */
/* A small green disc in the corner with the site's own offset keyline round it.
   Created hidden in the markup; the script un-hides it and then this class is
   the only thing that moves it in and out. */
.totop {
  position: fixed;
  right: 18px;
  bottom: 18px;
  z-index: 90;
  width: 50px;
  height: 50px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: var(--primary);
  color: var(--primary-ink);
  cursor: pointer;
  box-shadow: 0 0 0 2px var(--bg), 0 0 0 3px var(--primary), 0 6px 18px rgba(34, 31, 23, .18);
  opacity: 0;
  transform: translateY(14px) scale(.9);
  pointer-events: none;
  transition: opacity .32s var(--ease-out), transform .32s var(--ease-out),
              background .2s var(--ease-out), color .2s var(--ease-out);
}
.totop.is-in { opacity: 1; transform: none; pointer-events: auto; }
.totop:hover { background: var(--accent); color: var(--accent-ink); }
.totop:hover { box-shadow: 0 0 0 2px var(--bg), 0 0 0 3px var(--accent), 0 6px 18px rgba(34, 31, 23, .22); }
.totop svg { width: 22px; height: 22px; }
/* out of the way of the lightbox */
.lb-open .totop { opacity: 0; pointer-events: none; }
@media (max-width: 767px) {
  .totop { right: 14px; bottom: 14px; width: 46px; height: 46px; }
  .totop svg { width: 20px; height: 20px; }
}

/* the swipe indicator under a menu rail — how many cards, and which one you
   are on. Built by script, so it only appears where the rail really scrolls. */
.rail__dots {
  display: flex;
  justify-content: center;
  gap: 8px;
  margin-top: 16px;
  padding: 0;
  /* an <ol> for the semantics, but the markers must not render */
  list-style: none;
}
.rail__dots li { list-style: none; }
.rail__dots button {
  display: block;
  width: 8px;
  height: 8px;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: color-mix(in srgb, var(--primary-ink) 40%, transparent);
  cursor: pointer;
  transition: width .28s var(--ease-out), background .28s var(--ease-out);
}
.rail__dots button.is-on {
  width: 22px;
  background: var(--primary-ink);
}

/* ============================================================ TAP HINT ===== */
/* A hand that appears over one grid tile a few seconds in, to say the pictures
   open. Purely additive: it is inserted by script, sits above the tile with
   pointer-events off so it never eats the tap it is advertising, and it leaves
   for good on the first click. */
.tapme {
  position: absolute;
  left: 50%;
  top: 50%;
  z-index: 5;
  width: 62px;
  height: 62px;
  /* left/top put its corner at the tile's centre; this brings its own centre
     there instead. The ring and hand animate their own transforms inside. */
  transform: translate(-50%, -50%);
  pointer-events: none;
  opacity: 0;
  transition: opacity .45s var(--ease-out);
}
.tapme.is-in { opacity: 1; }
.tapme__ring {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 3px solid var(--primary);
  background: color-mix(in srgb, var(--bg) 55%, transparent);
}
.tapme__hand {
  position: absolute;
  inset: 0;
  margin: auto;
  width: 38px;
  height: 38px;
  color: var(--primary);
  filter: drop-shadow(0 0 2px rgba(255, 255, 255, .95))
          drop-shadow(0 1px 3px rgba(255, 255, 255, .8));
}
@media (prefers-reduced-motion: no-preference) {
  .tapme.is-in .tapme__ring { animation: tapRing 1.9s var(--ease-out) infinite; }
  .tapme.is-in .tapme__hand { animation: tapHand 1.9s var(--ease-out) infinite; }
}
@keyframes tapRing {
  0%   { transform: scale(.62); opacity: .9; }
  55%  { transform: scale(1);   opacity: .28; }
  100% { transform: scale(1.18); opacity: 0; }
}
@keyframes tapHand {
  0%, 100% { transform: translateY(0) scale(1); }
  46%      { transform: translateY(3px) scale(.9); }
}

/* =========================================================== LIGHTBOX ===== */
/* Created hidden and only ever shown from the script — with JS off the markup
   stays `hidden`, so nothing is being visually hidden that a reader would
   otherwise see. */
/* ======================================================== LATE HOURS ===
   A one-a-visit card announcing the longer hours. Three snapshots of the room
   at its busiest are fanned across the top and break the card's edge, so the
   thing reads as photographs dropped on the table rather than a web dialog.
   The deep green ground is the point: the news is about staying open after
   dark, and every other panel on the site is paper-white.              === */
.note {
  position: fixed;
  inset: 0;
  z-index: 210;
  display: grid;
  place-items: center;
  padding: clamp(14px, 5vw, 48px);
  background: rgba(34, 31, 23, .86);
}
.note[hidden] { display: none; }
body.note-open { overflow: hidden; }
.note-open .totop { opacity: 0; pointer-events: none; }
.note__card:focus { outline: none; }
.note__card {
  position: relative;
  width: min(430px, 100%);
  /* a grid item's min-width is auto, so without this the card grows to fit the
     fan of pictures rather than the fan being asked to fit the card */
  min-width: 0;
  /* room for the pictures hanging above it, matched below so the grid centres
     the whole composition and not just the panel */
  margin: 74px 0;
  padding: 0 clamp(20px, 6vw, 26px) 30px;
  background: var(--primary);
  color: var(--primary-ink);
  text-align: center;
  box-shadow: 0 26px 60px rgba(20, 18, 12, .5);
}
.note__shots {
  display: flex;
  justify-content: center;
  align-items: flex-end;
  margin: -74px 0 20px;
}
.note__shot {
  position: relative;
  display: block;
  /* sized off the card, not in pixels: the fan has to fit inside whatever
     width the panel ends up with, rotation bleed included */
  width: 31%;
  aspect-ratio: 1;
  flex: none;
  padding: 5px;
  background: var(--bg);
  box-shadow: 0 10px 24px rgba(20, 18, 12, .45);
}
.note__shot img { display: block; width: 100%; height: 100%; object-fit: cover; }
/* the fan: the middle print sits largest and on top, the outer two tucked
   under it and tipped the other way */
.note__shot:nth-child(1) { transform: rotate(-7deg) translateY(9px); margin-right: -4.5%; z-index: 1; }
.note__shot:nth-child(2) { width: 35%; transform: rotate(1.5deg) translateY(-7px); z-index: 3; }
.note__shot:nth-child(3) { transform: rotate(7deg) translateY(9px); margin-left: -4.5%; z-index: 2; }
.note__k {
  font-family: var(--font-body);
  font-size: 12.5px; font-weight: 700;
  letter-spacing: var(--track-accent); text-transform: uppercase;
  color: var(--bg);
  background: var(--accent);
  display: inline-block;
  padding: 5px 11px;
}
.note__h {
  margin: 12px 0 0;
  font-size: clamp(27px, 7.6vw, 33px);
  line-height: 1.04;
  text-wrap: balance;
}
.note__p {
  margin: 11px 0 0;
  font-family: var(--font-body);
  font-size: 15px; line-height: 1.5;
  color: var(--primary-ink-dim);
  text-wrap: pretty;
}
/* A red disc straddling the panel's top corner — attached to the card rather
   than parked in the screen corner, and set outside it so it clears the
   photographs hanging over the top edge. */
.note__x {
  position: absolute;
  top: -14px; right: -14px;
  z-index: 4;
  width: 36px; height: 36px;
  display: grid; place-items: center;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--accent);
  color: var(--accent-ink);
  cursor: pointer;
  box-shadow: 0 4px 14px rgba(20, 18, 12, .45);
}
.note__x svg {
  width: 15px; height: 15px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.4;
  stroke-linecap: round;
}
.note__x:hover { background: var(--bg); color: var(--accent); }
@media (prefers-reduced-motion: no-preference) {
  .note { opacity: 0; transition: opacity .34s var(--ease-out); }
  .note__card { transform: translateY(14px); transition: transform .42s var(--ease-out); }
  .note.is-in { opacity: 1; }
  .note.is-in .note__card { transform: none; }
}
.lb {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: grid;
  place-items: center;
  padding: clamp(16px, 4vw, 56px);
  background: rgba(34, 31, 23, .82);
}
.lb[hidden] { display: none; }
body.lb-open { overflow: hidden; }
.lb__fig {
  margin: 0;
  max-width: min(1100px, 100%);
  max-height: 100%;
  display: flex;
  flex-direction: column;
  background: var(--surface);
}
.lb__img {
  display: block;
  width: 100%;
  max-height: calc(100vh - 190px);
  object-fit: contain;
  background: var(--ink);
}
/* the thin green caption bar across the foot of the picture */
.lb__cap {
  background: var(--primary);
  color: var(--primary-ink);
  padding: 12px 18px;
  font-family: var(--font-body);
  font-size: var(--t-lbl);
  font-weight: 700;
  letter-spacing: var(--track-accent);
  text-transform: uppercase;
}
.lb__close {
  position: absolute;
  top: clamp(10px, 2vw, 22px);
  right: clamp(10px, 2vw, 22px);
  width: 46px;
  height: 46px;
  font-size: 30px;
  line-height: 1;
  color: var(--ink-invert);
  background: rgba(34, 31, 23, .5);
  border-radius: 999px;
}
.lb__close:hover { background: var(--accent); color: var(--accent-ink); }

/* =========================================================== PAGEHEAD ===== */
/* Sub-pages open with a slim band, not a viewport-high hero — the home page
   already spends a full screen on one photograph, and these exist to be read. */
.pagehead { background: var(--bg); padding: 56px 0 34px; border-bottom: 1px solid var(--line); }
.pagehead__in { text-align: center; }
.pagehead__k {
  font-family: var(--font-body); font-size: var(--t-lbl); font-weight: 700;
  letter-spacing: var(--track-accent); text-transform: uppercase;
  color: var(--primary); margin-bottom: 10px;
}
.pagehead__h { font-size: clamp(40px, 5.4vw, 76px); }
.pagehead__p { margin: 14px auto 0; max-width: 56ch; color: var(--ink-muted); }
/* Current page. Colour only — no fill, so the cell bar keeps its even rhythm.
   Gated above 768px: a phone shows one nav link (Menu) plus Order Delivery, and
   recolouring the button the owner points people at would only confuse. */
@media (min-width: 768px) {
  .cell--on { color: var(--accent); font-weight: 700; }
  .cell--on:hover { color: var(--primary-ink); }
}

/* ============================================================= ABHERO ===== */
/* The about page opens on the room itself. Shorter than the home hero — this
   page is here to be read, not to sell a photograph. */
.abhero { position: relative; height: clamp(340px, 52vh, 460px); overflow: hidden; background: var(--ink); }
.abhero__img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.abhero__scrim {
  position: absolute; inset: 0;
  background: linear-gradient(to top, rgba(42, 30, 27, .84) 0%, rgba(42, 30, 27, .48) 52%, rgba(42, 30, 27, .16) 100%);
}
.abhero__txt { position: relative; z-index: 2; height: 100%; display: flex; flex-direction: column; justify-content: flex-end; padding-bottom: 34px; }
.abhero__k {
  font-family: var(--font-body); font-size: var(--t-lbl); font-weight: 700;
  letter-spacing: var(--track-accent); text-transform: uppercase;
  color: var(--ink-invert); margin-bottom: 12px;
}
.abhero__h { font-size: clamp(44px, 6vw, 86px); line-height: .96; color: var(--ink-invert); }
.abhero__p { margin-top: 14px; max-width: 46ch; color: var(--ink-invert); }

/* =============================================================== SEAT ===== */
/* One tall photograph with a second dropped over its lower corner — two views
   of the same room, rather than a row of equal tiles. */
.seat { background: var(--bg); padding: 84px 0; }
.seat__row { display: grid; grid-template-columns: 1.05fr .95fr; gap: 56px; align-items: center; }
.seat__stack { position: relative; padding-bottom: 68px; padding-right: 56px; }
.seat__tall { display: block; position: relative; aspect-ratio: 4 / 5; overflow: hidden; border: 1px solid var(--line); }
.seat__tall img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.seat__inset {
  display: block; position: absolute; right: 0; bottom: 0;
  width: 56%; aspect-ratio: 1 / 1; overflow: hidden;
  border: 8px solid var(--bg);
  box-shadow: 0 0 0 1px var(--line);
}
.seat__inset img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.seat__k { color: var(--primary); margin-bottom: 12px; }
.seat__h { font-size: var(--t-big); max-width: 15ch; }
.seat__p { margin-top: 16px; max-width: 44ch; color: var(--ink-muted); }
.seat__list { margin-top: 24px; border-top: 1px solid var(--line); max-width: 44ch; }
.seat__list li {
  padding: 12px 0; border-bottom: 1px solid var(--line);
  font-family: var(--font-body); font-size: var(--t-lbl); font-weight: 700;
  letter-spacing: var(--track-accent); text-transform: uppercase; color: var(--ink);
}

/* ============================================================== TREND ===== */
/* Three squares, each stepped a little lower than the one before, so the row
   reads as something laid out by hand. */
.trend { background: var(--primary); color: var(--primary-ink); padding: 84px 0 96px; }
.trend__head { text-align: center; margin-bottom: 40px; }
.trend__k { color: var(--primary-ink-dim); margin-bottom: 12px; }
.trend__h { font-size: var(--t-big); max-width: 20ch; margin-inline: auto; }
.trend__p { margin: 16px auto 0; max-width: 52ch; color: var(--primary-ink-dim); }
.trend__row { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 22px; align-items: start; }
.shot { min-width: 0; }
.shot:nth-child(2) { margin-top: 44px; }
.shot:nth-child(3) { margin-top: 88px; }
.shot__media {
  display: block; position: relative; aspect-ratio: 1 / 1; overflow: hidden;
  border: 6px solid var(--bg);
  box-shadow: 0 0 0 1px var(--primary-ink-dim);
}
.shot__media img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; transition: transform .6s var(--ease-out); }
.shot:hover .shot__media img { transform: scale(1.05); }
.shot__cap {
  display: block; margin-top: 12px;
  font-family: var(--font-body); font-size: var(--t-lbl); font-weight: 700;
  letter-spacing: var(--track-accent); text-transform: uppercase; color: var(--primary-ink-dim);
}

/* ============================================================== KCAFE ===== */
/* A full-bleed photograph with the copy on a cream plate pulled up over it. */
.kcafe { background: var(--bg); padding-bottom: 84px; }
.kcafe__media { position: relative; aspect-ratio: 21 / 9; overflow: hidden; background: var(--ink); }
.kcafe__media img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.kcafe__plate {
  position: relative;
  margin: -96px 0 0 auto;
  width: min(620px, 100%);
  background: var(--surface);
  border: 1px solid var(--line);
  padding: 40px 44px 44px;
}
.kcafe__k { color: var(--primary); margin-bottom: 12px; }
.kcafe__h { font-size: var(--t-big); max-width: 18ch; }
.kcafe__p { margin-top: 16px; color: var(--ink-muted); }

/* ============================================================== STRIP ===== */
/* Squares you pull sideways. The rail is an ordinary scroller underneath, so
   touch, wheel and keyboard all still work if the pointer script never runs. */
.strip { background: var(--bg); padding: 0 0 88px; }
.strip__head { padding-bottom: 26px; }
.strip__k { color: var(--primary); margin-bottom: 10px; }
.strip__h { font-size: var(--t-big); }
.strip__rail {
  display: flex;
  gap: 14px;
  /* both axes allowed: the rail takes a sideways drag, the page takes a
     vertical one. `pan-x` alone would forbid vertical panning outright. */
  touch-action: pan-x pan-y pinch-zoom;
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: var(--gut);
  padding-inline: var(--gut);
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  cursor: grab;
}
.strip__rail::-webkit-scrollbar { display: none; }
.strip__rail.is-drag { cursor: grabbing; scroll-snap-type: none; user-select: none; }
.sq {
  position: relative;
  flex: 0 0 clamp(200px, 24vw, 300px);
  aspect-ratio: 1 / 1;
  overflow: hidden;
  scroll-snap-align: start;
  min-width: 0;
  background: var(--surface-alt);
}
.sq img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; pointer-events: none; }

/* ============================================================== FACTS ===== */
.facts { background: var(--primary); color: var(--primary-ink); padding: 46px 0; }
.facts__row { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 24px; text-align: center; }
.fact__n {
  display: block; font-family: var(--font-display);
  font-size: clamp(38px, 4.4vw, 62px); line-height: 1; letter-spacing: var(--track-display);
  color: var(--primary-ink);
}
.fact__t {
  display: block; margin-top: 10px;
  font-family: var(--font-body); font-size: var(--t-lbl); font-weight: 700;
  letter-spacing: var(--track-accent); text-transform: uppercase;
  color: var(--primary-ink-dim);
}

/* ============================================================= MOSAIC ===== */
/* The gallery runs as bands of five: one photograph at double size beside a
   block of four, with the big one changing sides band to band. Each band is its
   own grid, so the air between bands is real space rather than a wider gap in
   one long grid — and each band is one reveal group, so the five wipe in
   together the way a row of the home grid does. */
.mosaic { background: var(--bg); padding: 44px 0 88px; }
.band {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 12px;
}
.band + .band { margin-top: 56px; }
.mo { position: relative; aspect-ratio: 1 / 1; overflow: hidden; cursor: zoom-in; }
/* the big tile takes the left pair of columns, or the right pair on a flipped
   band; the four squares fill whatever is left, in order */
.mo--big { grid-column: 1 / span 2; grid-row: 1 / span 2; }
.band--flip .mo--big { grid-column: 3 / span 2; }
.mo__media {
  position: absolute; inset: 0; overflow: hidden;
  background: var(--surface-alt);
  border: 2px solid var(--primary);
}
.mo__media img {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  transition: transform .6s var(--ease-out);
}
.mo:hover .mo__media img { transform: scale(1.05); }
.mo__cap {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 2;
  padding: 12px 14px;
  background: linear-gradient(to top, rgba(34, 31, 23, .84), rgba(34, 31, 23, 0));
  color: var(--ink-invert);
  font-family: var(--font-body); font-size: var(--t-lbl); font-weight: 700;
  letter-spacing: var(--track-accent); text-transform: uppercase;
  opacity: 0; transform: translateY(8px);
  transition: opacity .35s var(--ease-out), transform .35s var(--ease-out);
}
.mo:hover .mo__cap, .mo:focus-visible .mo__cap { opacity: 1; transform: none; }

@media (max-width: 1023px) {
  .story__row { grid-template-columns: 1fr; gap: 26px; }
  .story__h, .story__p { max-width: none; }
}
@media (max-width: 767px) {
  .pagehead { padding: 36px 0 26px; }
  .pagehead__p { max-width: none; }
  .abhero { height: clamp(300px, 46vh, 400px); }
  .abhero__txt { padding-bottom: 26px; }
  .abhero__p { max-width: none; }
  .seat { padding: 52px 0; }
  .seat__row { grid-template-columns: 1fr; gap: 30px; }
  .seat__stack { padding-bottom: 46px; padding-right: 34px; }
  .seat__inset { width: 52%; border-width: 6px; }
  .seat__h, .seat__p, .seat__list { max-width: none; }
  .trend { padding: 52px 0 60px; }
  .trend__head { margin-bottom: 26px; }
  .trend__h, .trend__p { max-width: none; }
  /* the stagger would waste half a phone screen — square up and let it scroll */
  .trend__row { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
  .shot:nth-child(2), .shot:nth-child(3) { margin-top: 0; }
  .shot:nth-child(3) { grid-column: 1 / -1; }
  .shot__media { border-width: 5px; }
  .kcafe { padding-bottom: 52px; }
  .kcafe__media { aspect-ratio: 4 / 3; }
  .kcafe__plate { margin-top: -54px; padding: 26px 20px 28px; }
  .kcafe__h { max-width: none; }
  .strip { padding-bottom: 56px; }
  .strip__head { padding-bottom: 18px; }
  .strip__rail { gap: 10px; }
  .sq { flex: 0 0 62vw; }
  .facts { padding: 30px 0; }
  .facts__row { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
  /* "7 days" was breaking in two at the clamp's phone end */
  .fact__n { font-size: 29px; white-space: nowrap; }
  .fact__t { font-size: 10px; letter-spacing: .06em; }
  /* two across on a phone, big tiles still span two — the mosaic keeps its
     rhythm instead of collapsing to a single column */
  /* two across: the big tile takes the full width of the band, the four squares
     sit under it in a two-by-two */
  .mosaic { padding: 30px 0 56px; }
  .band { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
  .band + .band { margin-top: 34px; }
  .mo--big, .band--flip .mo--big { grid-column: 1 / -1; grid-row: auto; }
  .mo__cap { display: none; }
}

/* ==================================================== DESKTOP PASS ======
   Everything below adapts the wide layout only. The phone layout above is
   settled and none of these rules reach it.                          === */
@media (min-width: 768px) {
  /* the wordmark ran the full margin at 118px and swamped the plate */
  :root { --t-word: clamp(44px, 5.4vw, 78px); }

  /* the seal holds the left, the four link columns pack to the right */
  .gf__grid { grid-template-columns: 1fr repeat(4, max-content); gap: 36px 54px; }
  /* the social column is a list like the others, so it reads left off the
     same edge — centred icons broke the column's left rag */
  .gf__col .gf__list--ico { flex-direction: column; align-items: flex-start; gap: 14px; }

  /* four across, three down */
  .index__grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }

  /* three cards do not fill the rail; without this they sit left against a
     strip of empty green */
  .rooms__track { justify-content: center; }

  /* The delivery band closes inside one green rule, with the pink ground
     showing above and below it so it separates from the sections it sits
     between instead of butting onto them. */
  .deliver { padding: 78px 0; }
  .deliver__frame { border: 2px solid var(--primary); }
  .deliver__media, .deliver__txt { border: 0; }
  .deliver__txt { border-left: 2px solid var(--primary); }
  .deliver__bar { margin-top: 0; border-top: 2px solid var(--primary); }
}

/* --------------------------------------------------- HERO TRIPTYCH -----
   The hero clip is a vertical phone video: full-bleed on a phone, badly
   cropped across a desktop. Wide screens get three stills instead, divided
   by the house green, each arriving from a different edge.

   The frame wipes one way while the picture inside drifts the other and the
   two settle together — a camera revealing the room, rather than three
   panels sliding on. Every hidden start-state is scoped to html.js-reveal,
   so with JS off the three simply stand there.                       --- */
.hero__trio { display: none; }
@media (min-width: 768px) {
  .hero > .hero__img { display: none; }
  .hero__trio {
    position: absolute;
    inset: 0;
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 7px;
    background: var(--primary);
  }
  .hero__pane { position: relative; overflow: hidden; }
  .hero__pane img {
    position: absolute; inset: 0;
    width: 100%; height: 100%;
    object-fit: cover;
  }

  :where(html.js-reveal) .hero__pane:nth-child(1) { clip-path: inset(0 100% 0 0); }
  :where(html.js-reveal) .hero__pane:nth-child(2) { clip-path: inset(100% 0 0 0); }
  :where(html.js-reveal) .hero__pane:nth-child(3) { clip-path: inset(0 0 0 100%); }
  :where(html.js-reveal) .hero__pane:nth-child(1) img { transform: translateX(-9%) scale(1.16); }
  :where(html.js-reveal) .hero__pane:nth-child(2) img { transform: translateY(9%) scale(1.16); }
  :where(html.js-reveal) .hero__pane:nth-child(3) img { transform: translateX(9%) scale(1.16); }

  :where(html.js-reveal) .hero.in .hero__pane {
    clip-path: inset(0 0 0 0);
    transition: clip-path .86s var(--ease-wipe-hard);
  }
  :where(html.js-reveal) .hero.in .hero__pane img {
    transform: none;
    transition: transform 1.5s var(--ease-out);
  }
  :where(html.js-reveal) .hero.in .hero__pane:nth-child(2),
  :where(html.js-reveal) .hero.in .hero__pane:nth-child(2) img { transition-delay: .2s; }
  :where(html.js-reveal) .hero.in .hero__pane:nth-child(3),
  :where(html.js-reveal) .hero.in .hero__pane:nth-child(3) img { transition-delay: .4s; }
}
@media (prefers-reduced-motion: reduce) {
  .hero__pane, .hero__pane img { clip-path: none !important; transform: none !important; }
}

/* ------------------------------------------------- BOARD CAROUSEL -----
   The wide board used to stack every plate down two long columns, which is
   what made this section four screens tall. Now each column shows ONE plate
   and arrows step through the rest.

   Every plate is the same height — the tallest one on the whole board, so
   the two columns agree — and no type moves to get there: the box is a flex
   column and its photograph takes whatever height the rows leave, exactly
   the way it works on a phone.                                      --- */
@media (min-width: 768px) {
  /* the wide gap is a wide-screen luxury; at 768 the two columns need it back */
  .rails { gap: clamp(26px, 5.2vw, 76px); }
  /* Below about 1000px two columns leave each plate ~380px, and the longer
     item names start wrapping. The two rails stack instead, each taking the
     full width, and the names hold their line. */
  @media (max-width: 1023px) {
    .rails { grid-auto-flow: row; gap: 40px; }
  }
  .rail__vp { height: var(--boardH, auto); overflow: hidden; }
  .rail__track {
    flex-direction: row;
    align-items: stretch;
    gap: 0;
    height: 100%;
    transform: translateX(calc(var(--i, 0) * -100%));
    transition: transform .52s var(--ease-out);
  }
  .rail__track > .mgroup { flex: 0 0 100%; min-width: 0; }
  .mgroup { display: flex; flex-direction: column; }

  /* the photograph absorbs the leftover height */
  .mgroup__img { flex: 1 1 auto; aspect-ratio: auto; min-height: 130px; margin-top: 18px; }
  .mgroup--grow .mgroup__img { flex: 1 1 auto; min-height: 130px; max-height: none; }
  .mgroup__img--corner { flex: none; }
  .mlist { flex: none; }

  /* the overlays that the phone shows are the same here — a wide screen is no
     reason to break one composition into two loose frames */
  .mimgs { display: block; position: relative; margin: 18px 0 0; flex: 1 1 auto; min-height: 130px; }
  .mgroup .mimgs .mgroup__img { margin: 0; flex: none; min-height: 0; position: absolute; }
  .mimgs--pair { display: flex; gap: 12px; }
  .mgroup .mimgs--pair .mgroup__img { position: relative; flex: 1 1 0; min-width: 0; aspect-ratio: auto; }
  .mimgs--duo { align-self: center; width: auto; aspect-ratio: .96; max-width: 100%; }
  .mgroup .mimgs--duo .mgroup__img { aspect-ratio: 1; }
  .mgroup .mimgs--duo .mgroup__img:not(.mgroup__img--b) { left: 0; top: 0; height: 78%; }
  .mgroup .mimgs--duo .mgroup__img--b { right: 0; bottom: 0; height: 52%; z-index: 2; }
  .mimgs--tuck { align-self: stretch; width: auto; aspect-ratio: auto; max-width: none; }
  .mgroup .mimgs--tuck .mgroup__img:not(.mgroup__img--b) { left: 0; top: 0; width: 80%; height: 90%; aspect-ratio: auto; }
  .mgroup .mimgs--tuck .mgroup__img--b { right: 0; bottom: 0; height: 62%; aspect-ratio: 1; z-index: 2; }

  /* the stepper. Same disc as the back-to-top control, colours swapped: a
     green button would disappear into the green slab it sits on. */
  .rail__nav { display: flex; align-items: center; justify-content: center; gap: 18px; margin-top: 24px; }
  .rnav {
    width: 46px; height: 46px;
    display: grid; place-items: center;
    border: 0; border-radius: 50%;
    background: var(--bg);
    color: var(--primary);
    cursor: pointer;
    box-shadow: 0 0 0 2px var(--primary), 0 0 0 4px var(--bg), 0 6px 18px rgba(34, 31, 23, .2);
    transition: background .2s var(--ease-out), color .2s var(--ease-out), box-shadow .2s var(--ease-out);
  }
  .rnav svg { width: 21px; height: 21px; }
  .rnav:hover { background: var(--accent); color: var(--accent-ink); }
  .rnav:hover { box-shadow: 0 0 0 2px var(--accent), 0 0 0 4px var(--bg), 0 6px 18px rgba(34, 31, 23, .24); }
  .rail__count {
    font-family: var(--font-body);
    font-size: 14px; font-weight: 700;
    letter-spacing: var(--track-accent); text-transform: uppercase;
    color: var(--primary-ink-dim);
    min-width: 7ch; text-align: center;
  }
  .rail__count b { color: var(--primary-ink); }
}
@media (prefers-reduced-motion: reduce) {
  .rail__track { transition: none; }
}

/* the board's own furniture, trimmed so the plate gets the height instead.
   The jump links go: they name the two columns that are standing right below
   them, which is a duplicate control, not a shortcut. */
@media (min-width: 768px) {
  .board { padding: 54px 0 46px; }
  .board__head { margin-bottom: 20px; }
  .board__h { font-size: clamp(40px, 4.4vw, 58px); }
  .board__note { margin-top: 8px; }
  .board__jump { display: none; }
  .board__foot { margin-top: 18px; }
  .rail__head { padding-bottom: 8px; margin-bottom: 14px; }
  .rail__h { font-size: clamp(24px, 2.2vw, 30px); }
}

/* measuring pose only: the plate with its photograph given up, which is the
   shortest it may ever be without cutting into the menu itself */
.rails--rows .mgroup__img,
.rails--rows .mimgs { flex: 0 0 0 !important; height: 0 !important; min-height: 0 !important; overflow: hidden; }

/* ----------------------------------------------------- VISIT (WIDE) ----- */
@media (min-width: 768px) {
  .visit { padding: 68px 0; }

  /* The rail centres its first clip. That is right on a phone, where one clip
     fills the screen, and wrong here: it opened a 240px hole to the left of
     the videos. */
  .gram__rail { padding-inline: 0; }

  /* The address plate reads straight down the column — street, then city,
     then the button across the whole width, because directions are what this
     block is for. */
  .visit__card { grid-template-columns: minmax(0, 1fr); gap: 8px; padding: 22px 24px 20px; }
  .visit__street { grid-column: 1; grid-row: auto; }
  .visit__city { grid-column: 1; grid-row: auto; margin-bottom: 6px; }
  .visit__dir {
    grid-column: 1; grid-row: auto;
    justify-self: stretch;
    justify-content: center;
    padding: 15px 18px;
  }

  /* the ledger ran hard against the panel's own rules */
  .status__hours { margin-inline: 26px; }
  .status__hours .hours__row { padding-inline: 13px; }
  .status__hours .hours__row.is-today { padding-inline: 12px; }
}

/* ------------------------------------------------ ABOUT PAGE (WIDE) -----
   The three long bands each ran a screen and a half, and the height was not
   doing any work: a tall photograph on one side and a short paragraph on the
   other leaves a column of nothing. The pictures come back towards landscape,
   the stagger tightens, and the plate climbs further over the photograph it
   belongs to — same devices, less air.                                --- */
@media (min-width: 768px) {
  .seat { padding: clamp(34px, 6.6vh, 60px) 0; }
  .seat__row { gap: 48px; }
  .seat__stack { padding-bottom: 44px; padding-right: 48px; }
  .seat__tall { aspect-ratio: 4 / 3.4; }
  .seat__inset { width: 50%; }

  .trend { padding: 56px 0 60px; }
  .trend__head { margin-bottom: 30px; }
  .shot:nth-child(2) { margin-top: 26px; }
  .shot:nth-child(3) { margin-top: 52px; }

  .kcafe { padding-bottom: clamp(36px, 7vh, 64px); }
  .kcafe__media { aspect-ratio: 28 / 9; }
  .kcafe__plate { margin-top: -130px; padding: 34px 40px 38px; }

  .strip { padding-bottom: 64px; }
}

/* ------------------------------------------- SECTION HEIGHT (WIDE) -----
   The band paddings below are given in vh as well as px: a 1280x800 laptop
   has 100px less to spend than a 1440x900 one, and a fixed padding is the
   first thing that pushes a band past the limit on the shorter screen.
   No band may run past 95% of the window. Four across and three down of
   SQUARE cells is 1080px on a 1440 screen, so the wide grid gives up the
   square and takes its row height from the window instead — the crops are
   landscape, which suits a room and a bowl equally well.               --- */
@media (min-width: 768px) {
  .index__grid { grid-auto-rows: minmax(168px, calc((95vh - 232px) / 3)); }
  .index__grid .cellsq { aspect-ratio: auto; height: 100%; }

  .rooms { padding: clamp(24px, 4.4vh, 40px) 0 clamp(36px, 6.6vh, 60px); }
  .visit { padding: clamp(26px, 5.5vh, 50px) 0; }
  .trend { padding: clamp(32px, 6.2vh, 56px) 0 clamp(30px, 5.8vh, 52px); }

  /* the board's furniture again: every pixel here is one the plate needs */
  .board { padding: 40px 0 34px; }
  .board__h { font-size: clamp(38px, 3.6vw, 48px); }
  .board__head { margin-bottom: 16px; }
  .board__foot { margin-top: 14px; font-size: 14px; }
  .rail__head { padding-bottom: 6px; margin-bottom: 12px; }
  .rail__h { font-size: clamp(22px, 2vw, 27px); }
  .rail__cats { font-size: 14px; }
  .rail__nav { margin-top: 16px; }
}

/* ================================================ DESKTOP PASS TWO ======
   Bringing the marquee up into the first screen, and finishing the wide
   layout's photographs.                                              === */
@media (min-width: 768px) {
  /* --- the fold. Every value here is paying for the same thing: half the
     marquee visible without scrolling. --- */
  :root { --t-word: clamp(44px, 4.3vw, 62px); --t-display: clamp(44px, 5.4vw, 76px); }
  .mast__plate { padding-top: 20px; padding-bottom: 14px; }
  .cell { height: 46px; }
  .hero { height: clamp(420px, 66vh, 700px); }

  /* THE sits over BY, not over the middle of the line: the wordmark's centre
     falls between the two words, and a centred label there reads as belonging
     to neither. */
  /* padding, not a transform: a transform pushes the box's right edge past
     the page and opens a horizontal scrollbar. Centred text inside a padded
     box moves by half the padding and stays inside its own width. */
  .mast__the { padding-left: 13.2%; }

  /* the order button is the one solid cell on the bar */
  .cell--solid { background: var(--primary); color: var(--primary-ink); }
  .cell--solid:hover { background: var(--accent); color: var(--accent-ink); }

  /* --- the plates' photographs ---
     The overlay pair kept both pictures square, which on a wide plate wastes
     the width. The lead goes landscape and takes the room, the way the bakery
     plate does; the second stays square, tucked into the corner. */
  .mimgs--duo { align-self: stretch; width: auto; aspect-ratio: auto; max-width: none; }
  .mgroup .mimgs--duo .mgroup__img:not(.mgroup__img--b) {
    left: 0; top: 0; width: 78%; height: 88%; aspect-ratio: auto;
  }
  .mgroup .mimgs--duo .mgroup__img--b { right: 0; bottom: 0; height: 58%; aspect-ratio: 1; z-index: 2; }

  /* the ice cream plate has room under its rows now, so its photograph comes
     down off the heading and takes the foot of the card like every other */
  .mgroup--corner .mcorner {
    /* The corner layout's top/right offsets still apply once this is relative
       — they shifted the picture 20px down and 20px left, out of the card's
       padding. Clear them and take the same margins every other menu
       photograph has. */
    position: relative;
    inset: auto;
    margin: 18px 4px 4px;
    /* the markup puts it under the heading, where the corner layout needs it;
       as a flex item it has to go to the end of the column instead */
    order: 99;
    width: auto;
    aspect-ratio: auto;
    flex: 1 1 auto;
    min-height: 130px;
    /* side by side here rather than one dropped on the other: the foot of a
       wide card is a strip, and two prints across it read better */
    display: flex;
    gap: 10px;
  }
  .mgroup--corner .mcorner .mgroup__img--corner,
  .mgroup--corner .mcorner .mgroup__img--corner2 {
    /* relative, never static: the <img> inside is absolutely positioned and a
       static frame sends it up to the card, where it covers the whole plate */
    position: relative;
    inset: auto;
    flex: 1 1 0;
    min-width: 0;
    width: auto;
    aspect-ratio: auto;
  }
  /* a portrait cone in a landscape slot crops to the wafer; bias up to the
     swirl, which is the part worth showing */
  .mgroup--corner .mcorner .mgroup__img--corner img { object-position: 50% 44%; }
  .mgroup--corner .mgroup__h { max-width: none; }
  .mgroup--corner .mlist > .msub:first-child,
  .mgroup--corner .mlist > .mline:nth-child(2) { padding-right: 0; }
  .mgroup--corner .mlist { margin-top: 0; }

  /* the three cards read as captions under their pictures, centred with them */
  .rcard__n, .rcard__d { text-align: center; }

  /* the plate was pulled up over its photograph but still left a white band
     before the strip below it */
  .kcafe { padding-bottom: clamp(20px, 3vh, 30px); }
}

/* the size label and the prices under it share one alignment, or the label
   floats off to the side of the column it is meant to head */
.mrow__p { text-align: center; }

/* ============================================== DESKTOP PASS THREE ===== */
@media (min-width: 768px) {
  /* the marquee reads at the wordmark's size — the two are the page's only
     display-scale type above the fold and they were fighting each other */
  .marq__word { font-size: var(--t-word); }

  /* deeper over the photograph above it, which lifts everything below */
  .kcafe__plate { margin-top: -230px; }
}

/* a note about the whole board sits under the middle of it */
.board__foot { text-align: center; }

/* A cell that is square on a phone and landscape here needs a different crop
   in each: the bias is carried as a variable and only read in the wide
   layout, so the phone keeps the framing it was signed off with. */
@media (min-width: 768px) {
  .tile__media img { object-position: var(--pos, 50% 50%); }
}

}

/* the strawberry yogurt is a tall cup in a square frame; on the wide board the
   frame is a short strip, so the band is biased up to keep the lid in it */
@media (min-width: 768px) {
  .mgroup__img[data-img*="strawberry-yogurt-cup"] img { object-position: 50% 22%; }
}
