/* Happiness Corner Cafe
   Palette taken from the cafe's own branding: espresso browns, caramel,
   cream, and the warm amber of the "good coffee good mood" neon sign. */

:root {
  --espresso: #1B110B;
  --espresso-2: #2A1B12;
  --bark: #3D2A1D;
  --caramel: #C8873F;
  --amber: #F0B458;
  --cream: #F7F1E8;
  --cream-2: #EFE4D6;
  --ink: #221710;
  --muted: #7A6656;
  --line: rgba(34, 23, 16, 0.12);

  --wrap: 1180px;
  --r: 18px;
  --r-lg: 26px;

  /* Three roles, kept strictly separate: Fraunces sets display type, Inter
     runs prose, and the mono owns every label, control and ticket line. The
     label layer is deliberately never Inter-uppercase-letterspaced. */
  --display: "Fraunces", Georgia, "Times New Roman", serif;
  --body: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  --ease: cubic-bezier(0.22, 1, 0.36, 1);
}

* { box-sizing: border-box; margin: 0; padding: 0; }

/* Lenis owns scrolling, so native smooth-scroll must stay off or the two
   scrollers fight each other. The no-motion class is the fallback path. */
html.no-motion { scroll-behavior: smooth; }
html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }
.lenis.lenis-stopped { overflow: hidden; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: 0.001ms !important; transition-duration: 0.001ms !important; }
}

/* ---------- word masking for headlines ---------- */

.w {
  display: inline-block;
  overflow: hidden;
  vertical-align: top;
  /* Descenders (g, y, p) would be clipped by the mask without this. */
  padding-bottom: 0.14em;
  margin-bottom: -0.14em;
}
.wi { display: inline-block; will-change: transform; }

/* ---------- intro curtain ---------- */

/* The curtain is opaque and covers everything, so it only exists once the
   inline head script proves JS is alive to take it away again. Without JS
   it never renders and the page is readable immediately. */
.curtain { display: none; }
html.js .curtain {
  position: fixed; inset: 0; z-index: 90;
  background: var(--espresso);
  display: flex; align-items: center; justify-content: center;
  /* Second net: if JS is on but main.js fails, this still frees the page.
     Animates opacity, not visibility, because visibility in a keyframe is
     not applied reliably. */
  animation: curtain-failsafe 0.6s linear 5s forwards;
}
@keyframes curtain-failsafe {
  to { opacity: 0; pointer-events: none; visibility: hidden; }
}
.curtain-word {
  font-family: var(--display); font-size: clamp(1.8rem, 5vw, 3.4rem);
  font-weight: 600; letter-spacing: -0.02em; color: var(--cream);
  opacity: 0; transform: translateY(18px);
}
html.no-motion .curtain { display: none !important; }

/* ---------- custom cursor ---------- */

.cursor { position: fixed; inset: 0; pointer-events: none; z-index: 95; display: none; }
.cursor.on { display: block; }
.cursor-dot, .cursor-ring {
  position: fixed; top: 0; left: 0;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  will-change: transform;
  mix-blend-mode: difference;
}
.cursor-dot { width: 7px; height: 7px; background: var(--cream); margin: -3.5px 0 0 -3.5px; }
.cursor-ring {
  width: 34px; height: 34px; margin: -17px 0 0 -17px;
  border: 1px solid rgba(247, 241, 232, 0.6);
  transition: width 0.3s var(--ease), height 0.3s var(--ease),
              margin 0.3s var(--ease), background 0.3s var(--ease);
}
.cursor.hot .cursor-ring {
  width: 62px; height: 62px; margin: -31px 0 0 -31px;
  background: rgba(247, 241, 232, 0.14);
}
/* The real pointer stays visible on touch and reduced-motion. */
@media (hover: hover) and (pointer: fine) {
  .cursor.on ~ * a, .cursor.on ~ * button { cursor: none; }
}

body {
  background: var(--cream);
  color: var(--ink);
  font-family: var(--body);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img { display: block; max-width: 100%; }
a { color: inherit; }

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

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--espresso); color: var(--cream);
  padding: 12px 18px; border-radius: 0 0 10px 0;
}
.skip:focus { left: 0; }

:focus-visible { outline: 2px solid var(--caramel); outline-offset: 3px; border-radius: 4px; }

/* ---------- Buttons ---------- */

