/* TSTE Landing Page — Brand System per DESIGN.md. Follow your food curiosity. */

@font-face {
  font-family: 'Inter';
  src: url('../fonts/Inter-Variable.woff2') format('woff2-variations'), url('../fonts/Inter-Variable.woff2') format('woff2');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Noto Serif SemiBold';
  src: url('../fonts/NotoSerif-SemiBold.woff2') format('woff2');
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}

:root {
  --bg: #FAF8F5;
  --surface: #FFFFFF;
  --ink: #1A1F36;
  --ink-muted: #626B79;
  --accent: #C73E1D;
  --accent-ink: #FFFFFF;
  --reward: #E7CE89;
  /* Same tone as .chip.rarity-legendary's text color — reused as the "Game" side accent
     for Chef's Path so it stays readable on light backgrounds (--reward alone is too
     low-contrast for body text here). */
  --game-accent: #8A6A15;
  --border: #E4DED2;
  --danger: #A6341C;

  --font-display: 'Noto Serif SemiBold', 'Iowan Old Style', Georgia, serif;
  --font-body: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;

  --space-1: 8px;
  --space-2: 16px;
  --space-3: 24px;
  --space-4: 32px;
  --space-5: 48px;
  --space-6: 64px;
  --space-7: 96px;
  --space-8: 128px;

  --container-max: 1180px;

  --radius: 20px;
  --radius-pill: 999px;
  --shadow-float: 0 8px 24px rgba(26, 31, 54, 0.08);
  --motion-reveal: 400ms cubic-bezier(0.22, 0.61, 0.36, 1);
  --motion-fast: 180ms ease;
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  * { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; }
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 { font-family: var(--font-display); font-weight: 600; line-height: 1.15; margin: 0; color: var(--ink); }
h1 { font-size: clamp(2.75rem, 6vw, 4.5rem); }
h2 { font-size: clamp(1.75rem, 3.4vw, 2.4rem); }
h3 { font-size: 1.25rem; }
p { margin: 0; color: var(--ink); }
.text-muted { color: var(--ink-muted); }
a { color: inherit; }

.skip-link {
  position: absolute;
  left: -999px;
  top: 0;
  background: var(--ink);
  color: #fff;
  padding: var(--space-2) var(--space-3);
  z-index: 200;
  border-radius: 0 0 8px 0;
}
.skip-link:focus { left: 0; }

:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 3px;
}

.container {
  max-width: var(--container-max);
  margin: 0 auto;
  padding: 0 var(--space-3);
}
/* Left inset that matches where .container's own centering lands its content, so flush
   full-bleed blocks (hero/bleed-content below) can align with container-wrapped sections
   instead of sitting flush at the viewport edge. Collapses back to --space-3 once the
   viewport is narrower than --container-max, same as .container itself. */
:root { --container-inset-left: max(var(--space-3), calc((100vw - var(--container-max)) / 2 + var(--space-3))); }
.container-wide {
  max-width: 1400px;
  margin: 0 auto;
  padding: 0 var(--space-3);
}

section { padding: var(--space-7) 0; }
@media (max-width: 640px) { section { padding: var(--space-5) 0; } }

.eyebrow {
  display: inline-block;
  font-size: 0.8125rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: var(--space-2);
}

/* Scoped modifier for a single, more prominent eyebrow (currently: "How TSTE works") */
.eyebrow-lg {
  font-size: 1.15rem;
  letter-spacing: 0.03em;
  margin-bottom: var(--space-3);
}
@media (max-width: 640px) { .eyebrow-lg { font-size: 1rem; } }

.lede {
  font-size: 1.1875rem;
  line-height: 1.55;
  color: var(--ink-muted);
  max-width: 62ch;
}

/* Buttons */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 14px 28px;
  border-radius: var(--radius-pill);
  font-weight: 600;
  font-size: 1rem;
  text-decoration: none;
  border: 2px solid transparent;
  cursor: pointer;
  transition: transform var(--motion-fast), background var(--motion-fast), border-color var(--motion-fast);
  min-height: 44px;
}
.btn:hover { transform: translateY(-1px); }
.btn-primary { background: var(--accent); color: var(--accent-ink); }
.btn-primary:hover { background: #AE3417; }
.btn-secondary { background: transparent; color: var(--ink); border-color: var(--ink); }
.btn-secondary:hover { background: var(--ink); color: #fff; }
.btn-glass {
  background: rgba(255, 255, 255, 0.14);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  border: 1px solid rgba(255, 255, 255, 0.5);
  color: #fff;
}
.btn-glass:hover { background: rgba(255, 255, 255, 0.24); }

.btn-row { display: flex; gap: var(--space-2); flex-wrap: wrap; }
.btn-row.center { justify-content: center; }

/* Download-availability notice dialog (js/download-notice.js) */
.download-notice {
  position: fixed;
  margin: auto;
  max-width: 420px;
  width: calc(100% - 32px);
  border: none;
  border-radius: var(--radius);
  padding: var(--space-4);
  background: var(--surface);
  color: var(--ink);
  box-shadow: var(--shadow-float);
}
.download-notice::backdrop { background: rgba(26, 31, 54, 0.5); }
.download-notice-close {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 44px;
  height: 44px;
  border: none;
  background: transparent;
  color: var(--ink-muted);
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
}
.download-notice-close:hover { color: var(--ink); }
.download-notice-text { margin: 0 0 var(--space-3); padding-right: var(--space-4); }
.download-notice-cta { display: inline-flex; }

/* Logo — inline SVG reconstruction of the approved cloche mark */
.logo { display: inline-flex; align-items: center; gap: 12px; text-decoration: none; color: inherit; }
.logo svg { display: block; }
.logo-word { font-family: var(--font-display); font-weight: 600; font-size: 1.4rem; letter-spacing: 0.02em; }

/* Nav — floating glass pill, subtly transparent from the start, firms up on scroll */
.nav {
  position: fixed;
  top: var(--space-2);
  left: var(--space-3);
  right: var(--space-3);
  max-width: 1180px;
  margin: 0 auto;
  z-index: 50;
  border-radius: var(--radius-pill);
  background: rgba(26, 31, 54, 0.32);
  border: 1px solid rgba(255, 255, 255, 0.16);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  box-shadow: var(--shadow-float);
  transition: background var(--motion-fast), border-color var(--motion-fast), box-shadow var(--motion-fast);
  color: #fff;
}
.nav.is-scrolled {
  background: rgba(250, 248, 245, 0.92);
  border-color: var(--border);
  color: var(--ink);
}
.nav .container { display: flex; align-items: center; justify-content: space-between; max-width: none; margin: 0; padding: 12px var(--space-3); }
.nav-links { display: flex; align-items: center; gap: var(--space-4); list-style: none; margin: 0; padding: 0; }
.nav-links a { text-decoration: none; font-weight: 600; font-size: 0.95rem; color: inherit; }
.nav-actions { display: flex; align-items: center; gap: 12px; }
.nav-lang { display: flex; gap: 8px; font-size: 0.85rem; }
.nav-lang a { color: inherit; opacity: 0.75; text-decoration: none; padding: 4px 8px; border-radius: 8px; }
.nav-lang a[aria-current="true"] { opacity: 1; }
.nav.is-scrolled .nav-lang a[aria-current="true"] { background: var(--border); }

/* Placeholder phone-screen mockup, top-right of nav — reserves the spot for a future real app screenshot */
.nav-phone-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 38px;
  flex-shrink: 0;
  border: 2px solid currentColor;
  border-radius: 6px;
  opacity: 0.55;
}
.nav-phone-placeholder .nav-phone-screen {
  width: calc(100% - 6px);
  height: calc(100% - 10px);
  border-radius: 2px;
  background-image: repeating-linear-gradient(135deg, currentColor 0, currentColor 1px, transparent 1px, transparent 4px);
  opacity: 0.6;
}
@media (max-width: 560px) { .nav-phone-placeholder { display: none; } }

.nav-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: 12px;
  border: 1px solid currentColor;
  background: transparent;
}
.nav-toggle svg { display: block; }
.nav-toggle svg path { stroke: currentColor; }

@media (max-width: 760px) {
  .nav-links {
    display: none;
    position: absolute;
    top: calc(100% + 8px);
    left: 0;
    right: 0;
    background: var(--surface);
    color: var(--ink);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    flex-direction: column;
    align-items: flex-start;
    padding: var(--space-3);
    gap: var(--space-2);
    box-shadow: var(--shadow-float);
  }
  .nav-links.is-open { display: flex; }
  .nav-toggle { display: inline-flex; }
}

