/* ═══════════════════════════════════════════════════════════════════════
   fressocafe.com

   Type:  New Spirit Light, tracked -30  (display)
          Hanken Grotesk Regular / Medium, tracked -20  (text)
   Marks: the "Fresso Cafe" wordmark (New Spirit Medium, -35), the F petal and
          the walking mark, all three SVG outlines exported from the brand's
          Illustrator files. The bar shows exactly ONE; data-logo says which.
   Tone:  cream ground, ink text, one deep green. Hairlines, a small radius on
          any photograph that is not bleeding off the page, and a lot of air.
          Nothing moves unless it is changing state, and everything that
          changes state is eased.
   ═══════════════════════════════════════════════════════════════════════ */

@font-face { font-family: 'New Spirit'; src: url('../fonts/NewSpirit-Light.woff2') format('woff2'); font-weight: 300; font-style: normal; font-display: swap; }
@font-face { font-family: 'Hanken Grotesk'; src: url('../fonts/HankenGrotesk-Regular.woff2') format('woff2'); font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: 'Hanken Grotesk'; src: url('../fonts/HankenGrotesk-Medium.woff2') format('woff2'); font-weight: 500; font-style: normal; font-display: swap; }

:root {
  /* fressoCream / fressoGreen, the same values the app and the gift page use */
  --cream: #f7f5f0;
  /* The mount board behind an Instagram print: a shade ABOVE the cream, so a
     framed photograph sits on paper and not on the page. */
  --paper: #fffdf8;
  --ink: #1a1a1a;
  --ink-2: #5a564f;
  --ink-3: #716c63;
  --line: rgba(26, 26, 26, .14);
  --green: #265221;
  --night: #0f1611;
  --on-night: #f3f0e9;
  --on-night-2: rgba(243, 240, 233, .66);
  --line-night: rgba(243, 240, 233, .2);

  --display: 'New Spirit', 'Iowan Old Style', 'Palatino Linotype', Georgia, serif;
  --text: 'Hanken Grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;

  --gutter: clamp(20px, 5vw, 64px);
  --max: 1360px;
  /* ONE measure of air, used as the block padding of every section, so any two
     neighbours are always exactly two of them apart. Sections that bleed a
     photograph to an edge drop it on that edge only. */
  --section: clamp(72px, 9vw, 130px);
  /* The left edge every section shares: the .wrap content edge, expressed so a
     full-bleed section (the app, the filmstrip) can line its words up with it. */
  --edge: max(var(--gutter), calc((100vw - var(--max)) / 2 + var(--gutter)));

  --ease: cubic-bezier(.22, .61, .2, 1);
  --ease-soft: cubic-bezier(.4, 0, .2, 1);
  --header-h: 72px;

  /* Photographs are cards, not cut-outs: one small radius on every framed
     picture. Full-bleed pictures (the hero, the app photograph, the Our Story
     plate) reset it, because a rounded corner on a bleeding edge is a mistake. */
  --radius: 10px;

  /* The marks: the scrolled size is the base, and the top-of-page state is the
     same mark scaled up around its own middle, so it stays on the nav's optical
     centre line in both states. One transform, so the two can cross-fade. */
  --brand-top: 1.2;
}

/* The walking mark is drawn as line and paper. The line is currentColor; the
   paper is this, so the mark can sit on the cream bar and over the photograph
   without ever flattening into a silhouette. Registering it lets it ease. */
@property --mk-paper {
  syntax: '<color>';
  inherits: true;
  initial-value: #f7f5f0;
}

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

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--header-h) + 8px);
}

body {
  margin: 0;
  background: var(--cream);
  color: var(--ink);
  font-family: var(--text);
  font-weight: 400;
  font-size: 1.0625rem;
  line-height: 1.62;
  letter-spacing: -.02em;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

img, svg, picture { display: block; max-width: 100%; }
img { height: auto; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; letter-spacing: inherit; background: none; border: 0; padding: 0; cursor: pointer; }
h1, h2, h3, p, figure, ul, ol, dl, dd { margin: 0; }
ul, ol { padding: 0; list-style: none; }
::selection { background: var(--green); color: var(--cream); }

:focus-visible { outline: 1.5px solid currentColor; outline-offset: 4px; }

.skip {
  position: fixed; left: 16px; top: -60px; z-index: 100;
  background: var(--ink); color: var(--cream); padding: 10px 16px;
  transition: top .3s var(--ease);
}
.skip:focus { top: 16px; }

/* Pages cross-fade into each other where the browser can do it. */
@view-transition { navigation: auto; }
::view-transition-old(root), ::view-transition-new(root) { animation-duration: .5s; animation-timing-function: var(--ease-soft); }

/* ─── Type ─────────────────────────────────────────────────────────────── */

.display {
  font-family: var(--display);
  font-weight: 300;
  letter-spacing: -.03em;
  line-height: 1.08;
  font-size: clamp(2.25rem, 1.35rem + 3.6vw, 4.25rem);
  text-wrap: balance;
}
.display-s {
  font-family: var(--display);
  font-weight: 300;
  letter-spacing: -.03em;
  line-height: 1.18;
  font-size: clamp(1.5rem, 1.2rem + 1.2vw, 2.125rem);
  text-wrap: balance;
}
.lede { font-size: clamp(1.0625rem, 1rem + .3vw, 1.1875rem); line-height: 1.6; color: var(--ink-2); text-wrap: pretty; }
.small { font-size: .9375rem; line-height: 1.55; }
.muted { color: var(--ink-2); }
.medium { font-weight: 500; }

.wrap { width: 100%; max-width: var(--max); margin-inline: auto; padding-inline: var(--gutter); }

/* A text link: a hairline that thickens into the ink on hover. */
.link {
  position: relative; display: inline-block; font-weight: 500;
  padding-bottom: 3px;
  background: linear-gradient(currentColor, currentColor) 0 100% / 100% 1px no-repeat;
  transition: background-size .5s var(--ease), opacity .3s;
}
.link::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 1px;
  background: currentColor; transform: scaleX(0); transform-origin: left;
}
/* A text link is 26px of type. A thumb needs 44, so the link carries an
   invisible one that does not move a single pixel of the layout. */
