/* ============================================================================
   Stories — base, layout, utilities and components.
   Responsive system is CONTAINER QUERIES ONLY: no viewport media queries and no
   pixel breakpoints. Put .sc-cq on the wrapper you want measured, then use the
   layout classes on the element inside it. Breakpoint vocabulary, in rem:
     wide   ≥ 64rem   three-up, side-by-side
     mid    < 64rem   two-up
     narrow < 40rem   single column
     tight  < 28rem   stacked controls, condensed chrome
   ========================================================================== */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: 6rem;
}

body {
  margin: 0;
  background: var(--surface-page);
  color: var(--text-body);
  font-family: var(--font-core);
  font-size: var(--fs-body);
  font-weight: var(--fw-regular);
  line-height: var(--lh-body);
  letter-spacing: var(--tr-body);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, h4, h5, h6 {
  margin: 0;
  color: var(--text-heading);
  font-weight: var(--fw-medium);
  text-wrap: balance;
}

/* Santral is supplied in Bold only, so it carries h1–h3. Everything else uses --font-core. */
h1, h2, h3 { font-family: var(--font-display); }
h4, h5, h6 { font-family: var(--font-core); }

h1 {
  font-size: var(--fs-h1);
  line-height: var(--lh-tight);
  letter-spacing: var(--tr-h1);
  text-transform: var(--case-heading);
  font-weight: var(--fw-bold);
}
h2 {
  font-size: var(--fs-h2);
  line-height: var(--lh-heading);
  letter-spacing: var(--tr-h2);
  text-transform: var(--case-heading);
  font-weight: var(--fw-bold);
}
h3 {
  font-size: var(--fs-h3);
  line-height: var(--lh-snug);
  letter-spacing: var(--tr-h3);
  text-transform: var(--case-heading);
  font-weight: var(--fw-bold);
}
h4 {
  font-size: var(--fs-h4);
  line-height: var(--lh-snug);
  font-weight: var(--fw-medium);
}

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

a {
  color: var(--text-link);
  text-decoration: none;
  text-underline-offset: 0.18em;
  transition: var(--t-control);
}
a:hover {
  color: var(--text-link-hover);
  text-decoration: underline;
  text-decoration-thickness: 1px;
}

a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
summary:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

img, svg, video { display: block; max-width: 100%; }
button { font: inherit; color: inherit; }
ul, ol { margin: 0; padding: 0; list-style: none; }
hr { border: 0; border-top: 1px solid var(--line); margin: 0; }

::selection { background: var(--ember); color: var(--ink-0); }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: var(--surface-sunken); }
::-webkit-scrollbar-thumb { background: var(--ink-3); }
::-webkit-scrollbar-thumb:hover { background: var(--ink-4); }

/* Skip link — first tab stop on every page. */
.skip-link {
  position: absolute;
  left: var(--sp-5);
  top: -4rem;
  z-index: 100;
  padding: var(--sp-3) var(--sp-5);
  background: var(--ink-9);
  color: var(--ink-0);
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  transition: top var(--dur) var(--ease-out);
}
.skip-link:focus { top: var(--sp-5); text-decoration: none; color: var(--ink-0); }

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

/* ── Bevel: the brand's corner treatment. Chamfer, never radius. ──────────── */
.sc-bevel {
  --b: var(--bevel-card);
  clip-path: polygon(
    var(--b) 0, calc(100% - var(--b)) 0, 100% var(--b), 100% calc(100% - var(--b)),
    calc(100% - var(--b)) 100%, var(--b) 100%, 0 calc(100% - var(--b)), 0 var(--b)
  );
}
.sc-bevel-xs { --b: var(--bevel-xs); }
.sc-bevel-sm { --b: var(--bevel-sm); }
.sc-bevel-lg { --b: var(--bevel-lg); }

/* Bevel only the two diagonal corners — used for tags, menu chips, image frames. */
.sc-bevel-diag {
  --b: var(--bevel-sm);
  clip-path: polygon(
    var(--b) 0, 100% 0, 100% calc(100% - var(--b)),
    calc(100% - var(--b)) 100%, 0 100%, 0 var(--b)
  );
}

/* Bordered bevel: outer paints the hairline, inner paints the surface.
   clip-path cuts borders, so the border has to be painted as an outer layer. */
.sc-bevel-frame {
  --b: var(--bevel-card);
  --frame-line: var(--line);
  --frame-fill: var(--surface-card);
  background: var(--frame-line);
  padding: 1px;
  clip-path: polygon(
    var(--b) 0, calc(100% - var(--b)) 0, 100% var(--b), 100% calc(100% - var(--b)),
    calc(100% - var(--b)) 100%, var(--b) 100%, 0 calc(100% - var(--b)), 0 var(--b)
  );
  transition: background-color var(--dur) var(--ease-out);
}
.sc-bevel-frame > * {
  --bi: calc(var(--b) - 0.5px);
  background: var(--frame-fill);
  height: 100%;
  clip-path: polygon(
    var(--bi) 0, calc(100% - var(--bi)) 0, 100% var(--bi), 100% calc(100% - var(--bi)),
    calc(100% - var(--bi)) 100%, var(--bi) 100%, 0 calc(100% - var(--bi)), 0 var(--bi)
  );
  transition: background-color var(--dur) var(--ease-out);
}

/* Chamfered hairline OUTLINE, for controls that stay transparent inside.
   An inset box-shadow only runs along the four STRAIGHT edges, so clipping the
   box to a chamfer erases each corner and leaves the stroke with a diagonal gap
   at all four — and the .sc-bevel-frame sandwich above can't stand in, since it
   needs an opaque interior to hide its line layer. So the stroke is painted as
   a filled RING: one polygon that traces the outer octagon clockwise, bridges
   inward, and traces the inner octagon anticlockwise. The opposing windings
   cancel across the middle (default nonzero fill rule), leaving a --lw band that
   turns every corner. The bridge is walked out and back along the same line, so
   it encloses no area of its own. The element keeps its own clip-path for the
   fill; this only replaces the border. */