/* Full-bleed image sections (hero + bleed-section variant) */
.hero, .bleed-section {
  position: relative;
  display: flex;
  align-items: flex-end;
  padding: 0;
}
.hero { min-height: 94vh; overflow: hidden; }
.bleed-section { min-height: 66vh; overflow: hidden; }
.hero-bg { position: absolute; inset: 0; z-index: 0; }
.hero-bg img { width: 100%; height: 100%; object-fit: cover; }
.hero-scrim {
  position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(180deg, rgba(26,31,54,0.10) 0%, rgba(26,31,54,0.35) 55%, rgba(26,31,54,0.82) 100%);
}
/* Hero (top section only) — extra left-side scrim so the left-aligned text stays readable over the busier part of the image */
.hero > .hero-scrim {
  background:
    linear-gradient(90deg, rgba(10,12,22,0.92) 0%, rgba(10,12,22,0.6) 40%, rgba(10,12,22,0) 72%),
    linear-gradient(180deg, rgba(26,31,54,0.10) 0%, rgba(26,31,54,0.35) 55%, rgba(26,31,54,0.82) 100%);
}
.hero-content {
  position: relative; z-index: 2;
  width: 100%;
  padding: var(--space-8) var(--space-3) var(--space-7);
  text-align: center;
  color: #fff;
}
.hero-content .eyebrow { color: var(--reward); }
.hero-content h1 { color: #fff; max-width: 17ch; margin: var(--space-2) auto var(--space-3); }
.hero-content .lede { color: rgba(255,255,255,0.86); max-width: 56ch; margin: 0 auto var(--space-4); }

/* Hero (top section only) — left-aligned so the truffle reveal in the image stays visible */
.hero > .hero-content {
  text-align: left;
  /* max-width grows with the extra left inset so the readable text column stays the
     original ~572px wide instead of being squeezed by the added padding-left. */
  max-width: calc(620px + var(--container-inset-left) - var(--space-3));
  margin: 0;
  padding-left: var(--container-inset-left);
}
.hero > .hero-content h1 { margin: var(--space-2) 0 var(--space-3); }
.hero > .hero-content .lede { margin: 0 0 var(--space-4); }

.bleed-content {
  position: relative; z-index: 2;
  padding: var(--space-6) var(--space-3);
  padding-left: var(--container-inset-left);
  max-width: calc(640px + var(--container-inset-left) - var(--space-3));
  color: #fff;
}
.bleed-content .eyebrow { color: var(--reward); }
.bleed-content h2 { color: #fff; margin-bottom: var(--space-2); }
.bleed-content p { color: rgba(255,255,255,0.86); margin-bottom: var(--space-2); }

/* Hero phone mockup — placeholder bezel for a future real app screenshot.
   Anchored a bit below the vertical center of the hero image (top:58% + translateY(-50%)) so
   it sits comfortably between the nav pill and the bottom of the hero, never overlapping the
   navigation. Positioned with `right` (not `left`) so it scales with viewport width and stays
   clear of the left-aligned hero copy, which has a fixed max-width of 620px regardless of
   viewport. */
.hero-phone-mock {
  position: absolute;
  z-index: 2;
  right: 15%;
  top: 58%;
  transform: translateY(-50%);
  width: 248px;
  height: 506px;
  padding: 12px;
  background: rgba(26, 31, 54, 0.72);
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 40px;
  box-shadow: var(--shadow-float);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
}
.hero-phone-mock-screen {
  position: relative;
  width: 100%;
  height: 100%;
  border-radius: 30px;
  overflow: hidden;
  background-image: repeating-linear-gradient(135deg, rgba(255,255,255,0.16) 0, rgba(255,255,255,0.16) 1px, transparent 1px, transparent 8px);
  background-color: rgba(255, 255, 255, 0.05);
}
.hero-phone-mock-screen img {
  position: absolute;
  inset: 0;
  z-index: 1;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.hero-phone-mock-screen::before {
  content: '';
  position: absolute;
  z-index: 2;
  top: 12px; left: 50%;
  transform: translateX(-50%);
  width: 54px; height: 9px;
  border-radius: 6px;
  background: rgba(10, 13, 22, 0.7);
}
@media (max-width: 1100px) { .hero-phone-mock { right: 5%; width: 210px; height: 428px; } }
/* Below 900px the phone no longer fits beside the (now full-width) hero copy. It drops out of
   the absolute overlay and flows in normal document order below the hero text (eyebrow/h1/lede/
   CTA) instead of floating as a top-right badge. .hero-content padding-top only needs to clear
   the fixed nav pill now, not reserve room for a badge underneath it. */
@media (max-width: 900px) {
  /* .hero is `display: flex; align-items: flex-end;` at every width. Without flex-direction:
     column here, a statically-flowed device frame would sit beside the hero copy as a second
     row item, not below it. Cross-axis becomes horizontal once the axis flips, so the inherited
     flex-end would also shove .hero-content to the right edge instead of keeping it left-aligned
     — override to flex-start; the device frame still self-centers via its own auto margins,
     which take precedence over align-items regardless of this value. */
  .hero { flex-direction: column; align-items: flex-start; }
  /* padding-bottom overrides the base .hero-content space-7 (96px): that gap was designed to
     clear the absolutely-positioned device frame on desktop, but on mobile the frame flows
     below in normal document order (see .hero-device-frame below) and supplies its own
     margin-top instead, so the inherited 96px just left a large dead gap above the phone. */
  .hero-content { padding-top: 110px; padding-bottom: var(--space-3); }
  .hero-phone-mock {
    position: static;
    margin: var(--space-4) auto var(--space-7);
    right: auto;
    top: auto;
    left: auto;
    transform: none;
    width: 180px;
    height: 367px;
    padding: 10px;
    border-radius: 32px;
  }
  .hero-phone-mock-screen { border-radius: 24px; }
  .hero-phone-mock-screen::before { top: 10px; width: 40px; height: 7px; }
}

/* Hero device frame — rotating screenshot carousel, black bezel (style borrowed from
   the Dopafy landing page's .phone-frame). Same position/breakpoints as .hero-phone-mock above,
   which it replaces on the DE hero only (EN keeps the placeholder mock until EN screenshots
   exist). Rotation reuses the generic .slideshow-bg cross-fade from main.js — no extra JS.
   No CSS-drawn notch: the screenshots themselves already show the real Dynamic Island, so a
   second one drawn on top of the bezel would double up. */
.hero-device-frame {
  position: absolute;
  z-index: 2;
  right: 15%;
  top: 58%;
  transform: translateY(-50%);
  width: 248px;
}
.hero-device-bezel {
  position: relative;
  background: #0a0a0a;
  border-radius: 42px;
  padding: 12px;
  box-shadow:
    0 0 0 2px rgba(255, 255, 255, 0.12),
    0 30px 60px rgba(0, 0, 0, 0.45),
    var(--shadow-float);
}
.hero-device-screen {
  position: relative;
  border-radius: 32px;
  overflow: hidden;
  aspect-ratio: 662 / 1440;
  background: #000;
}
.hero-device-screen img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
@media (max-width: 1100px) { .hero-device-frame { right: 5%; width: 210px; } }
@media (max-width: 900px) {
  /* Bigger + tighter to the CTA above: at 180px the phone read as an afterthought floating
     in leftover space, mostly because .hero-content's space-7 padding-bottom (overridden
     above) plus this margin-top stacked into a ~130px dead gap under the button. */
  .hero-device-frame {
    position: static;
    margin: var(--space-1) auto var(--space-4);
    right: auto;
    top: auto;
    left: auto;
    transform: none;
    width: 230px;
  }
  .hero-device-bezel { padding: 8px; border-radius: 32px; }
  .hero-device-screen { border-radius: 24px; }
}

/* Slideshow background — subtle cross-fade between multiple food photos */
.slideshow-bg img { position: absolute; inset: 0; opacity: 0; transition: opacity 1800ms ease; }
.slideshow-bg img.is-active { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .slideshow-bg img { transition: none; } }

/* Section heading block */
.section-head { max-width: 62ch; margin-bottom: var(--space-5); }
.section-head.center { margin-left: auto; margin-right: auto; text-align: center; }
/* Pricing intro + Partner-Tag intro (de/partner.html): h2 and p sit flush by default
   (both margin:0) -- these are the two spots on the page where that reads as cramped. */
#pakete .section-head p, #partner-tag .section-head p { margin-top: var(--space-3); }

.split-copy h2 { margin-bottom: var(--space-2); }
.split-copy p { color: var(--ink-muted); margin-bottom: var(--space-3); }

.chip-row { display: flex; flex-wrap: wrap; gap: 8px; margin: var(--space-3) 0; }
.chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 14px; border-radius: var(--radius-pill);
  border: 1px solid var(--border); background: var(--surface);
  font-size: 0.85rem; font-weight: 600; color: var(--ink);
  text-decoration: none;
}
.chip.rarity-legendary { border-color: var(--reward); color: #8A6A15; background: #FBF6E7; }
.chip.rarity-rare { border-color: var(--accent); color: var(--accent); background: var(--surface); }

/* CHEF'S PATH — diagonal real-life/game split ------------------------------------------
   Two independently-cropped panels, 50/50 width, each a landscape photo generated for this
   layout (objection-maps-pin.webp on the real side, chefs-path-tool-unlock-{en,de}.webp —
   a floating reward card over a blurred dinner-table scene — on the game side) so each
   needs only a mild object-fit: cover trim, not a forced-aspect crop. Each panel's inner
   edge is cut with its own clip-path wedge so the diagonal boundary is part of the panel
   shapes themselves, not a separate overlay bar; a thin gradient gap between the two
   wedges (echoing the diagonal reveal line in the TSTE logo's cloche lid) is the only seam
   accent. */
#chefs-path { position: relative; overflow: hidden; }
.chefs-path-bg { position: absolute; inset: 0; z-index: 0; }
.chefs-path-bg img { width: 100%; height: 100%; object-fit: cover; opacity: 0.22; }
#chefs-path .container { position: relative; z-index: 1; }

.path-split { position: relative; margin-bottom: var(--space-6); }
.path-split-media {
  position: relative;
  display: flex;
  align-items: stretch;
  gap: 3px;
  width: 100%;
  aspect-ratio: 12 / 5;
  border-radius: var(--radius);
  overflow: hidden;
  background: linear-gradient(160deg, var(--accent), var(--game-accent));
  box-shadow: var(--shadow-float);
}
@media (max-width: 760px) { .path-split-media { flex-direction: column; aspect-ratio: auto; gap: 3px; } }
.path-split-panel { position: relative; overflow: hidden; flex: 1 1 0%; }
.path-split-panel-real { clip-path: polygon(0 0, 100% 0, calc(100% - 20px) 100%, 0 100%); }
.path-split-panel-game { clip-path: polygon(20px 0, 100% 0, 100% 100%, 0 100%); }
@media (max-width: 760px) {
  .path-split-panel { flex: none; width: 100%; aspect-ratio: 16 / 10; }
  .path-split-panel-real { clip-path: polygon(0 0, 100% 0, 100% calc(100% - 14px), 0 100%); }
  .path-split-panel-game { clip-path: polygon(0 14px, 100% 0, 100% 100%, 0 100%); }
}
.path-split-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.path-split-img-real { object-position: 64% center; }
.path-split-img-game { object-position: 55% center; }

/* Labels sit directly on the photo, in the corners the diagonal seam points toward, so the
   eye reads Real (top-left) -> seam -> Game (bottom-right) as one continuous diagonal line
   instead of two disconnected captions below the image. */
.path-split-tag {
  position: absolute;
  z-index: 2;
  display: inline-flex;
  padding: 10px 22px;
  border-radius: var(--radius-pill);
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.15rem;
  letter-spacing: 0.02em;
  color: #fff;
  box-shadow: 0 4px 14px rgba(10, 13, 22, 0.28);
}
.path-split-tag-real { top: var(--space-3); left: var(--space-3); background: rgba(199, 62, 29, 0.95); }
.path-split-tag-game { bottom: var(--space-3); right: var(--space-3); background: rgba(138, 106, 21, 0.95); }

@media (max-width: 640px) {
  .path-split-tag { font-size: 1rem; padding: 8px 18px; }
}

/* Subtext bubbles break out of the image right at the corner each tag sits in: the real
   bubble pokes out above the top edge next to "In Real Life", the game bubble pokes out
   below the bottom edge next to "In the Game" -- so they read as speech bubbles the tags
   are saying, anchored to the photo, rather than captions floating separately underneath. */
.path-split-bubble {
  position: absolute;
  z-index: 3;
  max-width: 290px;
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--border);
  border-radius: 16px;
  padding: 14px 18px;
  font-size: 0.9rem;
  line-height: 1.5;
  box-shadow: var(--shadow-float);
}
.path-split-bubble p { margin: 0; color: var(--ink-muted); }
/* A plain triangle tail (border trick, two stacked triangles: ::before slightly larger in
   the border color for a 1px stroke, ::after smaller and inset in the bubble's own surface
   color for the fill) instead of a rotated square -- a rotated square reads as a diamond
   sitting inside the box, not a bubble corner, no matter how it's offset. */
.path-split-bubble::before,
.path-split-bubble::after {
  content: '';
  position: absolute;
  width: 0;
  height: 0;
}
/* Tails point inward at the centered guide (.chefs-path-guide) instead of down/up at their
   own tag, so both bubbles read as him speaking from the middle of the split. Border color
   matches each bubble's own tag (--accent red for real, --game-accent gold for game) so it
   visually pairs with it instead of reading as one generic neutral-gray bubble style. */
.path-split-bubble-real { top: 90px; left: 250px; border: 2px solid var(--accent); }
.path-split-bubble-real::before {
  bottom: -12px; right: 22px;
  border-left: 11px solid transparent;
  border-right: 11px solid transparent;
  border-top: 12px solid var(--accent);
}
.path-split-bubble-real::after {
  bottom: -10px; right: 24px;
  border-left: 9px solid transparent;
  border-right: 9px solid transparent;
  border-top: 10px solid var(--surface);
}
/* max-width 280px is not eyeballed -- live-measured against this exact copy so it wraps
   "...collect skills" / "to build your own restaurant." on two lines, not three. Shifted
   right of .chefs-path-guide's legs instead of sitting underneath them. */
.path-split-bubble-game { bottom: -55px; right: 210px; max-width: 280px; border: 2px solid var(--game-accent); }
.path-split-bubble-game::before {
  top: -12px; left: 22px;
  border-left: 11px solid transparent;
  border-right: 11px solid transparent;
  border-bottom: 12px solid var(--game-accent);
}
.path-split-bubble-game::after {
  top: -10px; left: 24px;
  border-left: 9px solid transparent;
  border-right: 9px solid transparent;
  border-bottom: 10px solid var(--surface);
}

/* Below 760px the panels stack and .chefs-path-guide re-anchors to a horizontally-centered
   spot straddling the seam (top: calc(62.5vw - 163px), see .chefs-path-guide's own mobile
   rule) instead of sitting between two side-by-side panels — so the bubbles can no longer sit
   at the desktop's diagonal-corner offsets (those pointed at tags that are no longer where the
   guide is). Both now flank him left/right, vertically centered on his own midpoint using the
   identical 62.5vw formula so they track his position across viewport widths, with tails
   rotated to point sideways at him instead of up/down. */
@media (max-width: 760px) {
  .path-split-bubble {
    max-width: 150px; font-size: 0.78rem; padding: 10px 12px;
    top: calc(62.5vw - 81.5px); bottom: auto;
    transform: translateY(-50%);
  }
  .path-split-bubble-real { right: calc(50% + 40px); left: auto; }
  /* Nudged down from the shared vertical center so it clears the location-pin badge
     baked into the real-life photo behind it. */
  .path-split-bubble-game { left: calc(50% + 40px); right: auto; top: calc(62.5vw - 81.5px + 34px); }
  .path-split-bubble-real::before, .path-split-bubble-real::after,
  .path-split-bubble-game::before, .path-split-bubble-game::after {
    top: 50%; bottom: auto; transform: translateY(-50%);
  }
  .path-split-bubble-real::before {
    right: -12px; left: auto;
    border-top: 11px solid transparent; border-bottom: 11px solid transparent;
    border-left: 12px solid var(--accent); border-right: none;
  }
  .path-split-bubble-real::after {
    right: -10px; left: auto;
    border-top: 9px solid transparent; border-bottom: 9px solid transparent;
    border-left: 10px solid var(--surface); border-right: none;
  }
  .path-split-bubble-game::before {
    left: -12px; right: auto;
    border-top: 11px solid transparent; border-bottom: 11px solid transparent;
    border-right: 12px solid var(--game-accent); border-left: none;
  }
  .path-split-bubble-game::after {
    left: -10px; right: auto;
    border-top: 9px solid transparent; border-bottom: 9px solid transparent;
    border-right: 10px solid var(--surface); border-left: none;
  }
}
@media (max-width: 420px) {
  .path-split-bubble { max-width: 122px; font-size: 0.74rem; padding: 8px 10px; }
}

/* The in-app Story Wizard guide, standing centered on the seam between the two photos, now
   full-body (outpainted legs, see docs/handoffs) so he actually stands on the ground instead
   of a hips-up crop reading as "standing" only by illusion. He has no bubble of his own --
   the two existing panel bubbles (.path-split-bubble-real/-game) are retailed to point at
   him instead of at their tags, so both halves read as him narrating. Sized up a step past
   the hat-brim-matched baseline on request; left recomputed each time from height * (237/600
   intrinsic aspect) / 2 to stay centered. Centered via left:calc(50% - halfWidth) rather than
   a transform, since the reveal animation below already uses transform for its slide-in. */
.chefs-path-guide {
  position: absolute;
  z-index: 4;
  bottom: -4px;
  left: calc(50% - 63px);
  height: 320px;
  width: auto;
  pointer-events: none;
  filter: drop-shadow(0 8px 14px rgba(10, 13, 22, 0.4));
  /* Fades/slides in slightly after the rest of .path-split (driven by the parent's own
     .reveal/.is-visible, not a separate observer trigger) so he visibly "arrives" once the
     split image has already settled. */
  opacity: 0;
  transform: translateY(10px);
  transition: opacity var(--motion-reveal), transform var(--motion-reveal);
  transition-delay: 0.35s;
}
.path-split.is-visible .chefs-path-guide {
  opacity: 1;
  transform: none;
}
@media (prefers-reduced-motion: reduce) {
  .chefs-path-guide {
    opacity: 1;
    transform: none;
    transition: none;
  }
}
@media (max-width: 760px) {
  /* Panels stack on mobile, so "bottom of the real panel" isn't a fixed offset from
     .path-split's own bottom anymore -- anchor from the top of the stack instead, at
     roughly the bottom of the (16:10) real panel, grounded the same way as desktop. */
  .chefs-path-guide { bottom: auto; top: calc(62.5vw - 163px); left: calc(50% - 32px); height: 163px; }
}

/* CHEF'S PATH — click-through step slideshow -------------------------------------------- */
.path-slideshow { position: relative; }
/* Guide stands beside the phone screen, not in front of it. .path-slide-frame is always
   exactly 160x326 at (0,0) in .path-slideshow coordinates (verified live: the grid track
   sizes to the tallest slide, and the frame is that tallest element on every slide, so its
   box never moves when steps change) -- position off frame's right edge (160px + --space-3
   gap) instead of guessing from viewport units. bottom:78px grounds his feet where the
   frame's own bottom sits (slideshow height 404 - track height 326).
   Sized down from the .chefs-path-guide scale on purpose: .path-slide-copy text sits
   vertically centered in the row (measured live at y 120-206 for 6 of 7 steps, y 70-256 for
   step 3's two extra chip-rows), so a full head-to-shoulder-height guide would sit on top of
   it. Bubble is anchored to the row's TOP instead of beside his head, in the one band clear
   on every step including step 3. Bubble text swaps per active step via showPathSlide() in
   js/main.js. */
.path-slideshow-guide {
  position: absolute;
  z-index: 3;
  left: 130px;
  bottom: 78px;
  height: 140px;
  width: auto;
  pointer-events: none;
  filter: drop-shadow(0 8px 14px rgba(10, 13, 22, 0.3));
}
.path-slideshow-guide-bubble {
  position: absolute;
  z-index: 3;
  left: 182px;
  top: 6px;
  bottom: auto;
  max-width: 175px;
  transition: opacity 200ms ease;
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--border);
  border-radius: 16px;
  padding: 10px 14px;
  font-size: 0.8rem;
  line-height: 1.4;
  box-shadow: var(--shadow-float);
}
.path-slideshow-guide-bubble p { margin: 0; color: var(--ink-muted); }
.path-slideshow-guide-bubble::before {
  content: '';
  position: absolute;
  bottom: -6px;
  left: 18px;
  width: 12px;
  height: 12px;
  background: var(--surface);
  border-right: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  transform: rotate(45deg);
}
@media (max-width: 760px) {
  .path-slideshow-guide, .path-slideshow-guide-bubble { display: none; }
}
@media (prefers-reduced-motion: reduce) { .path-slideshow-guide-bubble { transition: none; } }
.path-slideshow-track { display: grid; }
.path-slide {
  grid-area: 1 / 1;
  display: flex; align-items: center; gap: var(--space-4);
  opacity: 0; pointer-events: none;
  transition: opacity 300ms ease;
}
.path-slide.is-active { opacity: 1; pointer-events: auto; }
@media (prefers-reduced-motion: reduce) { .path-slide { transition: none; } }
@media (max-width: 760px) {
  .path-slide { flex-direction: column; align-items: flex-start; }
  /* Text (with the "Schritt X von 7" tag) reads before the screenshot on mobile, matching
     the reading order everywhere else the guide narrates a step before showing it. */
  .path-slide-copy { order: 1; }
  .path-slide-frame { order: 2; }
}

.path-slide-frame {
  flex: none;
  width: 160px; height: 326px;
  padding: 6px;
  background: #0a0a0a;
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 26px;
  box-shadow:
    0 0 0 2px rgba(255, 255, 255, 0.08),
    0 20px 40px rgba(10, 13, 22, 0.35),
    var(--shadow-float);
}
.path-slide-frame-screen {
  position: relative; width: 100%; height: 100%;
  border-radius: 21px;
  overflow: hidden;
  background-image: repeating-linear-gradient(135deg, rgba(255,255,255,0.16) 0, rgba(255,255,255,0.16) 1px, transparent 1px, transparent 8px);
  background-color: rgba(255, 255, 255, 0.05);
}
.path-slide-frame-screen img {
  position: absolute;
  inset: 0;
  z-index: 1;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* No CSS-drawn notch here either: the path-step screenshots already bake in the real
   Dynamic Island, so a second one on top would double up. */
.path-slide-tag { display: inline-block; font-size: 0.75rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--ink-muted); margin-bottom: var(--space-1); }
.path-slide-copy h3 { margin-bottom: var(--space-1); }
.path-slide-copy p { color: var(--ink-muted); }
.path-slide-copy .chip-row { margin-top: var(--space-2); margin-bottom: 0; }

.path-slideshow-controls { display: flex; align-items: center; justify-content: center; gap: var(--space-3); margin-top: var(--space-4); }
.path-slideshow-prev, .path-slideshow-next {
  all: unset; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  width: 44px; height: 44px;
  border: 1px solid var(--border); border-radius: 50%;
  color: var(--ink);
}
.path-slideshow-prev:hover, .path-slideshow-next:hover { background: var(--surface); border-color: var(--ink); }
.path-slideshow-dots { display: flex; gap: 0; }
.path-slideshow-dots button {
  all: unset; cursor: pointer;
  width: 8px; height: 8px; padding: 12px;
  background-clip: content-box;
  border-radius: 50%; background: var(--border);
}
.path-slideshow-dots button.is-active { background: var(--accent); width: 20px; border-radius: var(--radius-pill); transition: width 200ms ease; }
@media (prefers-reduced-motion: reduce) { .path-slideshow-dots button.is-active { transition: none; } }
.path-slideshow-prev:focus-visible,
.path-slideshow-next:focus-visible,
.path-slideshow-dots button:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }

/* More ways — mosaic, every tile carries an image. Two columns: 2 taller cards on the
   left (occasion, cuisine), 3 shorter stacked cards on the right (dish, prep, ingredients) —
   both columns stretch to the same total height. */
.mosaic-section { position: relative; background: #F5EEE0; }
.mosaic-section::before {
  content: ''; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.05 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-repeat: repeat;
}
.mosaic-section .container { position: relative; z-index: 1; }
/* align-items:stretch + flex:1 on cards -- the left column (2 cards) stretches to match
   the right column's (3 cards) total height, so both columns end level with no gap before
   the wide card below, instead of the left column falling short. */
.module-grid { display: flex; gap: var(--space-3); align-items: stretch; }
@media (max-width: 900px) { .module-grid { flex-direction: column; } }
.module-col { display: flex; flex-direction: column; gap: var(--space-3); flex: 1; }
.module-card {
  background: #FBF7EE; border: 1px solid var(--border); border-radius: var(--radius);
  overflow: hidden; display: flex; flex-direction: column; flex: 1;
  box-shadow: 0 10px 24px rgba(26, 31, 54, 0.06);
}
.module-card--wide { flex: none; }
.module-card-body { padding: var(--space-3); display: flex; flex-direction: column; gap: 6px; }
.module-card h3 { font-size: 1.4rem; line-height: 1.15; }
.module-card h3::first-letter { color: var(--accent); font-size: 1.2em; }
.module-card p { color: var(--ink-muted); font-size: 0.92rem; }
.module-col-left .module-card img { width: 100%; aspect-ratio: 3 / 2; height: auto; flex: none; object-fit: cover; }
.module-col-right .module-card img { width: 100%; height: 230px; flex: none; object-fit: cover; }
/* Wide card below the two columns -- same magazine-card look (cream, border, shadow) as
   the grid above, just laid out as a row (image left, copy right) instead of stacked, so
   it reads as one continuous wall rather than a separate section. */
.module-card--wide { flex-direction: row; height: 230px; margin-top: var(--space-3); }
.module-card--wide img { width: 42%; height: 100%; flex: none; object-fit: cover; }
.module-card--wide .module-card-body { flex: 1; justify-content: center; padding: var(--space-4); overflow: hidden; }
@media (max-width: 640px) {
  .module-card--wide { flex-direction: column; height: auto; }
  .module-card--wide img { width: 100%; height: auto; aspect-ratio: 4 / 3; }
}

/* TSTE Mission Cards — flip cards replacing the old objection disclosures */
.mission-cards { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-3); }
@media (max-width: 900px) { .mission-cards { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .mission-cards { grid-template-columns: 1fr; } }

.mission-card { -webkit-perspective: 1400px; perspective: 1400px; }
.mission-card-btn {
  all: unset; display: block; width: 100%; aspect-ratio: 3 / 4; cursor: pointer;
}
.mission-card-inner {
  display: block;
  position: relative; width: 100%; height: 100%;
  /* -webkit- prefix is load-bearing on iOS/mobile Safari: without it this container silently
     falls back to `transform-style: flat`, which flattens the back face's own counter-rotation
     (line below) into a 2D mirror instead of keeping it in the 3D context the outer flip
     expects -- the back face's text renders mirrored once flipped into view. */
  -webkit-transform-style: preserve-3d; transform-style: preserve-3d;
  transition: transform 550ms cubic-bezier(0.22, 0.61, 0.36, 1);
}
.mission-card.is-flipped .mission-card-inner { transform: rotateY(180deg); }
@media (prefers-reduced-motion: reduce) { .mission-card-inner { transition: none; } }

.mission-card-face {
  display: flex; flex-direction: column;
  position: absolute; inset: 0;
  backface-visibility: hidden; -webkit-backface-visibility: hidden;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  background: var(--surface);
  padding: var(--space-3);
  box-shadow: var(--shadow-float);
  overflow: hidden;
  text-align: left;
  /* Belt-and-suspenders on top of backface-visibility above: some mobile Safari versions still
     depth-sort the two faces incorrectly inside the preserve-3d context, painting the face
     that's rotated away on top instead of behind, mirrored. A hard visibility switch can't
     rely on that depth sort at all -- delayed to 275ms (half the 550ms flip) so it only takes
     over once the flip has passed the midpoint, same moment backface-visibility would already
     be hiding it if the depth sort were correct. */
  transition: visibility 0s linear 275ms;
  visibility: visible;
}
.mission-card:not(.is-flipped) .mission-card-face.back,
.mission-card.is-flipped .mission-card-face.front { visibility: hidden; }
@media (prefers-reduced-motion: reduce) { .mission-card-face { transition: none; } }
.mission-card-face.back { transform: rotateY(180deg); background: var(--ink); border-color: var(--ink); }
.mission-card-face .mission-logo { position: absolute; top: 16px; right: 16px; width: 26px; height: 26px; opacity: 0.9; }
.mission-card-face .mission-tag { font-size: 0.75rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--accent); }
.mission-card-face.back .mission-tag { color: var(--reward); }
.mission-card-face .mission-question {
  font-family: var(--font-display); font-size: 1.2rem; line-height: 1.25;
  margin-top: auto; padding-top: var(--space-3);
}
.mission-card-face.back .mission-question { color: var(--bg); margin-top: var(--space-2); padding-top: 0; }
.mission-card-face.back p.mission-proof { color: #C7CBD6; font-size: 0.95rem; margin-top: var(--space-2); }
.mission-card-face.back .mission-proof-list {
  list-style: none; margin: var(--space-2) 0 0; padding: 0;
  display: flex; flex-direction: column; gap: 4px;
  color: #C7CBD6; font-size: 0.95rem;
}
.mission-card-face.back .mission-proof-list li { padding-left: 1em; position: relative; }
.mission-card-face.back .mission-proof-list li::before { content: "\2022"; position: absolute; left: 0; color: var(--reward); }
.mission-card-face .mission-hint { margin-top: 10px; font-size: 0.78rem; color: var(--ink-muted); }
.mission-card-face .loop-word { font-size: 2.2rem; margin: auto 0; padding-top: 0; }

/* Small proof-of-app phone frame in the back face's free corner, below the tag/question/
   proof text -- same black-bezel language as .phone-bezel/.partner-hero-device, scaled down
   to fit the narrow mission-card. Absolutely positioned (not a flex child) since the card's
   own aspect-ratio fixes its height and text length varies per card. */
.mission-card-device {
  position: absolute; z-index: 1;
  bottom: var(--space-3); right: var(--space-3);
  width: 52px;
  background: #0a0a0a; padding: 3px; border-radius: 11px;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.14), 0 8px 16px rgba(10, 13, 22, 0.4);
}
.mission-card-device img {
  display: block; width: 100%; aspect-ratio: 662 / 1440;
  object-fit: cover; border-radius: 8px;
}

.mission-card-face.bg-photo { background-size: cover; background-position: center; }
/* Background images set here (not as inline style="") to satisfy the page's style-src 'self' CSP */
.mission-card-face.bg-photo-loop-discover { background-image: url('../images/brand/loop-discover.webp'); }
/* Taste/Collect swapped on purpose: loop-collect.webp (eating at the table) reads as "tasting",
   loop-taste.webp (tool reveal) reads as "collecting" — matches each card's label better. */
.mission-card-face.bg-photo-loop-taste { background-image: url('../images/brand/loop-collect.webp'); }
.mission-card-face.bg-photo-loop-collect { background-image: url('../images/brand/loop-taste.webp'); }
.mission-card-face.bg-photo-build-restaurant { background-image: url('../images/brand/build-restaurant-chefs-table.webp'); }
.mission-card-face.bg-photo-objection-maps { background-image: url('../images/brand/objection-maps-pin.webp'); }
.mission-card-face.bg-photo-objection-saved-places { background-image: url('../images/brand/objection-saved-places.webp'); }
.mission-card-face.bg-photo-objection-food-apps-screen { background-image: url('../images/brand/objection-food-apps-screen.webp'); }
.mission-card-face.bg-photo-objection-food-expertise { background-image: url('../images/brand/objection-food-expertise.webp'); }
.mission-card-face.bg-photo::before {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(26,31,54,0.05) 0%, rgba(26,31,54,0.85) 85%);
}
.mission-card-face.bg-photo .mission-tag,
.mission-card-face.bg-photo .mission-question,
.mission-card-face.bg-photo .mission-hint { position: relative; z-index: 1; }
/* Step tag sits near the top of the card, where the bottom-weighted scrim below is nearly
   transparent — give it its own solid accent chip so it stays legible against busy photos. */