.link::before { content: ''; position: absolute; inset: -9px -6px -7px; }
@media (hover: hover) {
  .link:hover { background-size: 0 1px; background-position: 100% 100%; }
  .link:hover::after { transform: scaleX(1); transition: transform .5s var(--ease) .18s; }
}

/* The one button: a ruled rectangle that fills. */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  min-height: 50px; padding: 0 26px;
  border: 1px solid currentColor;
  font-weight: 500; font-size: .9375rem; letter-spacing: -.01em;
  position: relative; isolation: isolate; overflow: hidden;
  transition: color .45s var(--ease);
}
.btn::before {
  content: ''; position: absolute; inset: 0; z-index: -1;
  background: var(--ink); transform: translateY(101%);
  transition: transform .45s var(--ease);
}
.on-night .btn::before { background: var(--on-night); }
@media (hover: hover) {
  .btn:hover { color: var(--cream); }
  .btn:hover::before { transform: translateY(0); }
  .on-night .btn:hover { color: var(--night); }
}
.btn:active { transform: scale(.985); }
.btn svg { width: 15px; height: 15px; flex: none; }

/* ─── Header ───────────────────────────────────────────────────────────── */

.header {
  position: fixed; inset: 0 0 auto 0; z-index: 50;
  height: var(--header-h);
  color: var(--ink);
  transition: color .5s var(--ease-soft), transform .6s var(--ease);
}
/* The scrolled bar is SOLID cream, not a tinted blur. A translucent bar changes
   colour with whatever passes under it, and the walking mark — which is line
   over paper — then shows its paper as a pale rectangle against a bar that is
   no longer cream. Opaque, the mark's paper and the bar are the same colour and
   the box disappears; over the hero the bar is clear and the mark reverses. */
.header::before {
  content: ''; position: absolute; inset: 0;
  background: var(--cream);
  border-bottom: 1px solid var(--line);
  opacity: 1; transition: opacity .5s var(--ease-soft);
}
/* Over the photograph the bar is clear, and the room keeps moving under it: the
   statement scrolls up through the mark, a lit lantern drifts behind the nav.
   A scrim painted on the HERO cannot help, because it scrolls away with the
   hero. This one is painted on the BAR, so it travels with the marks it is
   protecting and fades out at its own lower edge — a band of shade, not a
   panel. It is the only thing standing between the mark and the photograph. */
.header::after {
  content: ''; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background: linear-gradient(180deg, rgba(10, 14, 11, .6) 0%, rgba(10, 14, 11, .44) 42%, rgba(10, 14, 11, .16) 78%, rgba(10, 14, 11, 0) 100%);
  opacity: 0; transition: opacity .5s var(--ease-soft);
}
.header.is-over::after { opacity: 1; }
.header-in {
  position: relative; height: 100%;
  display: flex; align-items: center; justify-content: space-between;
}
/* Over the photograph the bar is clear and the type is light. At the very top
   of any page the bar is clear too and the marks sit at full size. */
.header.is-over { color: var(--on-night); }
.header.is-over::before, .header.is-top::before { opacity: 0; }
.header.is-hidden { transform: translateY(-100%); }

/* ONE mark, chosen by data-logo on <html> (site.js also takes ?logo=petal etc.
   so the three can be compared on a phone). Everything else is display:none, so
   the bar never carries a petal AND a wordmark. */
.brand {
  display: inline-flex; align-items: center;
  transform-origin: left center;
  /* The mark is 16pt of drawing; a thumb needs 44. The padding buys the target
     and the matching negative margin gives the layout back, so the bar is
     unchanged and the transform still pivots on the mark's own middle. */
  padding-block: 14px; margin-block: -14px;
  transition: transform .7s var(--ease), --mk-paper .5s var(--ease-soft);
}
.brand svg, .footer-mark svg { fill: currentColor; overflow: visible; }
.brand .mk { display: none; flex: none; }
[data-logo="wordmark"] .brand .mk-wordmark,
html:not([data-logo]) .brand .mk-wordmark { display: block; width: 108px; height: auto; }
[data-logo="petal"] .brand .mk-petal { display: block; width: 27px; height: auto; }
[data-logo="walking"] .brand .mk-walking { display: block; height: 36px; width: auto; }

.header.is-top .brand { transform: scale(var(--brand-top)); }

/* The petals take their colours on cream and go to one ink over a photograph.
   The paths live in the sprite, so the colours travel down as custom properties
   (selectors cannot reach through <use>, inherited properties can). */