/* .btn, .tag and .menu-tab already establish a containing block of their own. */
.sc-bevel-line,
.swatch__chip { position: relative; }
.sc-bevel-line::after,
.btn--outline::after,
.tag--outline::after,
.tag--ember::after,
.swatch__chip::after,
.menu-tab::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: var(--stroke, var(--line));
  --lw: 1px;
  /* Offsetting a 45° chamfer inward by w pulls its ends in by w(√2 − 1). */
  --lb: calc(var(--b) - 0.4142 * var(--lw));
  transition: background-color var(--dur-fast) var(--ease-out);
}
/* All four corners — pairs with .sc-bevel. */
.sc-bevel-line::after,
.btn--outline::after,
.tag--outline::after,
.tag--ember::after,
.swatch__chip::after {
  clip-path: polygon(
    var(--b) 0, calc(100% - var(--b)) 0, 100% var(--b), 100% calc(100% - var(--b)),
    calc(100% - var(--b)) 100%, var(--b) 100%, 0 calc(100% - var(--b)), 0 var(--b), var(--b) 0,
    calc(var(--lw) + var(--lb)) var(--lw),
    var(--lw) calc(var(--lw) + var(--lb)),
    var(--lw) calc(100% - var(--lw) - var(--lb)),
    calc(var(--lw) + var(--lb)) calc(100% - var(--lw)),
    calc(100% - var(--lw) - var(--lb)) calc(100% - var(--lw)),
    calc(100% - var(--lw)) calc(100% - var(--lw) - var(--lb)),
    calc(100% - var(--lw)) calc(var(--lw) + var(--lb)),
    calc(100% - var(--lw) - var(--lb)) var(--lw),
    calc(var(--lw) + var(--lb)) var(--lw)
  );
}
/* Two diagonal corners — pairs with .sc-bevel-diag. */
.sc-bevel-diag.sc-bevel-line::after,
.menu-tab::after {
  clip-path: polygon(
    var(--b) 0, 100% 0, 100% calc(100% - var(--b)),
    calc(100% - var(--b)) 100%, 0 100%, 0 var(--b), var(--b) 0,
    calc(var(--lw) + var(--lb)) var(--lw),
    var(--lw) calc(var(--lw) + var(--lb)),
    var(--lw) calc(100% - var(--lw)),
    calc(100% - var(--lw) - var(--lb)) calc(100% - var(--lw)),
    calc(100% - var(--lw)) calc(100% - var(--lw) - var(--lb)),
    calc(100% - var(--lw)) var(--lw),
    calc(var(--lw) + var(--lb)) var(--lw)
  );
}

/* ── Type utilities ───────────────────────────────────────────────────────── */
.sc-eyebrow {
  font-size: var(--fs-micro);
  font-weight: var(--fw-medium);
  letter-spacing: var(--tr-eyebrow);
  text-transform: uppercase;
  color: var(--text-faint);
  line-height: 1;
}
.sc-label {
  font-size: var(--fs-micro);
  font-weight: var(--fw-medium);
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  color: var(--text-muted);
  line-height: 1;
}
.sc-lead {
  font-size: var(--fs-lead);
  line-height: var(--lh-snug);
  color: var(--text-body);
}
.sc-prose { max-width: var(--max-prose); }
.sc-num { font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1; }

/* ── Layout ───────────────────────────────────────────────────────────────── */
.sc-page {
  max-width: var(--max-page);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.sc-section { padding-block: var(--section-y); }
.sc-section--sunken {
  background: var(--surface-sunken);
  border-block: 1px solid var(--line-hairline);
}
.sc-stack { display: flex; flex-direction: column; gap: var(--stack); }
.sc-stack-lg { display: flex; flex-direction: column; gap: var(--sp-10); }
.sc-row { display: flex; align-items: center; gap: var(--sp-4); }

.sc-cq { container-type: inline-size; }

/* Cards: three-up → two-up → one-up */
.sc-cards {
  display: grid;
  gap: var(--sp-5);
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
@container (max-width: 64rem) { .sc-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@container (max-width: 40rem)  { .sc-cards { grid-template-columns: minmax(0, 1fr); } }

/* Two-column split; .sc-split-end hugs the right edge until it stacks */
.sc-split {
  display: grid;
  gap: var(--gutter);
  grid-template-columns: repeat(2, minmax(0, 1fr));
  align-items: start;
}
.sc-split-end { margin-left: auto; }
@container (max-width: 52rem) {
  .sc-split { grid-template-columns: minmax(0, 1fr); }
  .sc-split-end { margin-left: 0; }
}

/* Split weighted toward the second column (contact ↔ map) */
.sc-split-wide {
  display: grid;
  gap: var(--gutter);
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
  align-items: start;
}
@container (max-width: 56rem) { .sc-split-wide { grid-template-columns: minmax(0, 1fr); } }

/* Menu columns */
.sc-menu-cols {
  display: grid;
  gap: var(--section-y) var(--gutter);
  grid-template-columns: repeat(2, minmax(0, 1fr));
  align-items: start;
}
@container (max-width: 60rem) {
  .sc-menu-cols { grid-template-columns: minmax(0, 1fr); gap: var(--sp-10); }
}

/* Row that becomes a stack */
.sc-row-wrap {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-7);
}
@container (max-width: 40rem) {
  .sc-row-wrap { align-items: flex-start; flex-direction: column; }
}

/* Header chrome: inline nav above 48rem, disclosure below */
.sc-nav-inline { display: flex; align-items: center; gap: var(--sp-8); }
.sc-nav-compact { display: none; }
@container (max-width: 48rem) {
  .sc-nav-inline { display: none; }
  .sc-nav-compact { display: inline-flex; }
}

/* Location dropdown panel: five across, then three, two, one */
.sc-locmenu {
  display: grid;
  gap: var(--sp-5);
  grid-template-columns: repeat(5, minmax(0, 1fr));
}
@container (max-width: 72rem) { .sc-locmenu { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@container (max-width: 44rem) { .sc-locmenu { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@container (max-width: 26rem) { .sc-locmenu { grid-template-columns: minmax(0, 1fr); } }

/* Fluid type inside a container rather than the viewport */
@container (max-width: 40rem) { .sc-cq h1 { font-size: clamp(2.25rem, 11cqi, 3.5rem); } }
@container (max-width: 28rem) { .sc-cq h2 { font-size: clamp(1.75rem, 9cqi, 2.25rem); } }

/* ── Texture: the faint film-grain veil over full-bleed imagery ───────────── */
.sc-grain { position: relative; }
.sc-grain::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0.5;
  mix-blend-mode: soft-light;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='0.42'/%3E%3C/svg%3E");
}

/* Page-level noise: a fixed film grain over the whole surface. It never moves, so
   scrolling doesn't crawl the tile, and pointer-events:none keeps it out of the way.
   Blend mode is SCREEN, not overlay: overlay preserves blacks, so on a near-black page
   it is invisible. Screen lightens the dark surfaces where the grain is wanted and
   barely touches the cream elements. */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 9999;
  pointer-events: none;
  opacity: 0.07;
  mix-blend-mode: screen;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='p'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.62' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23p)'/%3E%3C/svg%3E");
}

/* Bottom protection gradient for text over photography.
   z-index:1 is essential: the ::before is generated as the FIRST child, so without it an
   absolutely-positioned image later in the DOM paints on top and the gradient protects
   nothing. Media inside stays at z-index auto/0; text goes at 2 or above.
   The second, lighter layer from the top keeps the sticky header legible. */
.sc-protect { position: relative; }
.sc-protect::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    linear-gradient(to bottom, var(--shade-a72) 0%, transparent 22%),
    linear-gradient(to top, var(--shade-a92) 0%, var(--shade-a72) 30%, var(--shade-a40) 62%, transparent 90%);
}

/* ── Icons ────────────────────────────────────────────────────────────────── */
.icon {
  display: inline-block;
  flex: 0 0 auto;
  width: 1.125rem;
  height: 1.125rem;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  fill: none;
}
.icon--sm { width: 0.9375rem; height: 0.9375rem; }
.icon--lg { width: 1.625rem; height: 1.625rem; }

/* ── Wordmark ─────────────────────────────────────────────────────────────── */
/* Painted with a CSS mask rather than an <img> so it inherits the brand tones
   exactly like an icon does, keeping the mark strictly monochrome.
   The supplied SVG is 600×125 → aspect 4.8. */
.wordmark {
  --mark-h: 1.625rem;
  display: block;
  flex: 0 0 auto;
  height: var(--mark-h);
  width: calc(var(--mark-h) * 4.8);
  background: var(--ink-9);
  -webkit-mask: url("/assets/logo.44a46778a7d24933.svg") no-repeat left center / contain;
  mask: url("/assets/logo.44a46778a7d24933.svg") no-repeat left center / contain;
  transition: height var(--dur) var(--ease-out), width var(--dur) var(--ease-out);
}

/* ── Site header ──────────────────────────────────────────────────────────── */
.site-header {
  position: sticky;
  top: 0;
  z-index: 40;
  container-type: inline-size;
  background: var(--surface-nav);
  -webkit-backdrop-filter: var(--blur-nav);
  backdrop-filter: var(--blur-nav);
  border-bottom: 1px solid var(--line-hairline);
}
.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-7);
  height: 5.25rem;
  transition: height var(--dur) var(--ease-out);
}
.site-header.is-condensed .site-header__inner { height: 4rem; }
.site-header.is-condensed .wordmark { --mark-h: 1.25rem; }
.site-header.is-condensed .brand__name { font-size: 0.625rem; letter-spacing: 0.11em; }

.brand {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--sp-2);
}
.brand:hover { text-decoration: none; }
.brand__name {
  color: var(--text-muted);
  transition: font-size var(--dur) var(--ease-out), letter-spacing var(--dur) var(--ease-out);
}