.mission-card-face.bg-photo .mission-tag {
  align-self: flex-start;
  background: var(--accent);
  color: #fff;
  padding: 3px 10px;
  border-radius: var(--radius-pill);
}
.mission-card-face.bg-photo .mission-question,
.mission-card-face.bg-photo .mission-hint { color: #fff; }
/* Corner mark gets the same dark chip treatment (bigger + backed) for the same reason as the
   step tag: a plain icon on a busy photo reads inconsistently, a backed circle always reads. */
.mission-card-face.bg-photo .mission-logo {
  z-index: 1;
  width: 34px; height: 34px;
  padding: 6px;
  box-sizing: border-box;
  background: rgba(10, 13, 22, 0.55);
  border-radius: 50%;
  opacity: 1;
}

/* Story — editorial, dimmed atmosphere image behind dark overlay (documented gap in DESIGN.md) */
.story-section { position: relative; overflow: hidden; background: var(--ink); color: var(--bg); }
.story-bg { position: absolute; inset: 0; z-index: 0; }
.story-bg img { width: 100%; height: 100%; object-fit: cover; opacity: 0.32; filter: saturate(0.7); }
.story-bg::after { content: ''; position: absolute; inset: 0; background: linear-gradient(180deg, rgba(26,31,54,0.55), rgba(26,31,54,0.94)); }
.story-section .container { position: relative; z-index: 1; }
.story-section .eyebrow { color: var(--reward); }
.story-section h2, .story-section h3 { color: var(--bg); }
.story-section .lede, .story-section p { color: #C7CBD6; }
.story-section .media-row-list li { color: #C7CBD6; }
.story-section .media-row-list strong { color: var(--bg); }
.story-section .benefit-index { border-bottom-color: rgba(255, 255, 255, 0.16); }
.story-copy { max-width: 68ch; margin: 0 auto; text-align: center; }
.story-copy h2 { font-size: clamp(1.9rem, 3.6vw, 2.6rem); margin-bottom: var(--space-3); }
.story-copy p + p { margin-top: var(--space-2); }
.story-mark { display: block; margin: 0 auto var(--space-3); width: 48px; height: 48px; opacity: 0.9; }

/* Where TSTE lives — grid of the 7 launch cities, replaces the old CSS-stat progress preview */
.city-grid { display: grid; grid-template-columns: 1.1fr 0.9fr; gap: var(--space-6); align-items: center; }
/* Extra room before the two CTA buttons -- h2 has no lede paragraph here to create
   that gap the way .split-copy normally does elsewhere. */
.city-grid .split-copy h2 { margin-bottom: var(--space-4); }
/* Mobile: the polaroid used to render after the map in DOM order and just stacked below it.
   Instead we lift it out of .city-map-wrap (display: contents removes only its own box, not
   its children) so it becomes a direct .city-grid item placeable next to the copy/button
   block, top row, while the map itself still spans a full-width row underneath. */
@media (max-width: 900px) {
  .city-grid {
    grid-template-columns: 1fr auto;
    grid-template-areas: "copy card" "map map";
    row-gap: var(--space-4);
    justify-items: stretch;
  }
  .city-grid .split-copy { grid-area: copy; }
  /* CTA is redundant with the hero/header download buttons already above the fold on
     mobile -- dropping it lets the map+photo row move up and close the gap it left. */
  .city-grid .split-copy .btn-row { display: none; }
  .city-map-wrap { display: contents; }
  .city-grid .city-map { grid-area: map; justify-self: center; margin-top: 0; }
  .city-grid .city-map-card {
    grid-area: card;
    position: static;
    align-self: end;
    justify-self: end;
    width: 92px;
    padding: 6px 6px 10px;
    transform: rotate(3deg);
  }
  .city-grid .city-map-card-img { height: 78px; }
  .city-grid .city-map-card-label { margin-top: 5px; gap: 2px; }
  .city-grid .city-map-name { font-size: 0.72rem; }
  .city-grid .city-map-badge { font-size: 0.52rem; padding: 2px 6px; }
}

/* Section watermark — a single city photo, desaturated and near-invisible, behind the white section */
.city-section { position: relative; overflow: hidden; }
.city-section-bg { position: absolute; inset: 0; z-index: 0; }
.city-section-bg img { width: 100%; height: 100%; object-fit: cover; object-position: center; filter: grayscale(0.35); opacity: 0.22; }
.city-section .container { position: relative; z-index: 1; }

/* Germany map — 7 city pins at real geographic positions (Natural Earth 110m outline,
   public domain), each with a subtle pulsing ring on the active pin. Clicking a pin (or
   the auto-advance timer) swaps the shared photo card below. Paused on hover/focus. */
.city-map-wrap { position: relative; width: 100%; max-width: 480px; margin-top: var(--space-4); }
.city-map { position: relative; width: 300px; max-width: 100%; aspect-ratio: 295 / 400; }
@media (max-width: 480px) { .city-map { width: 230px; } }
.city-map-svg { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
.city-map-outline { fill: var(--surface); stroke: var(--border); stroke-width: 2; }
.city-map-pin {
  all: unset; position: absolute; width: 40px; height: 40px;
  margin: -20px 0 0 -20px; /* center the icon exactly on the geographic pin coordinate */
  cursor: pointer;
}
.city-map-pin-icon {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: contain;
  filter: drop-shadow(0 2px 5px rgba(0,0,0,0.3)) grayscale(0.4) brightness(0.92);
  transition: transform 400ms cubic-bezier(0.22, 0.61, 0.36, 1), filter 400ms ease;
}
.city-map-pin-ring {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 24px; height: 24px; border-radius: 50%;
  background: var(--accent); opacity: 0;
}
.city-map-pin.is-active { z-index: 2; }
.city-map-pin.is-active .city-map-pin-icon {
  transform: scale(1.55) translateY(-4px);
  filter: drop-shadow(0 8px 16px rgba(0,0,0,0.35));
}
.city-map-pin.is-active .city-map-pin-ring { animation: city-pin-pulse 2200ms ease-out infinite; }
@keyframes city-pin-pulse {
  0% { opacity: 0.4; transform: translate(-50%, -50%) scale(1); }
  100% { opacity: 0; transform: translate(-50%, -50%) scale(2.4); }
}
@media (prefers-reduced-motion: reduce) { .city-map-pin-ring { animation: none !important; } }
.city-map-pin:focus-visible .city-map-pin-icon { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 8px; }

.city-map-pin-label {
  position: absolute; top: 50%; transform: translateY(-50%);
  white-space: nowrap; pointer-events: none;
  font-size: 0.68rem; font-weight: 600; letter-spacing: 0.01em; color: var(--ink);
  background: rgba(255, 255, 255, 0.85); padding: 2px 7px; border-radius: var(--radius-pill);
  box-shadow: 0 1px 4px rgba(26, 31, 54, 0.12);
}
.city-map-pin-label.label-right { left: calc(100% + 4px); }
.city-map-pin-label.label-left { right: calc(100% + 4px); }
@media (max-width: 480px) { .city-map-pin-label { font-size: 0.62rem; padding: 2px 6px; } }

/* Photo card — styled as a polaroid print, tucked into the lower-right corner of the map
   so it reads as a photo just dropped onto the country outline, not a caption strip below it. */
.city-map-card {
  position: absolute; right: -6%; bottom: 14%; z-index: 3;
  display: flex; flex-direction: column; align-items: center;
  width: 216px; background: var(--surface);
  padding: 12px 12px 18px; border-radius: 3px;
  box-shadow: 0 18px 34px rgba(26, 31, 54, 0.26), 0 3px 8px rgba(26, 31, 54, 0.12);
  transform: rotate(4deg); transform-origin: center bottom;
}
.city-map-card-img {
  width: 100%; height: 192px; border-radius: 2px; object-fit: cover;
}
.city-map-card.is-revealing { animation: city-polaroid-drop 480ms cubic-bezier(0.22, 0.61, 0.36, 1); }
@keyframes city-polaroid-drop {
  0% { opacity: 0; transform: translateY(-22px) rotate(-4deg) scale(0.92); }
  100% { opacity: 1; transform: translateY(0) rotate(4deg) scale(1); }
}
@media (prefers-reduced-motion: reduce) { .city-map-card.is-revealing { animation: none !important; } }
.city-map-card-label { display: flex; flex-direction: column; align-items: center; gap: 5px; margin-top: 10px; }
.city-map-name { font-family: var(--font-display); font-weight: 600; color: var(--ink); font-size: 1.05rem; }
.city-map-badge {
  font-size: 0.65rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em;
  color: #fff; background: var(--accent); padding: 3px 9px; border-radius: var(--radius-pill);
}
/* .city-map-wrap's own <600px stacking rule is superseded by the .city-grid-level
   `display: contents` layout above (applies from 900px down), which already places the map
   and the polaroid independently. */

/* Per-pin geographic position (kept in the stylesheet, not inline style="", because the
   page's CSP is style-src 'self' and blocks inline style attributes silently). */
.city-map-pin-0 { left: 82.15%; top: 32.07%; }
.city-map-pin-1 { left: 44.35%; top: 18.66%; }
.city-map-pin-2 { left: 61.88%; top: 89.13%; }
.city-map-pin-3 { left: 12.5%; top: 55.5%; }
.city-map-pin-4 { left: 6.0%; top: 46.0%; }
.city-map-pin-5 { left: 29.83%; top: 66.5%; }
.city-map-pin-6 { left: 35.37%; top: 80.83%; }

/* Waitlist form */
.waitlist-card {
  max-width: 560px; margin: 0 auto;
  background: rgba(255,255,255,0.96);
  border-radius: var(--radius);
  padding: var(--space-4);
  color: var(--ink);
  text-align: left;
}
/* Waitlist track toggle — which track (city vs. Android) the signup is for.
   Real radios, visually hidden, so it stays a native, keyboard- and
   screen-reader-operable radio group; the label is the visible pill,
   styled like .chip but with the accent-filled "active" state DESIGN.md
   reserves for primary actions. min-height on the label (not just padding)
   guarantees the 44px tap target regardless of the small pill font-size. */
.waitlist-track { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 var(--space-3); border: none; padding: 0; }
.waitlist-track input[type="radio"] {
  position: absolute; opacity: 0; width: 1px; height: 1px; overflow: hidden;
}
.waitlist-track label {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 44px; padding: 8px 18px; border-radius: var(--radius-pill);
  border: 1px solid var(--border); background: var(--surface);
  font-size: 0.85rem; font-weight: 600; color: var(--ink-muted);
  cursor: pointer; transition: var(--motion-fast);
}
.waitlist-track input[type="radio"]:checked + label {
  border-color: var(--accent); background: var(--accent); color: var(--accent-ink);
}
.waitlist-track input[type="radio"]:focus-visible + label {
  outline: 3px solid var(--accent); outline-offset: 2px;
}
.form-note { font-size: 0.85rem; color: var(--ink-muted); margin: 0 0 var(--space-2); }

.form-row { display: grid; grid-template-columns: 1.3fr 1fr; gap: var(--space-2); margin-bottom: var(--space-2); }
@media (max-width: 560px) { .form-row { grid-template-columns: 1fr; } }
.form-field { display: flex; flex-direction: column; gap: 6px; margin-bottom: var(--space-3); }
.form-field[hidden] { display: none; }
.form-field label { font-size: 0.85rem; font-weight: 600; color: var(--ink-muted); }
.form-field input, .form-field select {
  font: inherit; padding: 12px 14px; border-radius: 12px;
  border: 1px solid var(--border); background: var(--bg); color: var(--ink); min-height: 44px;
}
.form-field input:focus-visible, .form-field select:focus-visible { outline: 3px solid var(--accent); outline-offset: 1px; }
.honeypot-field { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
#partner-form .btn-primary { margin-top: var(--space-3); }
#launch-signup-form .btn-primary { margin-top: var(--space-5); }
.waitlist-card .btn { width: 100%; border: none; margin-top: 4px; }
.waitlist-card .btn:disabled { opacity: 0.6; cursor: not-allowed; }
.form-error, .form-success { font-size: 0.9rem; margin-top: var(--space-2); }
.form-error { color: var(--danger); }
.form-success { color: var(--ink); font-weight: 600; }
.form-privacy { font-size: 0.82rem; color: var(--ink-muted); text-align: left; margin-top: var(--space-3); }
.form-privacy a { color: var(--ink); }

/* Footer */
.site-footer { border-top: 1px solid var(--border); padding: var(--space-6) 0 var(--space-4); }
.footer-grid { display: flex; flex-wrap: wrap; gap: var(--space-6); justify-content: space-between; }
.footer-brand { max-width: 320px; display: flex; flex-direction: column; gap: var(--space-2); }
.footer-brand .logo { display: inline-flex; align-items: center; gap: 8px; }
.footer-brand p { color: var(--ink-muted); font-size: 0.9rem; }
.footer-cols { display: flex; flex-wrap: wrap; gap: var(--space-6); flex: 1; justify-content: flex-end; }
.footer-col { display: flex; flex-direction: column; gap: 10px; min-width: 150px; }
.footer-col h3 {
  font-size: 0.8125rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--ink); margin-bottom: 2px;
}
.footer-col a { text-decoration: none; color: var(--ink-muted); font-size: 0.9rem; }
.footer-col a:hover { color: var(--ink); }
.footer-bottom {
  display: flex; flex-wrap: wrap; gap: var(--space-2); justify-content: space-between;
  margin-top: var(--space-5); padding-top: var(--space-4); border-top: 1px solid var(--border);
  font-size: 0.82rem; color: var(--ink-muted);
}
@media (max-width: 700px) {
  .footer-grid { flex-direction: column; }
  .footer-cols { justify-content: flex-start; }
}

/* Utility classes replacing inline style="" attributes (blocked by style-src 'self' CSP) */
.section-surface { background: var(--surface); }
/* Extra clearance below the fixed floating nav for a page's first section on
   content-only subpages (no full-bleed hero to sit behind the nav instead). */
.page-hero { padding-top: calc(var(--space-7) + var(--space-5)); }
.waitlist-bleed { align-items: center; min-height: 86vh; }
.waitlist-bleed .hero-scrim {
  background: linear-gradient(180deg, rgba(10,13,22,0.58) 0%, rgba(10,13,22,0.74) 45%, rgba(10,13,22,0.9) 100%);
}
.mark-centered { margin: 0 auto var(--space-3); display: block; }
.waitlist-heading { color: #fff; font-size: clamp(1.75rem, 3.4vw, 2.4rem); }
.waitlist-lede { margin: var(--space-2) auto var(--space-4); }

/* Scroll reveal (fade/slide) */
.reveal { opacity: 0; transform: translateY(12px); transition: opacity var(--motion-reveal), transform var(--motion-reveal); }
.reveal.is-visible { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; transition: none; }
}

/* Explore page: curated Instagram embed grid */
.explore-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--space-4);
  margin-top: var(--space-4);
}
.explore-embed-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-float);
  overflow: hidden;
  padding: var(--space-2);
}
.explore-embed-card iframe { width: 100% !important; }
.explore-empty {
  grid-column: 1 / -1;
  text-align: center;
  color: var(--ink-muted);
  padding: var(--space-6) 0;
}