.pt-green { fill: var(--petal-green, currentColor); }
.pt-peach { fill: var(--petal-peach, currentColor); }
.pt-brown { fill: var(--petal-brown, currentColor); }
.brand { --petal-green: #00853f; --petal-peach: #e5b7a0; --petal-brown: #6c4f3b; --mk-paper: var(--cream); }
.header.is-over .brand {
  --petal-green: currentColor; --petal-peach: currentColor; --petal-brown: currentColor;
  /* Over the photograph the line goes cream and the paper goes to a warm shade
     of the room, so the mark reads as drawn on the picture, not stuck to it. */
  --mk-paper: rgba(12, 9, 5, .76);
}

.nav { display: none; }
.nav a {
  font-size: .9375rem; font-weight: 500; padding: 6px 0;
  background: linear-gradient(currentColor, currentColor) 0 100% / 0 1px no-repeat;
  transition: background-size .5s var(--ease);
}
.nav a[aria-current="page"] { background-size: 100% 1px; }
@media (hover: hover) { .nav a:hover { background-size: 100% 1px; } }
.nav-toggle {
  display: inline-flex; align-items: center; min-height: 44px; padding-left: 16px;
  font-size: .9375rem; font-weight: 500;
}

@media (min-width: 900px) {
  :root { --header-h: 84px; --brand-top: 1.22; }
  [data-logo="wordmark"] .brand .mk-wordmark,
  html:not([data-logo]) .brand .mk-wordmark { width: 122px; }
  [data-logo="petal"] .brand .mk-petal { width: 31px; }
  [data-logo="walking"] .brand .mk-walking { height: 41px; }
  .nav { display: flex; align-items: center; gap: clamp(22px, 2.6vw, 40px); }
  .nav-toggle { display: none; }
}

/* Full-screen menu (phones). */
.menu {
  position: fixed; inset: 0; z-index: 60;
  background: var(--cream); color: var(--ink);
  display: flex; flex-direction: column;
  padding: 0 var(--gutter) calc(28px + env(safe-area-inset-bottom));
  visibility: hidden; opacity: 0;
  transition: opacity .5s var(--ease-soft), visibility 0s linear .5s;
}
.menu.is-open { visibility: visible; opacity: 1; transition: opacity .5s var(--ease-soft), visibility 0s; }
.menu-top { height: var(--header-h); display: flex; align-items: center; justify-content: space-between; flex: none; }
/* The links take the middle of the sheet rather than hanging off the bar with
   a hundred points of nothing under them; the address block keeps the foot. */
.menu-links { margin-block: auto; padding-block: clamp(24px, 6vh, 56px); display: grid; gap: 4px; }
.menu-links a {
  display: block; padding: 8px 0;
  font-family: var(--display); font-weight: 300; letter-spacing: -.03em;
  font-size: clamp(2.25rem, 10vw, 3rem); line-height: 1.12;
  opacity: 0; transform: translateY(16px);
  transition: opacity .6s var(--ease), transform .7s var(--ease);
}
.menu.is-open .menu-links a { opacity: 1; transform: none; transition-delay: calc(.08s + var(--i) * .06s); }
.menu-foot { display: grid; gap: 6px; color: var(--ink-2); font-size: .9375rem; opacity: 0; transition: opacity .6s var(--ease) .35s; }
.menu.is-open .menu-foot { opacity: 1; }
.menu-foot a { color: var(--ink); display: inline-block; padding-block: 12px; margin-block: -12px; }

/* ─── Hero ─────────────────────────────────────────────────────────────── */

.hero {
  position: relative; isolation: isolate;
  min-height: 100vh; min-height: 100svh;
  display: grid; grid-template-rows: 1fr auto;
  color: var(--on-night); background: var(--night);
  overflow: hidden;
}
.hero-media { position: absolute; inset: -6% 0 -6% 0; z-index: -2; will-change: transform; }
.hero-media picture { display: block; width: 100%; height: 100%; }
.hero-media img {
  width: 100%; height: 100%; object-fit: cover; object-position: 50% 40%;
  transform: scale(1.08); opacity: 0;
  transition: transform 2.2s var(--ease), opacity 1.3s var(--ease-soft);
}
.is-ready .hero-media img { transform: none; opacity: 1; }
/* Two scrims, each doing one job, so neither has to be heavy enough to grey the
   room out: a soft pool under the statement and its two actions, and a foot so
   the address and the open line read. The band that keeps the bar legible is
   NOT here — it belongs to the bar, and travels with it. */
.hero::after {
  content: ''; position: absolute; inset: 0; z-index: -1;
  background:
    linear-gradient(180deg, rgba(10, 14, 11, .3) 0%, rgba(10, 14, 11, .14) 16%, rgba(10, 14, 11, .14) 46%, rgba(10, 14, 11, .42) 76%, rgba(10, 14, 11, .84) 100%),
    radial-gradient(78% 46% at 50% 50%, rgba(10, 14, 11, .58), rgba(10, 14, 11, .3) 58%, rgba(10, 14, 11, 0) 78%);
}
.hero-body {
  align-self: center; justify-self: center; text-align: center;
  padding: calc(var(--header-h) + 56px) var(--gutter) 40px;
  max-width: 54rem;
}
/* On a wide screen the room's brightest lantern hangs right of centre, and a
   dead-centred statement runs its first line straight across it. Walking the
   block a little off centre puts every line back on the dark panelling and
   gives the cascade of lanterns the right-hand side to itself: the photograph
   and the sentence stop competing for the same 200 points of the frame. */
@media (min-width: 1000px) and (min-aspect-ratio: 1/1) {
  .hero-body { margin-inline-end: clamp(0px, 10vw, 170px); }
}
/* The size is tuned to the SENTENCE, not to a scale. At the old one the owner's
   statement fell to seven lines on a 360–375pt phone, which is one line past
   the limit where a browser will balance a block, and it left "service." alone
   on the last line. A shade smaller and it comes in at six balanced lines from
   360 up, five from 414, and it reaches its ceiling sooner on a laptop, where
   it now reads LARGER than before. */
.hero-statement {
  font-family: var(--display); font-weight: 300; letter-spacing: -.03em;
  font-size: clamp(1.25rem, .74rem + 2.8vw, 2.5rem); line-height: 1.24;
  text-wrap: balance;
  /* One lantern in the photograph reads at 0.87 luminance, and on a wide screen
     the end of the first line crosses it: cream type on a lit paper shade, with
     the en dash all but gone. A scrim heavy enough to fix that would grey out
     the whole room, so the darkening travels WITH the letters instead — a wide,
     very soft halo that is invisible over the dark of the room and only shows
     where a glyph happens to cross something bright. */
  text-shadow: 0 1px 10px rgba(10, 14, 11, .5), 0 0 34px rgba(10, 14, 11, .42);
}
.hero-actions { margin-top: clamp(28px, 4vw, 44px); display: flex; flex-wrap: wrap; gap: 14px 30px; justify-content: center; align-items: center; }
.hero-foot {
  display: flex; justify-content: space-between; align-items: flex-end; gap: 24px;
  padding: 0 var(--gutter) calc(26px + env(safe-area-inset-bottom));
  font-size: .9375rem; line-height: 1.45;
}
.hero-foot p + p { text-align: right; }
/* The open line is written by the server and can be anything from "Open now"
   to "Opens Wednesday at 8:30 am". On a phone there is no width to gamble on,
   so the two blocks stack and both read from the left margin. */
@media (max-width: 519px) {
  .hero-foot { display: grid; justify-items: start; gap: 8px; }
  .hero-foot p + p { text-align: left; }
}
.hero-foot .dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; background: #7fbf8a; margin-right: 9px; transform: translateY(-1px); transition: background .4s; }
.hero-foot .is-closed .dot { background: rgba(243, 240, 233, .45); }

/* Load-in: the statement arrives after the room does. */
.rise { opacity: 0; transform: translateY(18px); transition: opacity 1s var(--ease), transform 1.1s var(--ease); transition-delay: calc(.3s + var(--i, 0) * .12s); }
.is-ready .rise { opacity: 1; transform: none; }

/* ─── Scroll reveal ────────────────────────────────────────────────────── */

[data-reveal] { opacity: 0; transform: translateY(22px); transition: opacity .85s var(--ease), transform .95s var(--ease); transition-delay: calc(var(--d, 0) * 1ms); }
[data-reveal].is-in { opacity: 1; transform: none; }

/* Photographs settle rather than slide: a slow de-zoom behind a fixed frame. */
.frame { position: relative; overflow: hidden; background: #e9e5dc; border-radius: var(--radius); }
/* …except where the photograph runs off the edge of the page. */
.app-photo, .story-photo { border-radius: 0; }
.frame picture { display: block; width: 100%; height: 100%; }
.frame img { width: 100%; height: 100%; object-fit: cover; transform: scale(1.07); transition: transform 1.8s var(--ease); }
[data-reveal].is-in .frame img, .frame[data-reveal].is-in img { transform: none; }
@media (hover: hover) {
  .frame.zoom img { transition: transform 1.8s var(--ease); }
  [data-reveal].is-in .frame.zoom:hover img, .frame.zoom[data-reveal].is-in:hover img { transform: scale(1.035); }
}

/* ─── The space ────────────────────────────────────────────────────────── */

.space { padding-block: var(--section); }
.space-grid { display: grid; gap: clamp(40px, 7vw, 96px); align-items: center; }
.space-copy { max-width: 31rem; }
.space-copy .lede { margin-top: 22px; }
.space-copy .link { margin-top: 30px; }
/* Two plates, the smaller one stepped down and across the corner of the larger
   with a cream margin between them. On a phone the step is shallower, so the
   big photograph keeps its subject instead of losing a third of it. */
.space-photos { position: relative; display: grid; grid-template-columns: repeat(12, 1fr); padding-bottom: 11%; }
.space-photos .a { grid-column: 1 / 11; grid-row: 1; aspect-ratio: 4 / 5; }
.space-photos .b { grid-column: 9 / 13; grid-row: 1; aspect-ratio: 3 / 4; align-self: end; margin-bottom: -11%; z-index: 1; box-shadow: 0 0 0 8px var(--cream); }
@media (min-width: 900px) {
  .space-grid { grid-template-columns: minmax(0, 5fr) minmax(0, 6fr); }
  .space-copy { justify-self: start; }
  .space-photos { padding-bottom: 13%; }
  .space-photos .a { grid-column: 1 / 10; }
  .space-photos .b { grid-column: 8 / 13; margin-bottom: -13%; box-shadow: 0 0 0 10px var(--cream); }
}

/* ─── Drinks mosaic ────────────────────────────────────────────────────── */

.drinks { padding-block: var(--section); }

/* A gallery wall, not a collage. Three sizes (portrait, square, and the two
   captioned drinks at twice the width), one gap everywhere, outer edges that
   line up, and the copy hung in the middle of the top row with a photograph on
   either side. Nothing is nudged out of the grid: the air is the composition. */
.mosaic {
  --gap: clamp(17px, 4.4vw, 22px);
  display: grid; gap: var(--gap);
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
.mosaic-copy { grid-column: 1 / -1; text-align: center; padding: 0 4px clamp(34px, 8vw, 52px); max-width: 34rem; justify-self: center; }
.mosaic-copy .lede { margin-top: 20px; }
.mosaic-copy .link { margin-top: 26px; }

.tile { margin: 0; min-width: 0; }
/* Every tile opens the menu (menu.fressocafe.com); the link adds nothing to see. */
.tile-link { display: block; color: inherit; }
.tile .frame { width: 100%; }
.tile.portrait .frame { aspect-ratio: 4 / 5; }
.tile.square .frame { aspect-ratio: 1 / 1; }
.tile.feature { grid-column: 1 / -1; }
.tile.feature .frame { aspect-ratio: 5 / 4; }

/* A gallery label, and it has to read as ONE thing with the photograph above
   it. The grid gap alone cannot do that: it is the same distance on both
   sides, so the label ends up as near to the next row as to its own picture.
   The caption is therefore tight under its plate and the whole captioned
   figure carries its own margin below, which buys the separation back. */
.tile.feature { margin-bottom: clamp(20px, 4.5vw, 34px); }
.tile figcaption { padding-top: 15px; max-width: 34rem; }
.tile figcaption .display-s { font-size: clamp(1.3125rem, 1.1rem + .7vw, 1.625rem); }
.tile figcaption p { margin-top: 7px; color: var(--ink-2); font-size: .9375rem; line-height: 1.55; max-width: 53ch; text-wrap: pretty; }
/* Tiles arrive a little more quietly than the rest of the page, and they arrive
   across the row rather than all at once: the wall fills left to right. The
   cascade is set HERE and not on each figure, because the rows are a pair on a
   phone and a band of four on a laptop, and the two want different counts. */
.mosaic [data-reveal] { transform: translateY(18px); }
.mosaic [data-reveal].is-in { transform: none; }
.mosaic .s2, .mosaic .s4, .mosaic .s6 { --d: 100; }

@media (min-width: 700px) {
  .mosaic { --gap: clamp(22px, 3vw, 38px); }
}

/* Wide: twelve columns, three bands. The copy is flanked, the two captioned
   drinks hold the middle at full weight, and four squares close it. Below this
   the two-column phone wall would stand five screens tall on a laptop, so the
   band layout starts as soon as twelve columns are wide enough to hold it. */
@media (min-width: 1000px) {
  .mosaic { grid-template-columns: repeat(12, minmax(0, 1fr)); }
  .mosaic-copy { grid-column: 4 / 10; grid-row: 1; align-self: center; max-width: none; padding: 0 clamp(6px, 1.2vw, 22px); }
  .tile.feature { grid-column: auto; margin-bottom: 0; }
  .tile.feature .frame { aspect-ratio: 4 / 3; }
  .mosaic .s1 { grid-column: 1 / 4;   grid-row: 1; align-self: center; }
  .mosaic .s2 { grid-column: 10 / 13; grid-row: 1; align-self: center; }
  .mosaic .f1 { grid-column: 1 / 7;   grid-row: 2; }
  .mosaic .f2 { grid-column: 7 / 13;  grid-row: 2; }
  .mosaic .s3 { grid-column: 1 / 4;   grid-row: 3; }
  .mosaic .s4 { grid-column: 4 / 7;   grid-row: 3; }
  .mosaic .s5 { grid-column: 7 / 10;  grid-row: 3; }
  .mosaic .s6 { grid-column: 10 / 13; grid-row: 3; }
  /* Row two carries the labels, so the band below it stands off by more than a
     gap; the grid rows do it once for both captions. */
  .mosaic { row-gap: var(--gap); }
  .mosaic .f1, .mosaic .f2 { padding-bottom: clamp(14px, 1.6vw, 26px); }
  /* Four across, so the cascade is four beats and not two. */
  .mosaic .s2 { --d: 120; }
  .mosaic .f2 { --d: 120; }
  .mosaic .s3 { --d: 0; }
  .mosaic .s4 { --d: 90; }
  .mosaic .s5 { --d: 180; }
  .mosaic .s6 { --d: 270; }
}

/* ─── The app ──────────────────────────────────────────────────────────── */

.on-night { background: var(--night); color: var(--on-night); }
.on-night .lede, .on-night .muted { color: var(--on-night-2); }

/* This section's ground is sampled from the photograph's own dark edge: a warm
   near-black, because the room in it is lit by tungsten and the site's --night
   is a green-black. With the two hues agreed, the picture can be DISSOLVED into
   the page with an eased mask instead of hidden under a scrim, and there is no
   seam left to see. The ramp follows smoothstep, so it has no shoulder to band. */
.app {
  position: relative; isolation: isolate; overflow: hidden;
  --app-night: #17110a;
  --app-night-0: rgba(23, 17, 10, 0);
}
.app.on-night { background: var(--app-night); }
.app-grid { display: grid; }
.app-photo {
  position: relative; aspect-ratio: 1 / 1.02; overflow: hidden;
  /* Phones: the picture sits above the words and dissolves downward. */
  --fade: linear-gradient(180deg,
    #000 0%, #000 68%,
    rgba(0, 0, 0, .972) 71.2%, rgba(0, 0, 0, .896) 74.4%, rgba(0, 0, 0, .784) 77.6%,
    rgba(0, 0, 0, .648) 80.8%, rgba(0, 0, 0, .5) 84%, rgba(0, 0, 0, .352) 87.2%,
    rgba(0, 0, 0, .216) 90.4%, rgba(0, 0, 0, .104) 93.6%, rgba(0, 0, 0, .028) 96.8%,
    rgba(0, 0, 0, 0) 100%);
  -webkit-mask-image: var(--fade); mask-image: var(--fade);
}
.app-photo picture { display: block; width: 100%; height: 100%; }
.app-photo img { width: 100%; height: 100%; object-fit: cover; object-position: 52% 50%; }
/* A breath of the ground along the edge the mask does not reach. */
.app-photo::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(180deg, var(--app-night) 0%, var(--app-night-0) 13%);
}
/* The words in a bleeding section still start on the page's own left edge. */
.app-copy { padding: clamp(36px, 8vw, 56px) var(--edge) var(--section); max-width: calc(38rem + var(--edge)); }
.app-copy .lede { margin-top: 22px; }
.app-list { margin-top: 30px; border-top: 1px solid var(--line-night); }
.app-list li { padding: 15px 0; border-bottom: 1px solid var(--line-night); display: flex; justify-content: space-between; gap: 20px; align-items: baseline; }
.app-list li span:last-child { color: var(--on-night-2); font-size: .9375rem; text-align: right; }
.app-cta { margin-top: 36px; display: flex; flex-wrap: wrap; align-items: center; gap: 16px 24px; }
.app-cta .small { color: var(--on-night-2); }

@media (min-width: 1000px) {
  .app-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1.16fr); align-items: stretch; min-height: min(92vh, 880px); }
  .app-copy { order: -1; align-self: center; justify-self: end; padding: var(--section) clamp(40px, 6vw, 96px) var(--section) var(--edge); width: 100%; max-width: none; }
  .app-photo { aspect-ratio: auto; height: 100%; min-height: 640px; }
  /* Wide: the picture dissolves sideways, toward the words. The crop is set so
     that the whole of the ramp falls on out-of-focus room and the ramp is over
     before it reaches the arm, which is where a fade turns muddy. */
  .app-photo img { object-position: 20% 50%; }
  .app-photo {
    --fade: linear-gradient(90deg,
      rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, .028) 2.4%, rgba(0, 0, 0, .104) 4.8%,
      rgba(0, 0, 0, .216) 7.2%, rgba(0, 0, 0, .352) 9.6%, rgba(0, 0, 0, .5) 12%,
      rgba(0, 0, 0, .648) 14.4%, rgba(0, 0, 0, .784) 16.8%, rgba(0, 0, 0, .896) 19.2%,
      rgba(0, 0, 0, .972) 21.6%, #000 24%, #000 100%);
  }
  .app-photo::after {
    background: linear-gradient(180deg, var(--app-night) 0%, var(--app-night-0) 11%, var(--app-night-0) 89%, var(--app-night) 100%);
  }
}