.site-nav { display: flex; align-items: center; gap: var(--sp-7); }

.nav-link {
  position: relative;
  display: inline-flex;
  align-items: center;
  min-height: var(--tap-min);
  padding: 0 var(--sp-1);
  font-size: var(--fs-micro);
  font-weight: var(--fw-medium);
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  color: var(--text-muted);
  transition: var(--t-control);
}
.nav-link:hover,
.nav-link[aria-current="page"] { color: var(--ink-9); text-decoration: none; }
.nav-link::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0.625rem;
  height: 1px;
  width: 0;
  background: currentColor;
  transition: width var(--dur) var(--ease-out);
}
.nav-link:hover::after,
.nav-link[aria-current="page"]::after { width: 100%; }

.loc-toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-3);
  min-height: var(--tap-min);
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
  font-family: var(--font-core);
  font-size: var(--fs-micro);
  font-weight: var(--fw-medium);
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  color: var(--text-muted);
  transition: var(--t-control);
}
.loc-toggle:hover,
.loc-toggle[aria-expanded="true"] { color: var(--ink-9); }
.loc-toggle .icon {
  transition: transform var(--dur) var(--ease-out);
}
.loc-toggle[aria-expanded="true"] .icon { transform: rotate(180deg); }

/* display:inline-flex is load-bearing — a <button> is inline-block by default,
   so align-items/justify-content would silently do nothing and the glyph would
   sit off-centre. The icon is a fixed square, so it centres exactly. */
.nav-toggle {
  /* Grid, not flex: both glyphs share one cell so they can cross-fade in place
     and the button keeps its size. display:none cannot be transitioned, and
     this is the one control whose state change the user is actually watching. */
  display: inline-grid;
  place-items: center;
  width: var(--control-h);
  height: var(--control-h);
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--ink-8);
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease-out);
}
.nav-toggle:hover,
.nav-toggle[aria-expanded="true"] { color: var(--ink-9); }
.nav-toggle .icon {
  grid-area: 1 / 1;
  width: 1.375rem;
  height: 1.375rem;
  transition: opacity var(--dur) var(--ease-out), transform var(--dur) var(--ease-out);
}
/* The bars turn out clockwise and the cross turns in behind them, so the swap
   reads as one continuous quarter-turn rather than two separate fades. */
.nav-toggle__open { opacity: 1; transform: none; }
.nav-toggle__close { opacity: 0; transform: rotate(-90deg) scale(0.6); }
.nav-toggle[aria-expanded="true"] .nav-toggle__open { opacity: 0; transform: rotate(90deg) scale(0.6); }
.nav-toggle[aria-expanded="true"] .nav-toggle__close { opacity: 1; transform: none; }

/* Compact disclosure list under the bar */
.nav-compact-panel {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--dur-slow) var(--ease-entry);
  border-top: 1px solid transparent;
}
.nav-compact-panel[data-open="true"] {
  grid-template-rows: 1fr;
  border-top-color: var(--line-hairline);
}
.nav-compact-panel > div { overflow: hidden; }
.nav-compact-panel .sc-page {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  padding-block: var(--sp-2) var(--sp-6);
}
/* Links rise into the unrolling panel 60ms apart — the same figure the location
   tiles use, since the two panels drop from the same header. Out is quicker
   than in, so closing feels like the panel is being put away rather than
   replayed backwards. */