/* B2B / partner pages */
.partner-hero { display: grid; gap: var(--space-5); align-items: center; padding: var(--space-7) 0 var(--space-6); }
.partner-hero h1 { font-size: clamp(2.25rem, 4vw, 3rem); max-width: 14ch; margin-bottom: var(--space-3); }
.partner-hero-copy .lede { margin-bottom: var(--space-4); }
.partner-hero-media { position: relative; }
.partner-hero-media img { width: 100%; height: auto; border-radius: var(--radius); display: block; }
/* Launch-day chip sits above the hero photo (not overlaid on it) -- plain .chip
   (no rarity variant) so it doesn't introduce a new accent color on top of the
   page's palette. */
/* margin-top (not -bottom): the chip now sits below the image+device instead of above it. */
.partner-hero-chip { display: flex; width: fit-content; margin-left: auto; margin-top: var(--space-3); box-shadow: var(--shadow-float); }
@media (max-width: 640px) { .partner-hero-chip { font-size: 0.78rem; padding: 5px 10px; } }
@media (min-width: 860px) { .partner-hero { grid-template-columns: 1.1fr 0.9fr; } }
/* flex-basis 0 (equal-width) forced text inside each button to wrap once both didn't fit
   the row at once; auto-basis + nowrap keeps each label on one line and lets .btn-row's
   existing flex-wrap drop the second button to its own full-width line instead when needed. */
@media (max-width: 640px) { .partner-hero-copy .btn-row .btn { flex: 1 1 auto; white-space: nowrap; } }

/* App-screenshot device frame overlapping the hero photo's bottom-right corner, same
   black-bezel style as .hero-device-frame. No CSS-drawn notch: the screenshot already
   bakes in the real Dynamic Island. */