/* ─── Visit ────────────────────────────────────────────────────────────── */

.visit { padding-block: var(--section); }
.visit-head { display: grid; gap: 18px; align-items: end; margin-bottom: clamp(36px, 6vw, 64px); }
@media (min-width: 900px) { .visit-head { grid-template-columns: 1fr auto; } }

/* The filmstrip. Native scrolling with snap, so a thumb does what a thumb
   expects; the buttons only scroll it. It takes any number of photographs. */
.strip {
  display: flex; gap: clamp(12px, 1.4vw, 18px);
  overflow-x: auto; overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory; -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  padding-inline: var(--edge);
  scroll-padding-inline: var(--edge);
  cursor: grab;
  /* Whatever happens to be peeking past the right-hand margin dissolves into
     the page instead of being guillotined. The ramp lives entirely inside the
     right-hand padding, so at the end of the strip — where the last photograph
     stops exactly at that margin — there is nothing left for it to touch. It
     works for three photographs and it works for nine, which is the point: the
     strip takes any number. */
  --strip-mask: linear-gradient(90deg, #000 0, #000 calc(100% - var(--edge)), rgba(0, 0, 0, .1) 100%);
  -webkit-mask-image: var(--strip-mask); mask-image: var(--strip-mask);
}
/* A mask clips to the border box, and an outline drawn outside it would be
   clipped away with the overflow; the strip's ring is therefore drawn inside. */
.strip:focus-visible { outline-offset: -3px; }
.strip::-webkit-scrollbar { display: none; }
.strip.is-dragging { cursor: grabbing; scroll-snap-type: none; scroll-behavior: auto; }
.strip.is-dragging img { pointer-events: none; }
/* Phones: every photograph is one tall card a little narrower than the screen,
   so the next one always shows at the edge. Wider screens: one shared height,
   and each photograph keeps its own proportions. */
.strip figure { flex: none; scroll-snap-align: start; width: 80vw; aspect-ratio: 4 / 5; }
.strip .frame { width: 100%; height: 100%; }
.strip img { -webkit-user-drag: none; user-select: none; }
@media (min-width: 760px) {
  .strip figure { width: auto; height: clamp(380px, 62vh, 640px); }
  .strip figure.r-tall { aspect-ratio: 3 / 4; }
  .strip figure.r-wide { aspect-ratio: 3 / 2; }
}

/* The count belongs with the arrows it counts, not marooned at the far side of
   the page from them. One quiet group, hung on the right margin. */
.strip-ctrl { display: flex; align-items: center; justify-content: flex-end; gap: 20px; margin-top: 20px; }
.strip-count { font-size: .875rem; color: var(--ink-3); font-variant-numeric: tabular-nums; letter-spacing: .01em; }
.strip-btns { display: flex; gap: 10px; }
.strip-btn {
  width: 48px; height: 48px; display: grid; place-items: center;
  border: 1px solid var(--line); color: var(--ink);
  transition: border-color .3s, color .3s, background .4s var(--ease);
}
.strip-btn svg { width: 16px; height: 16px; }
.strip-btn[disabled] { color: var(--ink-3); border-color: rgba(26, 26, 26, .08); opacity: .5; cursor: default; }
@media (hover: hover) { .strip-btn:not([disabled]):hover { background: var(--ink); color: var(--cream); border-color: var(--ink); } }

.visit-info {
  margin-top: clamp(48px, 7vw, 88px);
  display: grid; gap: 0;
  border-top: 1px solid var(--line);
}
.visit-col { padding: 28px 0 32px; border-bottom: 1px solid var(--line); }
.visit-col h3 { font-family: var(--display); font-weight: 300; letter-spacing: -.03em; font-size: 1.5rem; line-height: 1.2; margin-bottom: 16px; }
.visit-col address { font-style: normal; }
.visit-col .btn { margin-top: 22px; }
.status { display: flex; align-items: center; gap: 9px; margin-bottom: 14px; min-height: 1.6em; }
.status .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--green); flex: none; }
.status.is-closed .dot { background: var(--ink-3); }
.hours { width: 100%; max-width: 22rem; border-collapse: collapse; }
.hours th, .hours td { padding: 5px 0; text-align: left; font-weight: 400; vertical-align: top; }
.hours td { text-align: right; color: var(--ink-2); font-variant-numeric: tabular-nums; white-space: nowrap; }
.hours tr.is-today th, .hours tr.is-today td { font-weight: 500; color: var(--ink); }
/* The gap is the height of the invisible hit area each link carries, so three
   44px targets stack without ever overlapping one another. */