.nav-compact-panel .nav-link {
  opacity: 0;
  transform: translateY(0.5rem);
  transition: opacity 160ms var(--ease-out), transform 160ms var(--ease-out);
}
.nav-compact-panel[data-open="true"] .nav-link {
  opacity: 1;
  transform: translateY(0);
  transition: opacity 260ms var(--ease-out) calc(var(--i, 0) * 60ms),
    transform var(--dur-slow) var(--ease-entry) calc(var(--i, 0) * 60ms);
}

/* ── Location dropdown panel ──────────────────────────────────────────────── */
/* Full-bleed disclosure panel under the site header listing every café as an
   image + name tile. Absolutely positioned so it OVERLAYS the page instead of
   pushing it down. The grid-template-rows 0fr→1fr trick unrolls it to its own
   measured height with no JS measurement at all. */
.locmenu {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  z-index: 39;
  container-type: inline-size;
  display: grid;
  grid-template-rows: 0fr;
  pointer-events: none;
  background: var(--surface-nav-panel);
  -webkit-backdrop-filter: var(--blur-nav);
  backdrop-filter: var(--blur-nav);
  border-top: 1px solid transparent;
  border-bottom: 1px solid transparent;
  transition: grid-template-rows 300ms var(--ease-entry),
    border-color var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease-out);
}
.locmenu[data-open="true"] {
  grid-template-rows: 1fr;
  pointer-events: auto;
  border-top-color: var(--line-hairline);
  border-bottom-color: var(--line-hairline);
  box-shadow: var(--shadow-3);
  transition-duration: 460ms;
}
/* The animating element and the scroll container must be SEPARATE: overflow:hidden is
   what makes the row transition work, so if this element also owned scrolling,
   everything past the cap would be silently unreachable. */
.locmenu__scroll { overflow: hidden; }
.locmenu__inner {
  max-height: 80vh;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding-block: var(--sp-7) var(--sp-8);
}

.locmenu__tile {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  color: inherit;
  opacity: 0;
  transform: translateY(0.75rem);
  transition: opacity 160ms var(--ease-out), transform 160ms var(--ease-out);
}
.locmenu__tile:hover { text-decoration: none; color: inherit; }
/* Tiles follow the unrolling panel, 60ms apart. */
.locmenu[data-open="true"] .locmenu__tile {
  opacity: 1;
  transform: translateY(0);
  transition: opacity 260ms var(--ease-out) calc(var(--i, 0) * 60ms),
    transform var(--dur-slow) var(--ease-entry) calc(var(--i, 0) * 60ms);
}
/* Spotlight: the one you point at stays lit, its siblings drop to 44%.
   Gated on a hover-capable pointer — a touch device never hovers, so without
   this the tiles would sit permanently in their dimmed resting state. */
@media (hover: hover) {
  .locmenu__grid:has(.locmenu__tile:hover) .locmenu__tile:not(:hover) { opacity: 0.44; }
  .locmenu__grid:focus-within .locmenu__tile:not(:focus-visible):not(:hover) { opacity: 0.44; }
}

.locmenu__media {
  --b: var(--bevel-sm);
  position: relative;
  display: block;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  background: var(--surface-inset);
}
/* Lit by DEFAULT. The dimmed resting state is a hover affordance, so it only
   applies where a pointer can actually hover; on touch the tiles are simply on. */
.locmenu__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: saturate(0.5) brightness(0.88);
  transition: filter var(--dur-slow) var(--ease-out);
}
@media (hover: hover) {
  .locmenu__media img { filter: saturate(0.3) brightness(0.58); }
  .locmenu__tile:hover .locmenu__media img,
  .locmenu__tile:focus-visible .locmenu__media img { filter: saturate(0.5) brightness(0.88); }
}

.locmenu__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
}
.locmenu__name {
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  color: var(--ink-8);
  transition: color var(--dur-fast) var(--ease-out);
}
.locmenu__tile:hover .locmenu__name { color: var(--ink-9); }
.locmenu__arrow {
  color: var(--text-faint);
  transition: transform var(--dur) var(--ease-out), color var(--dur) var(--ease-out);
}
.locmenu__tile:hover .locmenu__arrow {
  color: var(--ink-9);
  transform: translate(2px, -2px);
}
.locmenu__address {
  font-size: var(--fs-xs);
  color: var(--text-faint);
  line-height: var(--lh-snug);
}

/* ── Buttons ──────────────────────────────────────────────────────────────── */
/* Hover is a cream wipe: a fill layer rises from the bottom edge and the label
   flips to ink. */
.btn {
  --b: var(--bevel-btn);
  --btn-bg: transparent;
  --btn-fg: var(--btn-outline-fg);
  --btn-wipe: var(--ink-9);
  --btn-fg-over: var(--ink-0);
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: var(--control-h);
  min-height: var(--tap-min);
  padding: 0 var(--sp-6);
  border: 0;
  cursor: pointer;
  background: var(--btn-bg);
  color: var(--btn-fg);
  font-family: var(--font-core);
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  transition: color var(--dur-fast) var(--ease-out) 60ms,
    background-color var(--dur-fast) var(--ease-out),
    transform var(--dur-instant) var(--ease-out);
}
.btn::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--btn-wipe);
  transform: translateY(101%);
  transition: transform 220ms var(--ease-entry);
}
.btn:hover::before { transform: translateY(0); transition-duration: 320ms; }
.btn:hover { color: var(--btn-fg-over); text-decoration: none; }
.btn:active { transform: translateY(1px); }
.btn__label {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: var(--sp-3);
}
.btn--solid { --btn-bg: var(--btn-solid-bg); --btn-fg: var(--btn-solid-fg); --btn-wipe: oklch(1 0 0); }
.btn--ghost { --btn-bg: var(--cream-a04); --btn-fg: var(--btn-ghost-fg); }
.btn--ember { --btn-bg: var(--ember); --btn-fg: var(--ink-0); --btn-wipe: var(--ember-bright); }
/* Outlines are stroked as an overlay rather than sandwiched between two filled
   layers, so a transparent variant stays transparent instead of filling with the
   line colour. The ::after ring above draws it — see the chamfered outline note. */