.partner-hero-device { position: absolute; z-index: 3; bottom: -66px; right: -46px; width: 150px; }
.partner-hero-device-bezel {
  position: relative;
  background: #0a0a0a;
  border-radius: 26px;
  padding: 8px;
  box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.12), 0 26px 52px rgba(0, 0, 0, 0.4);
}
.partner-hero-device-screen { position: relative; border-radius: 19px; overflow: hidden; aspect-ratio: 662 / 1440; background: #000; }
.partner-hero-device-screen img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
@media (max-width: 640px) { .partner-hero-device { width: 110px; right: -14px; bottom: -18px; } }

.benefit-list { display: grid; gap: var(--space-4); margin-top: var(--space-4); }
@media (min-width: 760px) { .benefit-list { grid-template-columns: repeat(3, 1fr); } }
/* Partner page has only 2 benefit items -- repeat(3,1fr) would leave a gap. */
@media (min-width: 760px) { .benefit-list--two { grid-template-columns: repeat(2, 1fr); } }
/* "Worum geht es?" trio (de/partner.html) as boxed cards, matching the partner PDF's
   card treatment -- opt-in modifier so the plain-text .benefit-item usage elsewhere
   (numbered "So wirst du Partner" steps, other pages) stays untouched. */
.benefit-list--cards .benefit-item {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  padding: var(--space-4);
}
.benefit-index {
  font-family: var(--font-display); font-size: 1.6rem; color: var(--accent); display: block;
  padding-bottom: var(--space-2); margin-bottom: var(--space-2); border-bottom: 2px solid var(--border);
}
.benefit-item h3 { font-size: 1.05rem; margin-bottom: var(--space-1); }
.benefit-item p { color: var(--ink-muted); font-size: 0.95rem; line-height: 1.55; }
/* "Was gewinnst du? / Was kostet es?" (de/partner.html) run a size step up from the
   default .benefit-item card -- opt-in so other .benefit-list--cards usage is untouched. */
.benefit-list--lg .benefit-item { padding: var(--space-5); }
.benefit-list--lg .benefit-item h3 { font-size: 1.4rem; }
.benefit-list--lg .pricing-features { font-size: 1rem; }

/* Value-flow: who brings in / gets what between Restaurants, TSTE and Kunden, as a wide
   horizontal strip below the cost/benefit boxes -- plain CSS, no rendered video. The
   connector's moving dot is the only "animation", same lightweight approach as the
   .route-reveal-stroke path animation used for the Berlin map elsewhere on this page. */
.value-flow {
  display: grid; grid-template-columns: 1fr auto 1fr auto 1fr; align-items: start;
  gap: var(--space-3); max-width: 860px; margin: var(--space-6) auto 0;
}
.value-flow-node { text-align: center; }
.value-flow-node h3 { font-size: 1.05rem; margin-bottom: 4px; }
.value-flow-node p { color: var(--ink-muted); font-size: 0.92rem; line-height: 1.5; margin: 0; }
.value-flow-node-tste h3 { color: var(--accent); }
/* Restaurant: the bare map-pin shape from the app's mission pins (MissionMapPin.swift) --
   white head with an accent ring, small accent triangle tail, no cuisine flag/icon inside. */
.value-flow-icon { display: block; margin: 0 auto var(--space-2); }
.value-flow-icon-pin { position: relative; width: 34px; height: 44px; }
.value-flow-pin-head {
  position: absolute; top: 0; left: 50%; transform: translateX(-50%);
  width: 34px; height: 34px; border-radius: 50%;
  background: var(--surface); border: 3px solid var(--accent); box-shadow: var(--shadow-float);
}
.value-flow-pin-tail {
  position: absolute; top: 27px; left: 50%; transform: translateX(-50%);
  width: 0; height: 0; border-left: 7px solid transparent; border-right: 7px solid transparent;
  border-top: 12px solid var(--accent);
}
/* TSTE: the header wordmark, recolored onto a dark badge instead of .logo's currentColor
   pairing, since the accent-red sparkle details need contrast against a circle, not text. */
.value-flow-icon-logo {
  display: flex; align-items: center; justify-content: center;
  width: 46px; height: 46px; margin: 0 auto var(--space-2);
  border-radius: 50%; background: var(--ink); box-shadow: var(--shadow-float);
}
.value-flow-icon-logo svg { width: 27px; height: 27px; color: var(--accent-ink); }
/* Kunden: the player's chef avatar, cropped to a circular portrait like the TSTE badge. */
.value-flow-icon-avatar {
  width: 46px; height: 46px; border-radius: 50%; overflow: hidden;
  box-shadow: var(--shadow-float);
}
.value-flow-icon-avatar img { width: 100%; height: 100%; object-fit: cover; }
.value-flow-connector { position: relative; align-self: center; width: 40px; height: 2px; background: var(--border); margin-top: 35px; }
.value-flow-connector span {
  position: absolute; top: 50%; left: 0; width: 6px; height: 6px; border-radius: 50%;
  background: var(--accent); transform: translate(-50%, -50%);
  animation: value-flow-dot 2.6s ease-in-out infinite;
}
.value-flow-connector:last-of-type span { animation-delay: 1.3s; }
@keyframes value-flow-dot {
  0%, 100% { left: 0; opacity: 0; }
  15% { opacity: 1; }
  85% { opacity: 1; }
  100% { left: 100%; opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .value-flow-connector span { animation: none; left: 50%; opacity: 1; }
}
/* Mobile: stack Restaurants -> TSTE -> Kunden, connectors rotate to short vertical bars. */
@media (max-width: 639px) {
  .value-flow { grid-template-columns: 1fr; max-width: 280px; gap: var(--space-3); justify-items: center; }
  .value-flow-connector { width: 2px; height: 24px; margin-top: 0; }
  .value-flow-connector span { top: 0; left: 50%; animation-name: value-flow-dot-vertical; }
  @keyframes value-flow-dot-vertical {
    0%, 100% { top: 0; opacity: 0; }
    15% { opacity: 1; }
    85% { opacity: 1; }
    100% { top: 100%; opacity: 0; }
  }
}

/* Partner pricing tiers -- Bronze/Silber/Gold/Platin. 2-col at tablet width avoids a
   cramped 4-up squeeze before there's room for it. Grid items stretch to equal height
   (default align-items) so the chip/note/button group at the bottom of each card lines
   up despite differing feature-list lengths -- .pricing-card's own margin-top: auto on
   the chip absorbs the leftover space. */
.pricing-grid { display: grid; gap: var(--space-4); margin-top: var(--space-6); }
@media (min-width: 640px) { .pricing-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) { .pricing-grid { grid-template-columns: repeat(3, 1fr); } }
.pricing-card {
  display: flex; flex-direction: column;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  padding: var(--space-4);
}
/* Gold stays the visually "hero" tier even with Platin priced above it -- dark card,
   same treatment as .story-section, so it reads as the obvious best-value pick. */
.pricing-card-featured { background: var(--ink); border-color: var(--ink); box-shadow: var(--shadow-float); }
.pricing-card-featured h3 { color: var(--bg); }
.pricing-card-featured .pricing-features { color: #C7CBD6; }
.pricing-card-featured .pricing-card-note { color: #C7CBD6; }
.pricing-card h3 { font-size: 1.3rem; margin-top: var(--space-2); }
.pricing-card .chip { margin-top: auto; align-self: flex-start; }
.pricing-card-note { color: var(--ink-muted); font-size: 0.85rem; margin: var(--space-2) 0 0; }
.pricing-note { text-align: center; color: var(--ink-muted); font-size: 0.9rem; margin-top: var(--space-4); }
.pricing-features { list-style: none; margin: var(--space-3) 0; padding: 0; display: grid; gap: 8px; color: var(--ink-muted); font-size: 0.92rem; }
.pricing-features li { padding-left: 20px; position: relative; }
.pricing-features li::before {
  content: ''; position: absolute; left: 0; top: 0.5em;
  width: 8px; height: 8px; border-radius: 50%; background: var(--accent);
}
.pricing-card-featured .pricing-features li::before { background: var(--reward); }
.pricing-select { margin-top: var(--space-3); text-align: center; }

/* Decorative food-photo marquee (de/partner.html): the track holds the image set twice
   back to back and scrolls exactly one set-width via translateX(-50%), so the loop is
   seamless with no JS. Images are fixed-width (not flex:1) because the animation needs
   a stable track width to compute against. */
/* Full-bleed: .food-strip is a direct child of <section>, outside .container, so it
   spans the full viewport width instead of being boxed in -- no border-radius, since
   the strip now runs edge to edge with nothing to round against. */
/* Extra room above separates the strip from the value-flow content it follows. A negative
   bottom margin (plus .section-flush-bottom dropping this section's own bottom padding)
   pulls the next section's (Partner-Tag) photo backdrop up over the strip's bottom edge,
   so the two sections visibly overlap instead of just sitting flush with no gap. */
.food-strip { margin-top: var(--space-7); margin-bottom: -64px; overflow: hidden; }
.section-flush-bottom { padding-bottom: 0; }
@media (max-width: 640px) { .food-strip { margin-bottom: -40px; } }
.food-strip-track {
  display: flex; gap: var(--space-2); width: max-content;
  animation: food-strip-scroll 90s linear infinite;
}
.food-strip-track img { width: 160px; height: 160px; flex: none; object-fit: cover; border-radius: var(--radius); }
@keyframes food-strip-scroll {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}
@media (prefers-reduced-motion: reduce) { .food-strip-track { animation: none; } }
@media (max-width: 640px) { .food-strip-track { gap: 4px; } .food-strip-track img { width: 110px; height: 110px; } }
.form-card { max-width: 560px; margin: 0 auto; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: var(--space-4); }
.form-field textarea {
  font: inherit; padding: 12px 14px; border-radius: 12px;
  border: 1px solid var(--border); background: var(--bg); color: var(--ink); min-height: 100px; resize: vertical;
}
.form-field textarea:focus-visible { outline: 3px solid var(--accent); outline-offset: 1px; }

/* Berlin launch event page (one-time campaign, Oct 18) */
.launch-hero { max-width: 480px; text-align: left; padding: var(--space-7) 0 var(--space-5); }
.launch-hero h1 { font-size: clamp(2.1rem, 4vw, 2.9rem); margin-bottom: var(--space-3); }
.launch-hero .lede { margin: 0 0 var(--space-4); }
.launch-restaurant-note { max-width: 480px; margin: 0 auto; text-align: center; }
.launch-restaurant-note h2 { font-size: 1.3rem; margin-bottom: var(--space-2); }
.launch-restaurant-note p { color: var(--ink-muted); margin-bottom: var(--space-3); }

/* Text left / animated map right on tablet+, stacked (source order) on mobile. */
.launch-hero-grid { display: grid; gap: var(--space-6); align-items: center; }
@media (min-width: 860px) {
  .launch-hero-grid { grid-template-columns: 1.05fr 0.95fr; gap: var(--space-7); }
}
.launch-hero-map { max-width: 480px; margin: 0 auto; }
@media (min-width: 860px) { .launch-hero-map { max-width: none; margin: 0; } }

/* Generic media+text row (partner page): photo/map on one side, short copy
   on the other -- same two-column pattern as .launch-hero-grid, reused
   outside the hero (no bg photo, no eyebrow/H1) and reused across several
   sections (benefit items, "what's expected", the map explainer), so the
   class stays generic rather than named after any one of them. Source order
   decides the side: whichever markup comes first lands in the wider left
   column -- rows alternate media-left/media-right by swapping which child
   comes first in the HTML, no modifier class needed. */
.media-row { display: grid; gap: var(--space-6); align-items: center; }
@media (min-width: 860px) {
  .media-row { grid-template-columns: 1.05fr 0.95fr; gap: var(--space-7); }
}
/* width:100% is required, not just max-width: a grid item with auto margins
   gets shrink-to-fit sizing instead of stretching to its track, and a child
   like .route-map (only absolutely-positioned content, so it has no in-flow
   box to derive an intrinsic width from) then collapses to 0x0 -- same
   collapse family as the .media-phone-shot.slideshow-bg sizing trap
   documented on the launch-berlin pages, one level up the tree. */
.media-row-media { width: 100%; max-width: 480px; margin: 0 auto; }
@media (min-width: 860px) { .media-row-media { max-width: none; margin: 0; } }
.media-row-text { max-width: 480px; margin: 0 auto; display: grid; gap: var(--space-4); }
@media (min-width: 860px) { .media-row-text { max-width: none; margin: 0; } }
/* Mobile-only reorder: some .media-row sections put the media first in DOM
   so it lands in the left column on desktop, but read better with the text
   (heading/lede/benefits) before the visual once stacked on mobile. Scoped to
   max-width so it never touches the >=860px column layout above. */
@media (max-width: 859px) {
  .media-row--text-first-mobile .media-row-media { order: 2; }
  .media-row--text-first-mobile .media-row-text { order: 1; }
}
/* Plain landscape photo (not the interactive .route-map or a phone
   screenshot) -- cropped to a consistent frame via object-fit:cover. */
/* height:auto is required here -- the <img> width/height HTML attributes
   act as low-priority presentational hints that otherwise fix the height
   at its native pixel value, leaving no auto dimension for aspect-ratio
   to compute and silently overriding the intended crop. */
.media-photo { display: block; width: 100%; height: auto; aspect-ratio: 4 / 3; object-fit: cover; border-radius: var(--radius); box-shadow: var(--shadow-float); }
/* Phone screenshot -- left uncropped (object-fit:cover would chop off most
   of a portrait screen inside a landscape frame), just height-constrained.
   Framed in .phone-bezel (below) so it reads as a phone rather than a
   floating screenshot -- same dark-bezel language as .hero-device-frame
   and .path-slide-frame elsewhere on the site, sized responsively instead
   of their fixed pixel dimensions since this lives in a flexible column. */
.media-phone-shot { display: block; max-height: 424px; width: auto; max-width: 100%; border-radius: 19px; }
/* Slideshow variant: cycles multiple screenshots via the generic .slideshow-bg
   cross-fade (js/main.js), same technique as .hero-device-screen. The images are
   position:absolute (from .slideshow-bg img), so the container needs its own
   intrinsic size via aspect-ratio instead of inheriting it from a single <img>. */
.media-phone-shot.slideshow-bg { position: relative; width: 195px; aspect-ratio: 662 / 1440; overflow: hidden; }
.media-phone-shot.slideshow-bg img { width: 100%; height: 100%; object-fit: cover; }
.media-phone-shot.slideshow-bg video { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Real footage (9:16) is a different, wider aspect than the 662:1440 app
   screenshots the container is otherwise sized for -- cover would crop its
   sides. Override to the clip's own ratio so nothing gets cut off. */
.media-phone-shot.slideshow-bg.media-phone-shot--video { aspect-ratio: 9 / 16; }
/* Small per-clip sound toggle for the inline Verabreden videos -- same
   muted-by-default + tap-to-unmute pattern as .hero-video-sound, just
   scaled down to fit inside a 195px phone mockup instead of a full-bleed
   hero panel. */
.inline-video-sound {
  position: absolute; z-index: 1; left: 50%; bottom: 10px; transform: translateX(-50%);
  display: flex; align-items: center; gap: 4px; white-space: nowrap;
  background: rgba(10,13,22,0.72); color: #fff; border: none; border-radius: 999px;
  padding: 5px 10px; font-family: var(--font-body); font-size: 11px; font-weight: 600;
  cursor: pointer; transition: background 200ms ease;
}
.inline-video-sound[data-unmuted] { background: rgba(199, 62, 29, 0.85); }
.phone-bezel {
  display: block; width: fit-content; margin: 0 auto;
  background: #0a0a0a; padding: 8px; border-radius: 26px;
  box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.12), 0 20px 40px rgba(10, 13, 22, 0.35), var(--shadow-float);
}
/* Compact variants: for a supporting illustration photo (not the focal,
   feature-carrying interactive map), a narrow fixed media column reads
   better than the ~50/50 split -- text gets the rest of the row's width.
   Two widths (plain --compact and the slightly larger --compact-lg) share
   one rule set via --media-col. Placed after the base rules so they win
   at equal specificity. */
.media-row--compact { --media-col: 200px; gap: var(--space-5); }
.media-row--compact-lg { --media-col: 300px; gap: var(--space-5); }
@media (min-width: 860px) {
  .media-row--compact, .media-row--compact-lg { grid-template-columns: var(--media-col) 1fr; align-items: center; gap: var(--space-6); }
}
.media-row--compact .media-row-media, .media-row--compact-lg .media-row-media { max-width: var(--media-col); margin: 0 auto; }
@media (min-width: 860px) {
  .media-row--compact .media-row-media, .media-row--compact-lg .media-row-media { max-width: var(--media-col); margin: 0; }
}
/* Media-right variant: text comes first in the DOM (so it reads first on
   mobile and lands in the wide column on desktop), the narrow media column
   sits on the right edge instead of the left. Same --media-col, just the
   two grid-template-columns tracks swapped. */
@media (min-width: 860px) {
  .media-row--compact.media-row--media-right, .media-row--compact-lg.media-row--media-right { grid-template-columns: 1fr calc(var(--media-col) + 100px); }
  .media-row--compact.media-row--media-right .media-row-media, .media-row--compact-lg.media-row--media-right .media-row-media { margin: 0 auto; }
  /* Phone itself also grows on desktop -- the wider column above otherwise
     just leaves empty space around a phone still sized for the narrow
     --compact column it was borrowed from. */
  .media-row--media-right .media-phone-shot.slideshow-bg { width: 260px; }
}
.media-row-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-3); }
.media-row-list li { position: relative; padding-left: 28px; color: var(--ink-muted); line-height: 1.55; }
.media-row-list li::before {
  content: ""; position: absolute; left: 0; top: 0.4em;
  width: 14px; height: 14px; border-radius: 50%; background: var(--accent);
}
.media-row-list strong { color: var(--ink); }

/* Translucent Berlin photo backdrop, scoped to the hero visual wrapper (text
   + map grid) -- position:relative/overflow:hidden on the wrapper, not the
   whole page-hero section, so the bg layer's inset:0 sizes to exactly this
   content instead of bleeding into unrelated sections. */
/* Chef's-Path steps slideshow layered over the former "Dein Restaurant" hero photo,
   now a transparent backdrop instead of its own text-over-photo section -- same
   absolute-photo-behind-content stacking as .launch-hero-visual below. */
.chefs-path-steps-bg { position: relative; overflow: hidden; }
.chefs-path-steps-photo { position: absolute; inset: 0; z-index: 0; opacity: 0.22; pointer-events: none; }
.chefs-path-steps-photo img { width: 100%; height: 100%; object-fit: cover; }
.chefs-path-steps-bg > .container { position: relative; z-index: 1; }

.launch-hero-visual { position: relative; overflow: hidden; }
.launch-hero-bg {
  position: absolute; inset: 0; z-index: 0;
  background-image: url('../images/brand/launch-berlin-photo.webp');
  background-size: cover;
  background-position: center 55%;
  opacity: 0.22;
  pointer-events: none;
}
.launch-hero-visual > .container { position: relative; z-index: 1; }
/* Partner-Tag section (de/partner.html) reuses .launch-hero-bg as a subtle backdrop
   -- same stacking as .launch-hero-visual, just scoped to #partner-tag directly since
   the section has no separate "-visual" wrapper. */
#partner-tag { position: relative; overflow: hidden; }
#partner-tag > .container { position: relative; z-index: 1; }
/* The text column sits directly over .launch-hero-bg (opacity:0.22 photo) --
   a translucent panel keeps body copy readable against the backdrop. */
#partner-tag .media-row-text {
  background: rgba(255, 255, 255, 0.6);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  border-radius: var(--radius);
  padding: var(--space-5);
  box-shadow: var(--shadow-float);
}

.route-map {
  position: relative; width: 100%; max-width: 640px; margin: 0 auto;
  aspect-ratio: 896 / 1120;
  border-radius: var(--radius);
  overflow: hidden;
  box-shadow: var(--shadow-float);
  z-index: 1;
}
/* Small teaser version in the hero (no head/explanation text), full size +
   explanation lives in its own section further down the page. Pin labels and
   the hover/tap food-photo reveal are shared with the full map below. */
.route-map--compact { max-width: 300px; }
.route-map--compact .route-pin-dot {
  width: 14px; height: 14px;
  box-shadow: 0 0 0 2px var(--surface), 0 2px 6px rgba(26,31,54,0.3);
}
.route-map-bg { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
.route-map-svg { position: absolute; inset: 0; width: 100%; height: 100%; display: block; z-index: 1; }
.route-path {
  fill: none; stroke: var(--accent); stroke-width: 5; stroke-linecap: round;
  stroke-dasharray: 8 10; /* static footprint texture -- the mask below controls
    how much of it is revealed, this element itself never animates */
}
/* Progressive reveal: a short repeating dasharray (8 10) can't itself do a
   one-time start-to-end reveal via stroke-dashoffset -- shifting it by a whole
   number of periods looks identical to no movement (confirmed: the property
   was animating correctly but the visible pattern repeats every 18 units, so
   large jumps land back near the same phase). Fix: keep .route-path's texture
   static and reveal it through a <mask> whose own stroke uses the classic
   single-dash SVG line-draw trick (dasharray = path length, dashoffset animates
   from full length to 0) -- that technique doesn't have a repeating period, so
   it genuinely draws from start to end, one footprint appearing after another. */
.route-reveal-stroke {
  fill: none; stroke: #fff; stroke-width: 24; stroke-linecap: round;
  stroke-dasharray: 1730; stroke-dashoffset: 1730;
  animation: route-walk 10s steps(48, jump-end) infinite;
}
@keyframes route-walk {
  0% { stroke-dashoffset: 1730; }
  70% { stroke-dashoffset: 0; }
  100% { stroke-dashoffset: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .route-reveal-stroke { animation: none; stroke-dashoffset: 0; }
}

/* .route-pin is a <details> element -- gives every pin native, no-JS
   click/tap-toggle and keyboard support (Enter/Space) for free, which the
   food-photo reveal below hooks into via the [open] attribute. Desktop also
   gets an instant :hover preview, independent of the open/closed state. */
.route-pin { position: absolute; z-index: 2; transform: translate(-50%, -50%); margin: 0; }
.route-pin summary {
  list-style: none; cursor: pointer;
  display: flex; flex-direction: column; align-items: center; gap: 5px;
}
.route-pin summary::-webkit-details-marker { display: none; }
.route-pin-dot {
  position: relative;
  width: 26px; height: 26px; border-radius: 50%;
  background: var(--accent); color: #fff;
  display: flex; align-items: center; justify-content: center;
  font-size: 0.72rem; font-weight: 700;
  box-shadow: 0 0 0 3px var(--surface), 0 4px 10px rgba(26,31,54,0.3);
}
/* Pulsing ring: the only hint that pins are interactive (no hover on touch,
   so this has to carry the affordance on mobile too). Scales from the dot's
   own box via inset:0, so it works unmodified at both dot sizes (26px full
   map, 14px compact map) without a size override. */
.route-pin-dot::after {
  content: "";
  position: absolute; inset: 0; border-radius: 50%;
  background: var(--accent);
  opacity: 0.5;
  animation: route-pin-pulse 2200ms ease-out infinite;
  pointer-events: none;
}
@keyframes route-pin-pulse {
  0% { opacity: 0.5; transform: scale(1); }
  100% { opacity: 0; transform: scale(2.2); }
}
@media (prefers-reduced-motion: reduce) {
  .route-pin-dot::after { animation: none; opacity: 0; }
}
.route-pin-label {
  font-size: 0.68rem; font-weight: 700; color: var(--ink);
  background: var(--surface); border: 1px solid var(--border);
  padding: 3px 9px; border-radius: var(--radius-pill);
  white-space: nowrap;
}
/* Fixed left/top per pin -- CSS classes, not inline style="", because the
   page CSP is style-src 'self' (no unsafe-inline): an inline style attribute
   is silently dropped by the browser, so the pin renders at 0,0. */
.route-pin-1 { left: 18.1%; top: 50%; }
.route-pin-2 { left: 48%; top: 25%; }
.route-pin-3 { left: 78%; top: 25%; }
.route-pin-4 { left: 70%; top: 52%; }
.route-pin-5 { left: 78%; top: 68%; }
.route-pin-6 { left: 40%; top: 72%; }

/* Food-photo reveal: centered over the whole map card rather than a small
   tooltip anchored to each pin, so it never overflows .route-map's
   overflow:hidden rounded card -- especially on the 300px compact map.
   Hover shows a non-interactive preview (pointer-events stay off, so the
   overlay never steals :hover away from the pin underneath and flickers);
   tap sets [open] via the native <details> toggle, which is what makes it
   interactive and is how touch devices (no real :hover) show/dismiss it. */
.route-food-photo {
  position: absolute; inset: 0; z-index: 3;
  display: flex; align-items: center; justify-content: center;
  background: rgba(26, 31, 54, 0.55);
  opacity: 0; pointer-events: none;
  transition: opacity 150ms ease;
}
.route-food-photo img {
  max-width: 68%; max-height: 68%;
  border-radius: var(--radius);
  box-shadow: var(--shadow-float);
  object-fit: cover;
}
.route-pin-1:hover ~ .route-food-photo-1,
.route-pin-2:hover ~ .route-food-photo-2,
.route-pin-3:hover ~ .route-food-photo-3,
.route-pin-4:hover ~ .route-food-photo-4,
.route-pin-5:hover ~ .route-food-photo-5,
.route-pin-6:hover ~ .route-food-photo-6 {
  opacity: 1;
}
.route-pin-1[open] ~ .route-food-photo-1,
.route-pin-2[open] ~ .route-food-photo-2,
.route-pin-3[open] ~ .route-food-photo-3,
.route-pin-4[open] ~ .route-food-photo-4,
.route-pin-5[open] ~ .route-food-photo-5,
.route-pin-6[open] ~ .route-food-photo-6 {
  opacity: 1;
  pointer-events: auto;
}

/* Legal / contact pages */
.legal-content { max-width: 720px; margin: 0 auto; display: flex; flex-direction: column; gap: var(--space-5); }
.legal-block h3 { margin-bottom: var(--space-2); }
.legal-block p { color: var(--ink-muted); line-height: 1.65; margin-bottom: var(--space-1); }
.legal-block p:last-child { margin-bottom: 0; }
.legal-block a { color: var(--accent); text-decoration: underline; text-underline-offset: 2px; }
.legal-block + .legal-block { padding-top: var(--space-5); border-top: 1px solid var(--border); }
.legal-block ul { margin: var(--space-1) 0 var(--space-1) 20px; padding: 0; }
.legal-block li { color: var(--ink-muted); line-height: 1.65; margin-bottom: 6px; }
.legal-block li:last-child { margin-bottom: 0; }
.page-hero-tight { padding-bottom: var(--space-2); }
.legal-section { padding-top: var(--space-3); }


/* ---- How TSTE works: one-screen-at-a-time carousel (replaces the flip cards) ---- */
/* One screen at a time: native horizontal scroll-snap (swipeable), JS only
   adds auto-advance + dots. The current screen slides out to the left. */
.wit-track { display: flex; overflow-x: auto; scroll-snap-type: x mandatory; scrollbar-width: none; overscroll-behavior-x: contain; }
.wit-track::-webkit-scrollbar { display: none; }
.wit-slide { flex: 0 0 100%; scroll-snap-align: start; padding: 24px 56px 48px; box-sizing: border-box; }
@media (max-width: 859px) { .wit-slide { padding: 16px 0 32px; } }
.wit-placeholder { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; text-align: center; padding: 16px; background: #1a1f36; color: #fff; font-size: 14px; }
.wit-dots { display: flex; justify-content: center; gap: 8px; }
.wit-dots button { width: 10px; height: 10px; padding: 0; border: none; border-radius: 999px; background: var(--border); cursor: pointer; transition: width 200ms ease, background 200ms ease; }
.wit-dots button.is-active { width: 24px; background: var(--accent); }
.wit-slide .media-row-text h3 { font-size: clamp(1.4rem, 3.4vw, 2rem); }


/* ---- Hero with intro video (DE home). Scoped to .hero--video so other pages using .hero are unaffected. ----
   Still photo stays as background; after ~1.5s a 9:16 video panel pops out of it. Desktop: full-height panel on
   the right, photo dims. <1024px: video opens as a fullscreen overlay (nothing cropped), closable with the X. */
.hero--video .hero-media { position: absolute; inset: 0; z-index: 0; overflow: hidden; }
.hero--video .hero-media img.hero-poster {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  opacity: 1; filter: brightness(1); transition: opacity 700ms ease, filter 700ms ease;
}
.hero--video .hero-media.is-punching img.hero-poster { transform: scale(1.04); transition: transform 220ms cubic-bezier(.3,0,.2,1); }
.hero--video .hero-media.is-playing img.hero-poster { opacity: 0.4; filter: brightness(0.6); }
.hero--video .hero-video-panel {
  position: absolute; z-index: 2; top: 0; bottom: 0;
  /* mirrors .nav centering (inset var(--space-3), max-width 1180px) so the right edges line up */
  right: max(var(--space-3), calc((100vw - 1180px) / 2));
  height: 100%; aspect-ratio: 9 / 16; overflow: hidden; background: #000;
  opacity: 0; transform: scale(0.1); transform-origin: 50% 55%;
  transition: opacity 460ms ease, transform 460ms cubic-bezier(.2,.85,.3,1.25);
}
.hero--video .hero-video-panel.is-revealed { opacity: 1; transform: scale(1); }
.hero--video .hero-video-panel video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* Browsers block unmuted autoplay: video starts muted, this button is the gesture that unlocks sound. */
.hero-video-sound {
  position: absolute; z-index: 3; left: 50%; bottom: 20px; transform: translateX(-50%);
  display: flex; align-items: center; gap: 6px;
  background: rgba(10,13,22,0.72); color: #fff; border: none; border-radius: 999px;
  padding: 8px 16px; font-family: var(--font-body); font-size: 13px; font-weight: 600;
  cursor: pointer; opacity: 0; pointer-events: none; transition: opacity 300ms ease;
}
.hero-video-panel.is-revealed .hero-video-sound { opacity: 1; pointer-events: auto; }
.hero-video-sound[data-unmuted] { background: rgba(199, 62, 29, 0.85); }
.hero-video-close { display: none; }
@media (max-width: 1023px) {
  .hero--video { min-height: 100svh; }
  .hero--video > .hero-scrim {
    background:
      linear-gradient(180deg, rgba(10,12,22,0.72) 0%, rgba(10,12,22,0.5) 45%, rgba(10,12,22,0.18) 72%, rgba(10,12,22,0) 90%),
      linear-gradient(0deg, rgba(10,12,22,0.55) 0%, rgba(10,12,22,0) 22%);
  }
  .hero--video .hero-media.is-playing img.hero-poster { opacity: 1; filter: none; }
  .hero--video .hero-content h1, .hero--video .hero-content .lede, .hero--video .hero-content .eyebrow { text-shadow: 0 1px 14px rgba(10,12,22,0.75), 0 0 2px rgba(10,12,22,0.5); }
  .hero--video .hero-video-panel {
    position: fixed; z-index: 1000; inset: 0; right: auto; width: 100%; height: 100dvh;
    aspect-ratio: auto; border-radius: 0; background: #000;
    transform: scale(0.6); transform-origin: 50% 35%; visibility: hidden; pointer-events: none;
    transition: opacity 400ms ease, transform 500ms cubic-bezier(.2,.85,.3,1), visibility 0s linear 500ms;
  }
  .hero--video .hero-video-panel.is-revealed { transform: scale(1); visibility: visible; pointer-events: auto; transition-delay: 0s; }
  .hero--video .hero-video-panel video { object-fit: contain; }
  .hero-video-sound { left: 16px; bottom: max(20px, env(safe-area-inset-bottom)); transform: none; }
  .hero-video-close {
    display: flex; align-items: center; justify-content: center;
    position: absolute; z-index: 3; top: max(16px, env(safe-area-inset-top)); right: 16px;
    width: 44px; height: 44px; border: none; border-radius: 50%;
    background: rgba(10,13,22,0.72); color: #fff; font-size: 22px; line-height: 1; cursor: pointer;
  }
}
@media (prefers-reduced-motion: reduce) {
  .hero--video .hero-media img.hero-poster, .hero--video .hero-video-panel { transition: none; }
  .hero--video .hero-video-panel { display: none; }
}

/* "So funktioniert TSTE" on the brand navy (same treatment as .story-section) */
#so-funktionierts, #how-it-works { background: var(--ink); color: var(--bg); }
#so-funktionierts .eyebrow, #how-it-works .eyebrow { color: var(--reward); }
#so-funktionierts h2, #so-funktionierts h3, #how-it-works h2, #how-it-works h3 { color: #fff; }
#so-funktionierts .wit-dots button, #how-it-works .wit-dots button { background: rgba(255, 255, 255, 0.28); }
#so-funktionierts .wit-dots button.is-active, #how-it-works .wit-dots button.is-active { background: var(--reward); }
@media (min-width: 860px) {
  #so-funktionierts .section-head, #how-it-works .section-head { max-width: none; }
  #so-funktionierts .section-head h2, #how-it-works .section-head h2 { max-width: 44ch; text-wrap: balance; }
}

/* Old hero photo as a faint backdrop behind the navy (image sits under the content, navy shows through) */
#so-funktionierts, #how-it-works { position: relative; overflow: hidden; }
#so-funktionierts::before, #how-it-works::before {
  content: ""; position: absolute; inset: 0; z-index: 0;
  background: url("../images/brand/hero-truffle.webp") center / cover no-repeat;
  opacity: 0.22;
}
#so-funktionierts > .container, #how-it-works > .container { position: relative; z-index: 1; }


/* "Zum Essen verabreden": light photo backdrop behind the cream surface */
.date-section { position: relative; overflow: hidden; background: var(--bg); }
.date-section::before {
  content: ""; position: absolute; inset: 0; z-index: 0;
  background: url("../images/brand/mosaic-friends-table.webp") center / cover no-repeat;
  opacity: 0.14;
}
.date-section > .container { position: relative; z-index: 1; }
.date-section .media-row-list { margin: 0; }
/* Default dot color (--border, #E4DED2) barely shows up against this
   section's cream background -- darken the inactive dots so the "more
   slides to swipe to" affordance is actually visible. */
.date-section .wit-dots button:not(.is-active) { background: rgba(26, 31, 54, 0.22); }
.date-section .media-row-list li { font-size: 1.15rem; }
#verabreden .section-head h2, #meet-up .section-head h2 { text-wrap: balance; }

/* Hero: outline button next to the primary CTA, readable on the dark scrim */
.hero-content .btn-secondary { color: #fff; border-color: rgba(255, 255, 255, 0.75); }
.hero-content .btn-secondary:hover { background: rgba(255, 255, 255, 0.16); color: #fff; }

/* Meet-up slider: pull the slide content up under the heading */
.date-section .section-head { margin-bottom: var(--space-1); }
@media (min-width: 860px) {
  .date-section .wit-slide { padding-top: 8px; }
  .date-section .media-row-text { align-self: start; padding-top: var(--space-3); }
}