.contact-list { display: grid; gap: 16px; }
@media (min-width: 900px) {
  .visit-info { grid-template-columns: 1.1fr 1.1fr .9fr; }
  .visit-col { padding: 36px 40px 56px 0; border-bottom: 0; }
  .visit-col + .visit-col { padding-left: 40px; border-left: 1px solid var(--line); }
}

/* ─── From our Instagram ───────────────────────────────────────────────────
   Six framed prints on the wall: a hairline, a wide cream mat, and the faintest
   shadow so the frame sits a millimetre off the page. Phones scroll them with
   the next frame showing at the edge, the way the Visit strip does; wide
   screens hang all six in one row. The section is built by site.js and stays
   out of the page entirely until the feed answers. */

[hidden] { display: none !important; }

.ig { padding-block: var(--section); }
.ig-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 18px 28px; margin-bottom: clamp(34px, 5vw, 58px); }
.ig-head .link { font-size: .9375rem; }

.ig-rail {
  display: flex; gap: clamp(16px, 2.2vw, 30px);
  overflow-x: auto; overflow-y: hidden; overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory; -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  padding-inline: var(--edge);
  scroll-padding-inline: var(--edge);
  /* Room under the frames for the shadow and for the caption to step into. */
  padding-block: 4px 32px;
}
.ig-rail::-webkit-scrollbar { display: none; }
.ig-item { position: relative; flex: none; scroll-snap-align: start; width: min(72vw, 340px); }