.btn--outline { --stroke: var(--line-strong); }
/* The cream wipe takes the whole face, so the stroke steps aside rather than
   sitting on top of it. */
.btn--outline:hover::after { background: transparent; }
.btn--lg { height: var(--control-h-lg); padding: 0 var(--sp-8); font-size: var(--fs-sm); }
.btn--sm { height: var(--control-h-sm); padding: 0 var(--sp-4); font-size: var(--fs-micro); }

/* ── Tag ──────────────────────────────────────────────────────────────────── */
.tag {
  --b: var(--bevel-chip);
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  height: 1.5rem;
  padding: 0 var(--sp-3);
  box-sizing: border-box;
  background: var(--cream-a08);
  color: var(--text-body);
  font-size: var(--fs-micro);
  font-weight: var(--fw-medium);
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  line-height: 1;
  white-space: nowrap;
}
.tag--outline { background: transparent; color: var(--text-muted); --stroke: var(--line); }
.tag--ember { background: transparent; color: var(--ember); --stroke: var(--line-accent); }
.tag--invert { background: var(--surface-invert); color: var(--text-on-invert); }
.tag__dot {
  width: 5px;
  height: 5px;
  border-radius: var(--radius-pill);
  background: currentColor;
  flex: 0 0 auto;
}

/* ── Section heading ──────────────────────────────────────────────────────── */
.section-heading {
  display: flex;
  flex-direction: column;
  gap: var(--sp-5);
  align-items: flex-start;
}
.section-heading > h2,
.section-heading > h3 { max-width: 22ch; }
.section-heading .sc-lead { color: var(--text-muted); }

/* ── Word-by-word heading reveal ──────────────────────────────────────────── */
/* Held back from the FIRST painted frame, not from the moment the words are
   split: site.js is deferred, so on a slow device the heading would otherwise
   paint solid, blink out when the split lands, and only then slide in. The js
   class is set inline in <head> for exactly this reason, and dropped again if
   site.js never runs — so a no-JS page, or one where the script fails, still
   shows the heading outright. */
.js .reveal:not([data-split]):not(.is-revealed) { opacity: 0; }

.reveal__w {
  display: inline-block;
  overflow: hidden;
  vertical-align: top;
}
.reveal__i { display: inline-block; }
.js .reveal[data-split] .reveal__i {
  opacity: 0;
  transform: translateY(85%);
  transition:
    opacity 0.7s var(--ease-power3) calc(var(--i, 0) * 0.08s),
    transform 0.7s var(--ease-power3) calc(var(--i, 0) * 0.08s);
  will-change: transform, opacity;
}
.js .reveal[data-split].is-revealed .reveal__i { opacity: 1; transform: translateY(0); }

/* Generic rise-and-fade entrance, staggered by --i. */
.js [data-enter] {
  opacity: 0;
  transform: translateY(1.5rem);
  transition:
    opacity var(--dur-reveal) var(--ease-entry) calc(var(--i, 0) * 0.09s),
    transform var(--dur-reveal) var(--ease-entry) calc(var(--i, 0) * 0.09s);
}
.js [data-enter].is-in { opacity: 1; transform: translateY(0); }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .js .reveal,
  .js .reveal[data-split] .reveal__i,
  .js [data-enter] { opacity: 1; transform: none; transition: none; }
  /* The outline still appears on hover — it just stops travelling. */
  .loc-card__outline path { transition: none; }
}

/* ── Location grid ────────────────────────────────────────────────────────── */
/* Sibling-dimming grid: hovering one card lifts it to full brightness, brightens
   its chamfered outline, and pushes every other card down to 44%. Column count
   comes from the .sc-cards container queries — three-up, two-up, one-up. */
.loc-card {
  position: relative;
  display: block;
  color: inherit;
  transition: opacity 260ms var(--ease-out);
}
.loc-card:hover { text-decoration: none; color: inherit; }
/* Hover-only: on a touch device every card stays fully lit. */
/* Keyed on :has(a card hovered), NOT on the grid being hovered — the grid also
   covers the gaps between cards, and pointing at empty track is not pointing at
   anything to spotlight. Kept in its own rule so the :focus-within half below
   still parses where :has does not. */
@media (hover: hover) {
  .loc-grid:has(.loc-card:hover) .loc-card:not(:hover) {
    opacity: 0.44;
    /* Siblings dim faster than they come back, so the focused card reads as the
       deliberate one. */
    transition-duration: 520ms;
  }
  .loc-grid:focus-within .loc-card:not(:focus-visible):not(:hover) {
    opacity: 0.44;
    transition-duration: 520ms;
  }
}
.loc-card__frame { height: 100%; }
.loc-card:hover .loc-card__frame { --frame-fill: var(--surface-raised); }

/* Chamfered outline that draws itself in along the card's own edge on hover.
   The path is generated in JS because the chamfer is a fixed 10px at any card
   size — a scaled viewBox would skew the 45° corners. pathLength="1" normalises
   the length so stroke-dashoffset animates 1 → 0 without measuring geometry.
   Drawn OVER the frame's hairline rather than replacing it, so the resting
   border stays put and only the bright stroke travels. */
.loc-card__outline {
  position: absolute;
  inset: 0;
  overflow: visible;
  pointer-events: none;
}
.loc-card__outline path {
  fill: none;
  stroke: var(--ink-9);
  stroke-width: 1;
  stroke-linejoin: miter;
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  transition: stroke-dashoffset 260ms var(--ease-out);
}
/* Slower in than out, so it draws deliberately and retracts quickly. */
.loc-card:hover .loc-card__outline path,
.loc-card:focus-visible .loc-card__outline path {
  stroke-dashoffset: 0;
  transition-duration: 620ms;
}
.loc-card__body { display: flex; flex-direction: column; height: 100%; }
.loc-card__media {
  position: relative;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  background: var(--surface-inset);
}
.loc-card__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Resting state is FULLY LIT — the spotlight works by dimming siblings, not by
     holding the default back. Hover only adds the push-in. */
  filter: saturate(0.62) brightness(0.94);
  transform: scale(1);
  transform-origin: center;
  /* Slower in than out so it settles rather than snaps. */
  transition: filter var(--dur-slow) var(--ease-out), transform 520ms var(--ease-out);
}
.loc-card:hover .loc-card__media img { transform: scale(1.05); transition-duration: var(--dur-slow), 760ms; }
@media (hover: hover) {
  .loc-grid:has(.loc-card:hover) .loc-card:not(:hover) .loc-card__media img { filter: saturate(0.42) brightness(0.68); }
}