/* Two tiers only, and neither is a bordered box: primary is a filled ticket
   stub, secondary is bare text on a drawn rule. Lowercase throughout — the
   uppercase-letterspaced label is the loudest tell of a generated page, and
   dropping it is most of what makes this read as quiet rather than shouted.

   Nothing here animates transform on hover. .magnetic buttons have GSAP
   writing an inline transform every mousemove, and inline beats CSS, so a
   translateY hover would be dead on exactly the buttons that use it. Colour
   is the one channel GSAP is not driving. */
.btn {
  position: relative;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  font-family: var(--mono); font-size: 13px; font-weight: 400;
  letter-spacing: 0.01em; text-transform: lowercase;
  padding: 13px 20px;
  text-decoration: none; white-space: nowrap; cursor: pointer;
  border: 0; background: none;
  transition: background 0.28s var(--ease), color 0.28s var(--ease),
              border-color 0.28s var(--ease);
}

/* The stub. Two radial-gradient mask layers bite a notch out of each side,
   the way a coupon tears off. This only works because the button is a solid
   fill: a mask cuts a border apart, leaving the notch arc unstroked, which is
   why the secondary tier below is a bare link rather than an outlined box.
   Browsers without mask support simply get the rounded rectangle. */
.btn-primary {
  --notch: 5px;
  background: var(--espresso); color: var(--cream);
  border-radius: 5px;
  -webkit-mask:
    radial-gradient(circle var(--notch) at 0 50%, transparent 98%, #000 100%) left / 50.5% 100% no-repeat,
    radial-gradient(circle var(--notch) at 100% 50%, transparent 98%, #000 100%) right / 50.5% 100% no-repeat;
  mask:
    radial-gradient(circle var(--notch) at 0 50%, transparent 98%, #000 100%) left / 50.5% 100% no-repeat,
    radial-gradient(circle var(--notch) at 100% 50%, transparent 98%, #000 100%) right / 50.5% 100% no-repeat;
}
/* Must stay a fill, never transparent: the mask removed the border, so a
   transparent hover would erase the button entirely. */
.btn-primary:hover { background: var(--caramel); color: var(--espresso); }

/* The rule is drawn from the text metrics, not hung off the bottom of the
   box: .cta is a stretch flex row, so a border-bottom would sit as far below
   the words as the tallest sibling is tall. This keeps it hugging the text
   whatever the primary button's height happens to be. */
.btn-ghost {
  color: var(--ink);
  padding: 13px 4px;
}
.btn-ghost::after {
  content: ""; position: absolute;
  left: 4px; right: 4px; top: calc(50% + 0.72em);
  height: 1px; background: var(--caramel);
  transition: background 0.28s var(--ease);
}
.btn-ghost:hover { color: var(--caramel); }

/* The hero and match sections sit on dark photos, so the default
   dark-on-light button styles would disappear into them. Invert both. */
.hero .btn-primary,
.match .btn-primary { background: var(--cream); color: var(--espresso); }
.hero .btn-primary:hover,
.match .btn-primary:hover { background: var(--amber); color: var(--espresso); }
.hero .btn-ghost,
.match .btn-ghost { color: var(--cream); }
.hero .btn-ghost::after,
.match .btn-ghost::after { background: rgba(247, 241, 232, 0.5); }
.hero .btn-ghost:hover,
.match .btn-ghost:hover { color: var(--amber); }
.hero .btn-ghost:hover::after,
.match .btn-ghost:hover::after { background: var(--amber); }

.dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: #46C46B; box-shadow: 0 0 0 0 rgba(70, 196, 107, 0.6);
  animation: pulse 2.4s infinite;
}
.dot.closed { background: #C4564A; animation: none; }
@keyframes pulse {
  0%   { box-shadow: 0 0 0 0 rgba(70, 196, 107, 0.55); }
  70%  { box-shadow: 0 0 0 7px rgba(70, 196, 107, 0); }
  100% { box-shadow: 0 0 0 0 rgba(70, 196, 107, 0); }
}

/* ---------- Nav ---------- */

.nav {
  position: fixed; inset: 0 0 auto 0; z-index: 60;
  padding: 14px 0;
  transition: background 0.3s var(--ease), box-shadow 0.3s var(--ease), padding 0.3s var(--ease);
}
.nav.solid {
  background: rgba(247, 241, 232, 0.9);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  box-shadow: 0 1px 0 var(--line);
  padding: 8px 0;
}

.nav-in { display: flex; align-items: center; justify-content: space-between; gap: 22px; }

.brand { display: flex; align-items: center; gap: 11px; text-decoration: none; }
.brand-mark {
  width: 40px; height: 40px; border-radius: 50%; object-fit: cover;
  background: var(--cream); flex: none;
  box-shadow: 0 2px 10px rgba(27, 17, 11, 0.18);
}
.brand-text { display: flex; flex-direction: column; line-height: 1.05; }
.brand-text strong {
  font-family: var(--display); font-size: 19px; font-weight: 700;
  letter-spacing: -0.01em; color: var(--cream);
  transition: color 0.3s var(--ease);
}
.brand-text em {
  font-style: normal; font-size: 9.5px; font-weight: 600;
  letter-spacing: 0.34em; text-transform: uppercase;
  color: var(--amber);
  transition: color 0.3s var(--ease);
}
.nav.solid .brand-text strong { color: var(--ink); }
.nav.solid .brand-text em { color: var(--caramel); }

.nav-links { display: flex; gap: 30px; }
.nav-links a {
  position: relative; font-size: 14.5px; font-weight: 500;
  color: rgba(247, 241, 232, 0.86); text-decoration: none;
  transition: color 0.25s var(--ease);
}
.nav-links a::after {
  content: ""; position: absolute; left: 0; bottom: -5px;
  width: 100%; height: 1.5px; background: var(--caramel);
  transform: scaleX(0); transform-origin: right;
  transition: transform 0.35s var(--ease);
}
.nav-links a:hover::after { transform: scaleX(1); transform-origin: left; }
.nav.solid .nav-links a { color: var(--muted); }
.nav.solid .nav-links a:hover { color: var(--ink); }

/* Icon-only, so the accessible name lives on aria-label in the markup. The
   number itself still appears in full in the visit block and the footer. */
.call {
  position: relative; flex: none;
  width: 44px; height: 44px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%;
  border: 1px solid rgba(247, 241, 232, 0.28);
  color: var(--cream); text-decoration: none;
  transition: background 0.28s var(--ease), color 0.28s var(--ease),
              border-color 0.28s var(--ease);
}
.call .ico { width: 20px; height: 20px; overflow: visible; }
.call:hover { background: var(--cream); color: var(--espresso); border-color: var(--cream); }
.nav.solid .call { border-color: var(--line); color: var(--ink); }
.nav.solid .call:hover { background: var(--espresso); color: var(--cream); border-color: var(--espresso); }

/* The dot sits on the rim as a badge. The separating ring is an outline, not
   a box-shadow: the pulse keyframe animates box-shadow on every frame and
   would overwrite a shadow ring outright. Outline is the one channel free. */
.call .dot {
  position: absolute; top: 1px; right: 1px;
  outline: 2px solid var(--espresso);
  transition: outline-color 0.3s var(--ease);
}
.nav.solid .call .dot { outline-color: var(--cream); }

/* ---------- Hero ---------- */

.hero {
  position: relative; isolation: isolate;
  min-height: 100svh;
  display: flex; align-items: center;
  padding: 140px 0 90px;
  overflow: hidden;
}

/* post_05_c is pre-cropped free of the promo text, so it can be shown far
   more openly. A light blur remains because the source is only 640px wide
   and gets upscaled: it reads as depth rather than as a soft JPEG. */
.hero-bg {
  position: absolute; inset: -4%;
  background: url("/img/post_05_c.jpg") center 58% / cover no-repeat;
  filter: blur(3px) saturate(1.14) brightness(0.66);
  transform: scale(1.05);
  z-index: -2;
}
.hero-veil {
  position: absolute; inset: 0; z-index: -1;
  background:
    radial-gradient(120% 90% at 12% 45%, rgba(27, 17, 11, 0.9) 0%, rgba(27, 17, 11, 0.6) 42%, rgba(27, 17, 11, 0.28) 70%),
    linear-gradient(180deg, rgba(27, 17, 11, 0.78) 0%, rgba(27, 17, 11, 0.32) 34%, rgba(27, 17, 11, 0.86) 100%);
}

.hero-in { position: relative; color: var(--cream); }

.eyebrow {
  display: inline-flex; align-items: center; gap: 10px;
  font-family: var(--mono);
  font-size: 12px; font-weight: 400; letter-spacing: 0.02em;
  text-transform: lowercase; color: rgba(247, 241, 232, 0.72);
  margin-bottom: 26px;
}
.eyebrow .sep { color: var(--caramel); }
/* 5px to match the ticket stub. Soft enough not to feel rigid, nowhere near
   the 100px pill that made the first pass read as generated. */
.status {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 4px 11px; border-radius: 5px;
  background: rgba(247, 241, 232, 0.08);
  border: 1px solid rgba(247, 241, 232, 0.22);
  color: var(--cream);
}

.hero h1 {
  font-family: var(--display);
  font-size: clamp(2.6rem, 6.4vw, 5.1rem);
  font-weight: 600; line-height: 1.03; letter-spacing: -0.028em;
  max-width: 16ch;
  text-wrap: balance;
}
.hero h1 .accent {
  color: var(--amber); font-style: italic;
}

.lede {
  margin-top: 24px; max-width: 46ch;
  font-size: clamp(1rem, 1.6vw, 1.16rem);
  color: rgba(247, 241, 232, 0.82);
}

.cta { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 34px; }

.facts {
  list-style: none; display: flex; flex-wrap: wrap; gap: 44px;
  margin-top: 60px; padding-top: 30px;
  border-top: 1px solid rgba(247, 241, 232, 0.16);
}
.facts li { display: flex; flex-direction: column; gap: 3px; }
.facts strong {
  font-family: var(--display); font-size: 1.32rem; font-weight: 600;
  letter-spacing: -0.01em;
}
.facts span {
  font-family: var(--mono); font-size: 11px; letter-spacing: 0.04em;
  color: rgba(247, 241, 232, 0.6);
}

.scroll-hint {
  position: absolute; left: 50%; bottom: 26px; transform: translateX(-50%);
  font-family: var(--mono);
  font-size: 11px; letter-spacing: 0.02em; text-transform: lowercase;
  color: rgba(247, 241, 232, 0.5);
  padding-bottom: 26px;
}
.scroll-hint::after {
  content: ""; position: absolute; left: 50%; bottom: 0;
  width: 1px; height: 18px; background: linear-gradient(var(--caramel), transparent);
  animation: drop 2s var(--ease) infinite;
}
@keyframes drop {
  0%   { transform: translateY(-8px); opacity: 0; }
  40%  { opacity: 1; }
  100% { transform: translateY(8px); opacity: 0; }
}

/* ---------- Marquee strip ---------- */

.strip {
  background: var(--espresso); color: var(--cream);
  padding: 16px 0; overflow: hidden;
}
.marquee { display: flex; }
/* Spacing lives on the items, not as a flex gap, so the JS duplicate is an
   exact copy and the loop wraps without a visible seam. GSAP drives x. */
.marquee-in {
  display: flex; align-items: center;
  white-space: nowrap; will-change: transform;
}
.marquee-in span {
  font-family: var(--display); font-size: 1.02rem; font-weight: 400;
  font-style: italic; color: rgba(247, 241, 232, 0.9);
  margin-right: 26px;
}
.marquee-in i {
  width: 5px; height: 5px; border-radius: 50%;
  background: var(--caramel); flex: none; margin-right: 26px;
}
@keyframes slide { to { transform: translateX(-50%); } }
.no-motion .marquee-in { animation: slide 34s linear infinite; }

/* ---------- Sections ---------- */

.section { padding: 108px 0; }

.head { max-width: 640px; margin-bottom: 52px; }

.kicker {
  display: inline-flex; align-items: center; gap: 10px;
  font-family: var(--mono); font-size: 12px; font-weight: 400;
  letter-spacing: 0.02em; text-transform: lowercase;
  color: var(--caramel); margin-bottom: 14px;
}
.kicker::before {
  content: ""; flex: none; width: 20px; height: 1px; background: currentColor;
}
.kicker.light { color: var(--amber); }

h2 {
  font-family: var(--display);
  font-size: clamp(2rem, 4vw, 3.15rem);
  font-weight: 600; line-height: 1.06; letter-spacing: -0.026em;
  text-wrap: balance;
}

.sub { margin-top: 16px; color: var(--muted); font-size: 1.04rem; max-width: 52ch; }

/* Margin note, in the voice of whoever built the page rather than the voice
   of the cafe. Marks the parts that were guessed so the owner can see exactly
   what still needs their input. */
.seam {
  margin-top: 20px; max-width: 64ch;
  font-family: var(--mono); font-size: 11.5px; line-height: 1.8;
  color: var(--muted);
  border-left: 2px solid var(--caramel);
  padding: 2px 0 2px 14px;
}
.seam.light { color: rgba(247, 241, 232, 0.62); border-left-color: var(--amber); }

/* ---------- Menu grid ---------- */

.menu-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 18px;
  /* The ticket numbers come from CSS, not markup, so reordering or dropping
     a card renumbers the run automatically. */
  counter-reset: item;
}

.card {
  background: #fff; border-radius: var(--r-lg); overflow: hidden;
  border: 1px solid var(--line);
  display: flex; flex-direction: column;
  counter-increment: item;
  transition: transform 0.4s var(--ease), box-shadow 0.4s var(--ease);
}
.card:hover { transform: translateY(-5px); box-shadow: 0 20px 44px -18px rgba(27, 17, 11, 0.3); }

/* Four uniform cards plus one double-width card fill a 3-column grid
   exactly: [1][2][3] / [4][wide]. No dead cells, no stretched gaps. */
.card-wide { grid-column: span 2; flex-direction: row; }

.shot { position: relative; aspect-ratio: 4 / 3; overflow: hidden; background: var(--cream-2); }
.shot-wide { aspect-ratio: auto; flex: 0 0 46%; }

/* The *_c.jpg sources are already cropped to the product, so a plain
   centered cover is all that is needed. GSAP owns transform here (parallax),
   so there is deliberately no CSS transform/transition on the image. */
.shot img {
  width: 100%; height: 100%; object-fit: cover;
  object-position: center;
  will-change: transform;
}

.card-body { padding: 24px; display: flex; flex-direction: column; gap: 9px; flex: 1; }
.card-wide .card-body { justify-content: center; padding: 34px; }

/* Ticket header: 01 category ------------- */
.tag {
  display: flex; align-items: center; gap: 10px;
  font-family: var(--mono); font-size: 11.5px; font-weight: 400;
  letter-spacing: 0.02em; text-transform: lowercase; color: var(--muted);
}
.tag::before {
  content: counter(item, decimal-leading-zero);
  flex: none; font-weight: 600; color: var(--caramel);
}
.tag::after {
  content: ""; flex: 1; height: 0;
  border-bottom: 1px dotted rgba(34, 23, 16, 0.3);
}
.card h3 {
  font-family: var(--display); font-size: 1.42rem; font-weight: 600;
  letter-spacing: -0.015em; line-height: 1.18;
}
.card-wide h3 { font-size: 1.85rem; }
.card-body > p { color: var(--muted); font-size: 0.95rem; }

/* Receipt lines. The dotted leader runs from the item to a price column that
   is deliberately empty: we were never given a price list, and an em dash is
   the honest answer. Filling it with invented numbers is the thing this
   layout exists to avoid. */
.lines { list-style: none; display: flex; flex-direction: column; gap: 6px; margin-top: 14px; }
.lines li {
  display: flex; align-items: baseline; gap: 8px;
  font-family: var(--mono); font-size: 12px; color: var(--bark);
}
.lines .ln { flex: none; }
.lines li::before {
  content: ""; order: 1; flex: 1;
  border-bottom: 1px dotted rgba(34, 23, 16, 0.32);
  transform: translateY(-3px);
}
.lines .px {
  order: 2; flex: none;
  color: var(--muted); letter-spacing: 0.06em;
}

/* ---------- Match night ---------- */

.match {
  position: relative; isolation: isolate; overflow: hidden;
  padding: 108px 0; color: var(--cream);
}
.match-bg {
  position: absolute; inset: -6%; z-index: -2;
  background: url("/img/post_10_c.jpg") center center / cover no-repeat;
  filter: blur(18px) brightness(0.4) saturate(1.15);
  transform: scale(1.1);
}
.match-veil {
  position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(105deg, var(--espresso) 6%, rgba(27, 17, 11, 0.9) 44%, rgba(27, 17, 11, 0.66) 100%);
}
.match-in {
  display: grid; grid-template-columns: 1fr 0.88fr;
  gap: 60px; align-items: center;
}
.match-copy h2 { margin-bottom: 18px; }
/* :not(.seam) so the prose width cap does not also clamp the margin note,
   which sets its own measure in mono and would otherwise lose on specificity. */
.match-copy > p:not(.seam) { color: rgba(247, 241, 232, 0.78); max-width: 44ch; }

.match-shot {
  border-radius: var(--r-lg); overflow: hidden;
  box-shadow: 0 34px 70px -22px rgba(0, 0, 0, 0.66);
  border: 1px solid rgba(247, 241, 232, 0.12);
}
.match-shot-in { overflow: hidden; }
.match-shot-in img {
  width: 100%; aspect-ratio: 16 / 10; object-fit: cover;
  will-change: transform;
}

/* ---------- Gallery: pinned, scrolls sideways ---------- */

.gal { background: var(--cream); overflow: hidden; }
.gal-inner {
  height: 100vh; height: 100svh;
  display: flex; flex-direction: column; justify-content: center;
  gap: 32px;
}
.gal-head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 24px; flex-wrap: wrap;
}
.gal-track {
  display: flex; gap: 14px;
  padding-inline: max(24px, calc((100vw - var(--wrap)) / 2 + 24px));
  will-change: transform;
}
.g {
  flex: 0 0 clamp(260px, 31vw, 440px);
  display: block; overflow: hidden; border-radius: var(--r);
  background: var(--cream-2); position: relative;
}
.g img {
  width: 100%; aspect-ratio: 1 / 1; object-fit: cover;
  transition: transform 0.6s var(--ease), filter 0.4s var(--ease);
}
.g:hover img { transform: scale(1.06); filter: brightness(1.05); }
.g::after {
  content: ""; position: absolute; inset: 0;
  box-shadow: inset 0 0 0 1px var(--line); border-radius: var(--r);
  pointer-events: none;
}
.gal-hint {
  text-align: center; font-family: var(--mono);
  font-size: 11.5px; letter-spacing: 0.02em;
  text-transform: lowercase; color: var(--muted);
}

/* ---------- Visit ---------- */

.visit { background: var(--cream-2); }
.visit-in { display: grid; grid-template-columns: 1fr 1fr; gap: 60px; align-items: center; }

.info {
  display: grid; grid-template-columns: 1fr 1fr; gap: 22px;
  margin: 34px 0;
  border-top: 1px solid var(--line); padding-top: 28px;
}
.info div { display: flex; flex-direction: column; gap: 4px; }
.info dt {
  font-family: var(--mono);
  font-size: 11.5px; font-weight: 400; letter-spacing: 0.02em;
  text-transform: lowercase; color: var(--caramel);
}
.info dd { display: flex; flex-direction: column; gap: 1px; }
.info dd strong, .info dd a {
  font-family: var(--display); font-size: 1.16rem; font-weight: 600;
  letter-spacing: -0.01em; color: var(--ink); text-decoration: none;
}
.info dd a { border-bottom: 1.5px solid var(--caramel); width: fit-content; transition: color 0.25s var(--ease); }
.info dd a:hover { color: var(--caramel); }
.info dd span { font-size: 12.5px; color: var(--muted); }

/* A designed location card rather than an embedded map: Google's keyless
   maps embed is refused in a frame, and this needs no third-party cookies. */
.visit-card {
  position: relative; display: block; overflow: hidden;
  border-radius: var(--r-lg);
  border: 1px solid var(--line);
  box-shadow: 0 24px 54px -22px rgba(27, 17, 11, 0.34);
  aspect-ratio: 1 / 1;
  background: var(--espresso);
  text-decoration: none;
  transition: transform 0.4s var(--ease), box-shadow 0.4s var(--ease);
}
.visit-card:hover { box-shadow: 0 32px 64px -22px rgba(27, 17, 11, 0.44); }
/* GSAP owns the image transform (parallax), so the hover lift lives on the
   card shadow instead of a competing transform. */
.visit-card-img { position: absolute; inset: 0; overflow: hidden; }
.visit-card-img img {
  width: 100%; height: 100%; object-fit: cover;
  will-change: transform;
}
.visit-card-veil {
  position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(27, 17, 11, 0.12) 0%, rgba(27, 17, 11, 0.52) 46%, rgba(27, 17, 11, 0.93) 100%);
}
.visit-card-body {
  position: absolute; inset: auto 0 0 0;
  padding: 34px; color: var(--cream);
  display: flex; flex-direction: column; gap: 8px; align-items: flex-start;
}
/* Natural casing, not lowercase like the rest of the label layer: this one
   is the cafe's actual name, and the house style stops at proper nouns. */
.visit-card-kicker {
  font-family: var(--mono);
  font-size: 11px; font-weight: 400; letter-spacing: 0.02em;
  text-transform: none; color: var(--amber);
}
.visit-card-addr {
  font-family: var(--display); font-size: 1.5rem; font-weight: 600;
  line-height: 1.24; letter-spacing: -0.015em;
}
.visit-card-cta {
  margin-top: 10px;
  font-family: var(--mono);
  font-size: 12px; font-weight: 400; letter-spacing: 0.02em;
  text-transform: none; color: var(--cream);
  padding-bottom: 3px; border-bottom: 1.5px solid var(--caramel);
  transition: color 0.25s var(--ease), border-color 0.25s var(--ease);
}
.visit-card:hover .visit-card-cta { color: var(--amber); border-color: var(--amber); }

/* ---------- Footer ---------- */

.foot { background: var(--espresso); color: var(--cream); padding-top: 68px; }
.foot-in {
  display: grid; grid-template-columns: 1fr 1.1fr; gap: 50px;
  padding-bottom: 48px;
}
.foot-brand { display: flex; gap: 14px; align-items: flex-start; }
.foot-brand > div { display: flex; flex-direction: column; gap: 6px; }
.foot-brand strong { font-family: var(--display); font-size: 1.24rem; font-weight: 600; }
.foot-brand span { font-size: 13.5px; color: rgba(247, 241, 232, 0.56); max-width: 34ch; }

.foot-cols { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; }
.foot-cols h4 {
  font-family: var(--mono);
  font-size: 11.5px; font-weight: 400; letter-spacing: 0.02em;
  text-transform: lowercase; color: var(--amber); margin-bottom: 12px;
}
.foot-cols p { font-size: 14px; color: rgba(247, 241, 232, 0.72); line-height: 1.8; }
.foot-cols a { text-decoration: none; border-bottom: 1px solid rgba(240, 180, 88, 0.5); transition: color 0.25s var(--ease); }
.foot-cols a:hover { color: var(--amber); }

.foot-bar {
  display: flex; justify-content: space-between; flex-wrap: wrap; gap: 10px;
  padding-top: 22px; padding-bottom: 26px;
  border-top: 1px solid rgba(247, 241, 232, 0.1);
  font-size: 12.5px; color: rgba(247, 241, 232, 0.45);
}

/* ---------- Responsive ---------- */

@media (max-width: 1000px) {
  .menu-grid { grid-template-columns: repeat(2, 1fr); }
  .card-wide { grid-column: span 2; }
  .match-in, .visit-in { grid-template-columns: 1fr; gap: 44px; }
  .match-shot { max-width: 460px; }
  .visit-card { max-width: 460px; }
  .foot-in { grid-template-columns: 1fr; gap: 38px; }
}

@media (max-width: 780px) {
  .nav-links { display: none; }
  .section { padding: 76px 0; }
  .match { padding: 76px 0; }
  .hero { padding: 120px 0 80px; }

  /* No pinning on touch: the strip becomes a native swipe track instead. */
  .gal-inner { height: auto; padding: 76px 0; gap: 26px; }
  .gal-track {
    overflow-x: auto; scroll-snap-type: x mandatory;
    padding-inline: 18px; gap: 10px;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .gal-track::-webkit-scrollbar { display: none; }
  .g { flex: 0 0 68vw; scroll-snap-align: center; }
  .gal-head { padding-inline: 18px; }
  .facts { gap: 26px; margin-top: 44px; }
  .facts li { flex: 1 1 40%; }
  .card-wide { flex-direction: column; }
  .shot-wide { flex: none; aspect-ratio: 16 / 9; }
  .card-wide .card-body { padding: 24px; }
  .card-wide h3 { font-size: 1.42rem; }
  .scroll-hint { display: none; }
}

@media (max-width: 560px) {
  .wrap { padding-inline: 18px; }
  .menu-grid { grid-template-columns: 1fr; }
  .card-wide { grid-column: span 1; }
  .info { grid-template-columns: 1fr; }
  .foot-cols { grid-template-columns: 1fr; gap: 26px; }
  .brand-text strong { font-size: 17px; }
  /* Deliberately not full-width: the secondary tier is a bare underlined
     link now, and stretching it across the screen reads as a broken box. */
  .cta { gap: 10px 18px; }
}