/* A mounted print: paper a shade above the page, a mat wide enough to read as
   a mat, a heavier lower margin the way a framer cuts one, one hairline, and a
   shadow that only says the frame is a millimetre off the wall. */
.ig-frame {
  display: block;
  --mat: clamp(15px, 1.5vw, 22px);
  background: var(--paper);
  border: 1px solid rgba(26, 26, 26, .17);
  padding: var(--mat) var(--mat) calc(var(--mat) * 1.45);
  box-shadow: 0 1px 2px rgba(26, 26, 26, .05), 0 18px 34px -24px rgba(26, 26, 26, .5);
  transition: transform .6s var(--ease), box-shadow .6s var(--ease), border-color .6s var(--ease);
}
.ig-shot { display: block; position: relative; overflow: hidden; aspect-ratio: 4 / 5; background: #e9e5dc; }
.ig-shot img { width: 100%; height: 100%; object-fit: cover; transform: scale(1.05); transition: transform 1.6s var(--ease), opacity 1s var(--ease); }
.ig-item.is-in .ig-shot img { transform: none; }
/* The caption waits underneath, in reserved space, and only steps forward when
   a pointer asks for it. */
.ig-cap {
  position: absolute; left: 1px; right: 1px; top: calc(100% + 10px);
  font-size: .875rem; line-height: 1.4; color: var(--ink-3);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  opacity: 0; transform: translateY(-5px);
  transition: opacity .45s var(--ease), transform .55s var(--ease);
}
@media (hover: hover) {
  .ig-frame:hover { transform: translateY(-6px); box-shadow: 0 1px 2px rgba(26, 26, 26, .05), 0 28px 46px -26px rgba(26, 26, 26, .58); border-color: rgba(26, 26, 26, .27); }
  .ig-frame:hover ~ .ig-cap { opacity: 1; transform: none; }
}
.ig-frame:focus-visible ~ .ig-cap { opacity: 1; transform: none; }
/* A thumb has no hover, so there is no caption to wait for and no strip of
   empty page under the frames pretending otherwise. */
@media (hover: none) {
  .ig-cap { display: none; }
  .ig-rail { padding-block: 4px 10px; }
}

/* Six prints in one row would be six postage stamps on a laptop. Three across,
   hung in two rows, gives each one a mat you can actually see. */
@media (min-width: 900px) {
  .ig-rail {
    display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: clamp(30px, 3.4vw, 54px) clamp(26px, 3vw, 46px);
    overflow: visible; scroll-snap-type: none;
    padding-block: 4px 0;
  }
  .ig-item { width: auto; }
}

/* ─── Questions ────────────────────────────────────────────────────────── */

.faq { padding-block: var(--section); }
.faq-grid { display: grid; gap: clamp(28px, 5vw, 48px); }
@media (min-width: 900px) { .faq-grid { grid-template-columns: minmax(0, 4fr) minmax(0, 7fr); align-items: start; } .faq-grid > :first-child { position: sticky; top: calc(var(--header-h) + 32px); } }
.qa { border-top: 1px solid var(--ink); }
.qa-item { border-bottom: 1px solid var(--line); }
.qa-item:last-child { border-bottom-color: var(--ink); }
.qa-q {
  width: 100%; display: flex; justify-content: space-between; align-items: center; gap: 24px;
  padding: 22px 0; text-align: left; font-weight: 500; font-size: 1.0625rem; line-height: 1.35;
}
.qa-icon { position: relative; width: 15px; height: 15px; flex: none; }
.qa-icon::before, .qa-icon::after { content: ''; position: absolute; left: 0; right: 0; top: 50%; height: 1px; background: currentColor; transition: transform .6s var(--ease), opacity .4s; }
.qa-icon::after { transform: rotate(90deg); }
.qa-item.is-open .qa-icon::after { transform: rotate(0deg); opacity: 0; }
.qa-item.is-open .qa-icon::before { transform: rotate(180deg); }
/* Height animates through the grid row, so there is nothing to measure. */
.qa-a { display: grid; grid-template-rows: 0fr; transition: grid-template-rows .65s var(--ease); }
.qa-item.is-open .qa-a { grid-template-rows: 1fr; }
.qa-a > div { overflow: hidden; min-height: 0; }
.qa-a p { padding: 0 clamp(20px, 6vw, 44px) 26px 0; color: var(--ink-2); max-width: 40rem; opacity: 0; transform: translateY(-6px); transition: opacity .45s var(--ease), transform .6s var(--ease); text-wrap: pretty; }
.qa-item.is-open .qa-a p { opacity: 1; transform: none; transition-delay: .12s; }
.qa-a a { color: var(--ink); text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 3px; }

/* ─── Footer ───────────────────────────────────────────────────────────── */

.footer { border-top: 1px solid var(--line); padding-block: clamp(56px, 8vw, 96px) calc(32px + env(safe-area-inset-bottom)); }
.footer-top { display: grid; gap: clamp(34px, 5vw, 48px); }
/* A signature, not a headline: the wordmark sits at reading size and the four
   columns take the width it gives back. */
.footer-mark { display: block; width: min(100%, 158px); color: var(--green); padding-block: 11px; margin-block: -11px; }
.footer-cols { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 36px 24px; }
.footer h3 { font-weight: 500; font-size: .9375rem; margin-bottom: 10px; }
.footer li, .footer address { font-style: normal; font-size: .9375rem; line-height: 1.5; color: var(--ink-2); }
.nw { white-space: nowrap; }
.footer li + li { margin-top: 5px; }
.footer li a { display: inline-block; padding-block: 5px; color: var(--ink); background: linear-gradient(currentColor, currentColor) 0 calc(100% - 5px) / 0 1px no-repeat; transition: background-size .45s var(--ease); }
@media (hover: hover) { .footer li a:hover { background-size: 100% 1px; } }
.footer-legal { margin-top: clamp(48px, 7vw, 88px); padding-top: 22px; border-top: 1px solid var(--line); display: grid; gap: 10px; font-size: .8125rem; line-height: 1.55; color: var(--ink-3); }
@media (min-width: 900px) {
  .footer-top { grid-template-columns: 158px minmax(0, 1fr); gap: 44px clamp(48px, 7vw, 108px); align-items: start; }
  .footer-mark { margin-top: 4px; }
  .footer-cols { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 36px clamp(24px, 3vw, 44px); }
  .footer-legal { grid-template-columns: 1fr auto; }
}

/* ─── Our Story ────────────────────────────────────────────────────────── */

.story { padding-top: calc(var(--header-h) + clamp(72px, 14vw, 200px)); padding-bottom: var(--section); }
.story-grid { display: grid; gap: clamp(48px, 8vw, 72px); }
.story-text { max-width: 30rem; }
.story-text h1 { margin-bottom: clamp(28px, 4vw, 44px); }
.story-text p { color: var(--ink); }
.story-text p + p { margin-top: 1.25em; }
.story-text .signoff { margin-top: 2em; font-weight: 500; }
.story-mark { color: var(--green); width: min(56vw, 250px); justify-self: start; }
.story-mark path { stroke-linecap: round; stroke-dasharray: 1; stroke-dashoffset: 1; transition: stroke-dashoffset 2.6s var(--ease) .3s; }
.is-ready .story-mark path { stroke-dashoffset: 0; }
.story-mark path:nth-child(2) { transition-delay: .5s; }
.story-mark path:nth-child(3) { transition-delay: .7s; }
.story-mark path:nth-child(4) { transition-delay: .9s; }
@media (min-width: 900px) {
  .story-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 30rem) minmax(0, 1fr) minmax(0, 18rem) minmax(0, .6fr); align-items: center; }
  .story-text { grid-column: 2; }
  .story-mark { grid-column: 4; width: 100%; justify-self: center; }
}
.story-photo { height: clamp(360px, 78vh, 820px); }
.story-photo img { object-position: 50% 62%; }
.story-next { padding-block: var(--section); text-align: center; }
.story-next .display { max-width: 18ch; margin-inline: auto; }
.story-next .hero-actions { margin-top: 36px; }

/* ─── Not found ────────────────────────────────────────────────────────── */
.lost { min-height: 78vh; min-height: 78svh; display: grid; place-items: center; text-align: center; padding: calc(var(--header-h) + var(--section)) var(--gutter) var(--section); }
.lost .lede { margin: 18px auto 34px; max-width: 26rem; text-wrap: balance; }

/* ─── Reduced motion ───────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { transition-duration: .01ms !important; transition-delay: 0s !important; animation-duration: .01ms !important; }
  [data-reveal], .rise { opacity: 1; transform: none; }
  .frame img, .hero-media img { transform: none; opacity: 1; }
  .story-mark path { stroke-dashoffset: 0; }
  @view-transition { navigation: none; }
}

/* Without JavaScript nothing is hidden waiting to be revealed. */
.no-js [data-reveal], .no-js .rise { opacity: 1; transform: none; }
.no-js .frame img, .no-js .hero-media img { transform: none; opacity: 1; }
.no-js .story-mark path { stroke-dashoffset: 0; }
.no-js .qa-a { grid-template-rows: 1fr; }
.no-js .qa-a p { opacity: 1; transform: none; }