.loc-card__text {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  padding: var(--sp-6) var(--sp-6) var(--sp-7);
  flex: 1;
}
.loc-card__name {
  font-size: var(--fs-h4);
  letter-spacing: var(--tr-h3);
  text-transform: uppercase;
  color: var(--ink-9);
  font-family: var(--font-display);
  font-weight: var(--fw-bold);
}
.loc-card__address {
  color: var(--text-muted);
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
}
.loc-card__foot {
  margin-top: auto;
  padding-top: var(--sp-5);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
}
.loc-card__hours.is-closed { color: var(--state-closed); }
.loc-card__arrow {
  color: var(--text-faint);
  transition: transform var(--dur) var(--ease-out), color var(--dur) var(--ease-out);
}
.loc-card:hover .loc-card__arrow { color: var(--ink-9); transform: translate(2px, -2px); }

/* ── Banner ───────────────────────────────────────────────────────────────── */
.banner {
  position: relative;
  min-height: clamp(25rem, 58vh, 40rem);
  display: flex;
  align-items: flex-end;
  overflow: hidden;
  background: var(--surface-sunken);
}
.banner__media { position: absolute; inset: 0; }
.banner__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: saturate(0.35) brightness(0.62) contrast(1.06);
}
.banner__body {
  position: relative;
  z-index: 3;
  width: 100%;
  padding-block: var(--sp-12) var(--sp-11);
  display: flex;
  flex-direction: column;
  gap: var(--sp-6);
  align-items: flex-start;
}
.banner__eyebrow { color: var(--ink-7); }
.banner h1 { max-width: 18ch; }
.banner__lead { max-width: 46ch; color: var(--ink-7); }
.banner__tags { display: flex; flex-wrap: wrap; gap: var(--sp-3); margin-top: var(--sp-3); }

/* ── Menu ─────────────────────────────────────────────────────────────────── */
.menu-tabs { display: flex; flex-wrap: wrap; gap: var(--sp-3); }
.menu-tab {
  --b: var(--bevel-sm);
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: var(--control-h);
  padding: 0 var(--sp-6);
  border: 0;
  box-sizing: border-box;
  cursor: pointer;
  background: transparent;
  color: var(--text-muted);
  font-size: var(--fs-micro);
  font-weight: var(--fw-medium);
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  white-space: nowrap;
  --stroke: var(--line);
  transition: color var(--dur-fast) var(--ease-out) 60ms,
    background-color var(--dur-fast) var(--ease-out);
}
.menu-tab::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--ink-9);
  transform: translateY(101%);
  transition: transform 220ms var(--ease-entry);
}
.menu-tab:hover::before { transform: translateY(0); transition-duration: 320ms; }
.menu-tab:hover { color: var(--ink-0); }
.menu-tab[aria-selected="true"] {
  background: var(--ink-9);
  color: var(--ink-0);
}
.menu-tab[aria-selected="true"]::before { display: none; }
/* Filled states swallow the stroke rather than drawing a line over their own fill. */
.menu-tab:hover::after,
.menu-tab[aria-selected="true"]::after { background: transparent; }
.menu-tab__label { position: relative; }

.menu-panel[hidden] { display: none; }

.menu-section { display: flex; flex-direction: column; gap: var(--sp-3); }
.menu-section__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-5);
  padding-bottom: var(--sp-4);
  border-bottom: 1px solid var(--line-strong);
}
.menu-section__title {
  font-size: var(--fs-h4);
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  font-weight: var(--fw-bold);
  font-family: var(--font-display);
}
.menu-section__note { color: var(--text-faint); }

.menu-item {
  display: grid;
  /* minmax(0,…) not 1fr: a bare 1fr floors at min-content, so a long dish name
     pushes the price out of the container and scrolls the whole page sideways. */
  grid-template-columns: minmax(0, 1fr) auto;
  column-gap: var(--sp-7);
  row-gap: var(--sp-2);
  align-items: baseline;
  padding-block: var(--sp-5);
  border-bottom: 1px solid var(--line-hairline);
}
.menu-item__name {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  flex-wrap: wrap;
  font-weight: var(--fw-medium);
  color: var(--text-heading);
  letter-spacing: 0.005em;
}
.menu-item__prices { display: flex; align-items: baseline; gap: var(--sp-6); }
.menu-item__price-group { display: inline-flex; align-items: baseline; gap: var(--sp-2); }
.menu-item__portion { color: var(--text-faint); }
.menu-item__desc {
  grid-column: 1 / 2;
  color: var(--text-muted);
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
  max-width: 54ch;
}

/* Stories menus print the price as a number followed by the Swedish ":–" mark,
   never "kr" and never a currency symbol. */
.price {
  color: var(--text-heading);
  font-size: var(--fs-body);
  font-weight: var(--fw-medium);
  white-space: nowrap;
  letter-spacing: 0.01em;
}
.price--muted { color: var(--text-muted); }
.price::after {
  content: ":–";
  color: var(--text-faint);
  font-weight: var(--fw-light);
  margin-left: 1px;
}

/* ── Hours table ──────────────────────────────────────────────────────────── */
.hours { display: flex; flex-direction: column; gap: var(--sp-5); }
.hours__title { font-size: var(--fs-h4); letter-spacing: var(--tr-label); text-transform: uppercase; }
.hours__list { margin: 0; display: flex; flex-direction: column; }
.hours__row {
  display: flex;
  align-items: baseline;
  gap: var(--sp-5);
  padding-block: var(--sp-4);
}
.hours__label { color: var(--text-muted); font-size: var(--fs-sm); }
.hours__row--emphasis .hours__label {
  color: var(--text-heading);
  text-transform: uppercase;
  letter-spacing: var(--tr-label);
}
.hours__rule { flex: 1; height: 1px; background: var(--line-hairline); transform: translateY(-3px); }
.hours__value {
  margin: 0;
  color: var(--text-heading);
  font-size: var(--fs-sm);
  white-space: nowrap;
}
.hours__value--empty { color: var(--text-faint); }

/* ── Info rows ────────────────────────────────────────────────────────────── */
.info-row {
  display: grid;
  grid-template-columns: 1.375rem minmax(0, 1fr);
  column-gap: var(--sp-5);
  row-gap: var(--sp-1);
  align-items: start;
  padding-block: var(--sp-5);
  color: inherit;
  border-bottom: 1px solid var(--line-hairline);
}
a.info-row:hover { text-decoration: none; color: inherit; }
.info-row__icon { margin-top: 2px; color: var(--text-faint); transition: color var(--dur-fast) var(--ease-out); }
a.info-row:hover .info-row__icon { color: var(--ember); }
.info-row__text { display: flex; flex-direction: column; gap: 2px; }
.info-row__label { color: var(--text-faint); }
.info-row__value {
  color: var(--text-heading);
  font-size: var(--fs-body);
  /* An e-mail or URL is one unbreakable token — let it break rather than scroll. */
  overflow-wrap: anywhere;
  transition: color var(--dur-fast) var(--ease-out);
}
a.info-row:hover .info-row__value { color: var(--ink-9); }
.info-row__note { color: var(--text-faint); font-size: var(--fs-xs); }

/* ── Map well ─────────────────────────────────────────────────────────────── */
/* width:100% is load-bearing: with only aspect-ratio + min-height the box takes
   its width FROM the min-height (260px × 1.6 = 416px) and overflows any column
   narrower than that, scrolling the whole page sideways on mobile. Pinning the
   width makes aspect-ratio derive the height instead, and min-height can then
   only ever make it taller. */
.map-well {
  width: 100%;
  aspect-ratio: 16 / 10;
  min-height: 16.25rem;
  --frame-fill: var(--surface-sunken);
}
.map-well__inner {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
}
.map-well iframe { width: 100%; height: 100%; border: 0; display: block; filter: grayscale(1) invert(0.92) contrast(0.86); }
.map-well__placeholder {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-4);
  color: var(--text-faint);
  text-align: center;
  padding: var(--sp-6);
}

/* ── Media frame (the "Om Stories" picture) ───────────────────────────────── */
.media-frame { aspect-ratio: 4 / 3; --frame-fill: var(--surface-inset); }
.media-frame__inner { position: relative; width: 100%; height: 100%; overflow: hidden; }
.media-frame__inner img { width: 100%; height: 100%; object-fit: cover; filter: saturate(0.55) brightness(0.9); }

/* Placeholder used wherever a photograph has not been supplied yet. */
.img-slot {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--sp-5);
  background: var(--surface-inset);
  color: var(--text-faint);
  font-size: var(--fs-micro);
  font-weight: var(--fw-medium);
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  text-align: center;
}

/* ── Notice card ──────────────────────────────────────────────────────────── */
.notice { --frame-fill: var(--surface-card); }
.notice__inner {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  align-items: flex-start;
  padding: var(--sp-10);
}
.notice__title { font-size: var(--fs-h4); }
.notice__body { color: var(--text-muted); font-size: var(--fs-sm); max-width: 52ch; }

/* ── Footer ───────────────────────────────────────────────────────────────── */
/* Deliberately minimal: the wordmark, the locations, a copyright line and the
   privacy link. Location links carry the same spotlight hover as the cards — the
   one you point at goes full cream, its siblings drop to 44%. */
.site-footer {
  container-type: inline-size;
  border-top: 1px solid var(--line-hairline);
  background: var(--surface-sunken);
}
.site-footer__inner {
  padding-block: var(--sp-11) var(--sp-8);
  display: flex;
  flex-direction: column;
  gap: var(--sp-10);
}
.footer-nav { display: flex; flex-wrap: wrap; gap: var(--sp-4) var(--sp-8); }
.footer-nav a {
  font-size: var(--fs-micro);
  font-weight: var(--fw-medium);
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  color: var(--text-muted);
  transition: color var(--dur-fast) var(--ease-out), opacity var(--dur) var(--ease-out);
}
@media (hover: hover) {
  .footer-nav:hover a:not(:hover),
  .footer-nav:focus-within a:not(:focus-visible):not(:hover) { opacity: 0.44; }
}
.footer-nav a:hover { color: var(--ink-9); text-decoration: none; }
.site-footer__legal {
  gap: var(--sp-5) var(--sp-7);
  padding-top: var(--sp-6);
  border-top: 1px solid var(--line-hairline);
}
.site-footer__legal span,
.site-footer__legal a { font-size: var(--fs-xs); color: var(--text-faint); }
.site-footer__legal a:hover { color: var(--ink-9); text-decoration: none; }

/* ── Cookie consent ───────────────────────────────────────────────────────── */
.consent {
  position: fixed;
  z-index: 60;
  left: var(--gutter);
  right: var(--gutter);
  bottom: var(--gutter);
  margin-inline: auto;
  max-width: 34rem;
  --frame-fill: var(--surface-card);
  box-shadow: var(--shadow-3);
  transition: opacity var(--dur) var(--ease-out), transform var(--dur) var(--ease-out);
}
.consent[hidden] { display: none; }
.consent__inner { display: flex; flex-direction: column; gap: var(--sp-5); padding: var(--sp-7); }
.consent__title { font-size: var(--fs-h4); }
.consent__body { color: var(--text-muted); font-size: var(--fs-sm); }
.consent__actions { display: flex; flex-wrap: wrap; gap: var(--sp-3); }
.consent__actions .btn { flex: 1 1 auto; }
/* The third way out of the banner. Kept as quiet text rather than a third
   button: consent has to be a real choice, so "accept" must not out-shout it,
   but it must not be buried either. */
.consent__more,
.footer-consent {
  align-self: flex-start;
  padding: 0;
  border: 0;
  background: transparent;
  font: inherit;
  color: var(--text-faint);
  text-decoration: underline;
  text-underline-offset: 0.2em;
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease-out);
}
.consent__more { font-size: var(--fs-xs); }
.footer-consent { font-size: var(--fs-xs); text-decoration: none; }
.consent__more:hover,
.footer-consent:hover { color: var(--ink-9); }

/* ── Cookie preferences modal ─────────────────────────────────────────────── */
/* A native <dialog>, so focus trapping, Escape-to-close, an inert background
   and the backdrop all come from the platform — the only JS is opening it and
   reading the toggles back out. The banner carries the two one-click answers;
   anything per-purpose happens here, and the footer link reopens it so consent
   can be withdrawn as easily as it was given. */
/* The chamfer and its hairline come from the .sc-bevel + .sc-bevel-line
   utilities rather than .sc-bevel-frame: the frame pins its child to
   height:100%, and against a dialog's own fit-content height the two never
   settle — the body ends up squeezed and its flex children shrink out of it. */
.cookie-modal {
  /* Viewport units, not %: a top-layer dialog's percentage box does not resolve
     against the viewport the way a plain fixed element's does. dvh so mobile
     browser chrome counts, with vh first for anything lacking it. */
  width: min(34rem, calc(100vw - var(--gutter) * 2));
  max-height: calc(100vh - var(--gutter) * 2);
  max-height: calc(100dvh - var(--gutter) * 2);
  padding: 0;
  border: 0;
  background: var(--surface-card);
  color: var(--text-body);
  --stroke: var(--line);
  box-shadow: var(--shadow-3);
}
/* Scoped to [open] so the UA's display:none still hides it when closed. */
.cookie-modal[open] { display: flex; }
.cookie-modal::backdrop {
  background: var(--shade-a72);
  -webkit-backdrop-filter: var(--blur-veil);
  backdrop-filter: var(--blur-veil);
}
.cookie-modal[open] { animation: cookie-modal-in var(--dur) var(--ease-entry) both; }
.cookie-modal[open]::backdrop { animation: cookie-veil-in var(--dur) var(--ease-out) both; }
@keyframes cookie-modal-in { from { opacity: 0; transform: translateY(0.75rem); } }
@keyframes cookie-veil-in { from { opacity: 0; } }

/* Scroll container and padded body kept SEPARATE, the same split .locmenu uses.
   Scrolling is switched on only where the panel cannot fit: Chromium reports
   scrollHeight past clientHeight here even when the content sits comfortably
   inside, so an unconditional overflow:auto puts a scrollbar over nothing. The
   body is ~24rem tall, so below 34rem of viewport it genuinely needs to scroll
   and above it never does. */
.cookie-modal__scroll {
  flex: 1 1 auto;
  min-height: 0;
}
@media (max-height: 34rem) {
  .cookie-modal__scroll { overflow-y: auto; overscroll-behavior: contain; }
}
.cookie-modal__inner {
  display: flex;
  flex-direction: column;
  gap: var(--sp-6);
  padding: var(--sp-7);
}
.cookie-modal__title { font-size: var(--fs-h4); }
.cookie-modal__body { color: var(--text-muted); font-size: var(--fs-sm); }
.cookie-modal__actions { display: flex; flex-wrap: wrap; gap: var(--sp-3); }
.cookie-modal__actions .btn { flex: 1 1 auto; }

.cookie-cat { display: flex; flex-direction: column; }
.cookie-cat__row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sp-5);
  padding-block: var(--sp-5);
  border-bottom: 1px solid var(--line-hairline);
}
.cookie-cat__row:first-child { border-top: 1px solid var(--line-hairline); }
.cookie-cat__text { display: flex; flex-direction: column; gap: var(--sp-2); }
.cookie-cat__name {
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  color: var(--text-heading);
}
.cookie-cat__desc { font-size: var(--fs-xs); color: var(--text-faint); line-height: var(--lh-snug); }
/* Native checkbox on purpose: a hand-rolled switch buys nothing here and costs
   keyboard and screen-reader behaviour that already works. */
.cookie-switch {
  flex: 0 0 auto;
  width: 1.25rem;
  height: 1.25rem;
  margin-top: 0.125rem;
  accent-color: var(--ink-9);
  cursor: pointer;
}
.cookie-switch:disabled { cursor: default; opacity: 0.55; }

/* ── Content pages (privacy, 404, style guide) ────────────────────────────── */
.prose { max-width: var(--max-prose); display: flex; flex-direction: column; gap: var(--sp-6); }
.prose h2 { margin-top: var(--sp-8); }
.prose h3 { margin-top: var(--sp-5); }
.prose p, .prose li { color: var(--text-body); }
.prose ul { display: flex; flex-direction: column; gap: var(--sp-3); padding-left: var(--sp-6); list-style: disc; }
.prose a { text-decoration: underline; }

.swatch-grid { display: grid; gap: var(--sp-5); grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr)); }
.swatch { display: flex; flex-direction: column; gap: var(--sp-3); }
.swatch__chip { --b: var(--bevel-sm); height: 4.5rem; --stroke: var(--cream-a08); }
.spec { display: flex; flex-direction: column; gap: var(--sp-7); }
.spec__row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-5); }

/* ── Page compositions ────────────────────────────────────────────────────── */
.home-intro { padding-top: clamp(2.75rem, 6.5vw, 6rem); padding-bottom: var(--sp-9); }
.home-intro h1 { max-width: 16ch; }
.home-intro .sc-lead { max-width: 34ch; color: var(--text-muted); }
.home-locations { padding-bottom: var(--section-y); }

.about-split { align-items: center; }
.about-copy { display: flex; flex-direction: column; gap: var(--sp-8); }
.wordmark--watermark { --mark-h: 2rem; opacity: 0.22; }

.site-footer__top { align-items: flex-start; }

.menu-block { display: flex; flex-direction: column; gap: var(--sp-9); }
.menu-toolbar { display: flex; gap: var(--sp-3); align-items: center; }

/* ── Print: the menu is the thing worth printing ──────────────────────────── */
@media print {
  body { background: #fff; color: #111; }
  body::after, .site-header, .site-footer, .consent, .banner__media,
  .menu-tabs, .btn, .map-well, .locmenu, .skip-link,
  .cookie-modal { display: none !important; }
  .menu-panel[hidden] { display: block !important; }
  .sc-section { padding-block: 1rem; }
  h1, h2, h3, h4, .menu-item__name, .price, .hours__value { color: #111; }
  .menu-item__desc, .menu-section__note, .price::after { color: #555; }
  .menu-item, .menu-section__head { border-color: #ccc; }
  .sc-menu-cols { grid-template-columns: 1fr 1fr; gap: 1.5rem; }
  .menu-section { break-inside: avoid; }
  a { color: #111; text-decoration: none; }
}
