/* =========================================================================
   BAD & BOUJEE

   The idea: this brand has never shot a landscape frame in its life.
   Every asset it owns is a 9:16 reel. So the site is built in portrait —
   the content is shown at the size it was made for, on a counter, in a
   room. Stretch a 640px reel across a 1440px hero and it looks like a
   mockup; stand it up at phone scale and it looks like the shop.

   Palette sampled from their own assets:
     #F9DB49  the yellow in their logo lettering
     #E14C5F  the wall of the shop (see --wall for the text-safe variant)
     #F4A6C8  the donut that replaces the O in BOUJEE
     #0B0A08  their logo ground
   The checkerboard is the floor they actually stand on, used as a floor.
   Single-theme on purpose: the brand is a black room. Everything painted.
   ========================================================================= */

:root {
  scroll-padding-top: 74px;
  /* Nicht Schwarz. Ty: "das komplette Schwarz im Hintergrund tut meinen
     Augen weh" — und der bessere Grund dafuer ist, dass Schwarz der Grund
     ist, den JEDE dunkle Seite hat. Ihre Marke ist pink, also liegt ihr
     Pink im Grund: so wenig, dass man es nicht als Farbe sieht, genug,
     dass die Seite nicht wie eine Vorlage aussieht. akibon.studio macht es
     genauso, nur mit seinem Violett — das System wird uebernommen, die
     Farbe nicht.
     Gemessen, was es kostet: --ash faellt von 12,8 auf 12,5:1. Nichts. */
  --void:      #140B11;
  --void-2:    #1E1219;
  --void-3:    #281A22;
  --yellow:    #F9DB49;
  --coral:     #E14C5F;   /* sampled from the shop wall — accents, dots, borders */
  --wall:      #B02F41;   /* the same hue, deep enough to carry text:
                             white 6.3:1, yellow 4.6:1. The sampled value
                             came off a warm-lit photo and failed both. */
  --pink:      #F4A6C8;
  --cream:     #FBF3E2;
  /* Gemessen im Browser gegen den tatsaechlich zusammengesetzten Grund,
     nicht geschaetzt. Die alten Werte bestanden die Norm gerade so und lasen
     sich trotzdem wie Hintergrund: 4,5:1 ist der Boden, nicht das Ziel.
     Alles hier liegt jetzt darueber, und --ash-dim ist keine Textfarbe mehr
     knapp unter der Grenze, sondern selbst lesbar. */
  /* Zweiter Anlauf. Beim ersten habe ich gegen die Pruefnorm gemessen und
     bei 8,7:1 aufgehoert — bestanden, und trotzdem las es sich wie Grau auf
     Schwarz. Der Fehler war die Marke, nicht die Messung: Beschreibungen
     und Anweisungen sind INHALT, kein Beiwerk, und Inhalt gehoert nicht in
     ein gedaempftes Grau. Jetzt liegt Fliesstext dicht unter Cream, und
     gedaempft ist nur noch, was wirklich nebensaechlich ist. */
  --ash:       #D7CFC3;   /* 12,6:1 — Fliesstext, Beschreibungen, Anweisungen */
  --ash-quiet: #C0B8AC;   /* 10,1:1 — Kleingedrucktes, Meta, Etiketten */
  --ash-dim:   #A29A8E;   /* 7,1:1  — Punkte, Trenner, das Leiseste */
  --line:      #362834;   /* dieselbe Tuenchung wie der Grund */

  /* Dritter Anlauf, und diesmal an ihrem Logo gemessen statt an einer
     Eigenschaft davon.

     Ihr Logo ist HANDGEZEICHNET: dicke, breite, runde Versalien in Gelb,
     Marker-Optik, ein ganz normales &. Ich hatte daraus "wackelig und rund"
     gelesen und Fraunces genommen, weil es WONK- und SOFT-Achsen hat, die
     genau das koennen. Nur ist Fraunces eine BUCHSERIFE. Die Textur stimmte,
     die Familie war falsch - "Vanilla Bomb" las sich wie eine Weinkarte, und
     klein wurde es zappelig. Ty, dreimal: "die scheiss schriftart ... finde
     eine geilere die cleaner ist und passender".

     Gabarito trifft die Familie: breite, runde, satte Versalien, dieselbe
     freundliche Schwere wie ihre gemalten Buchstaben, ein &, das jeder kennt
     - und bei 19px in einer Liste sauber statt verspielt. Nebeneinander
     gestellt mit Bricolage, Archivo, Rubik, Unbounded und Figtree auf ihrem
     eigenen Inhalt; die Probe liegt in .probe/fonts.html.

     Schibsted Grotesk darunter fuer Fliesstext, DM Mono nur fuer kleine
     technische Anzeigen. Keine vierte Familie. */
  --display: "Gabarito", "Helvetica Neue", Arial, sans-serif;
  --boujee:  "Gabarito", "Helvetica Neue", Arial, sans-serif;
  --body:    "Schibsted Grotesk", "Helvetica Neue", Arial, sans-serif;
  --mono:    "DM Mono", ui-monospace, "SF Mono", Menlo, monospace;

  --pad: clamp(20px, 5vw, 64px);
  --maxw: 1240px;
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--void);
  color: var(--cream);
  font-family: var(--body);
  font-size: 17px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img { max-width: 100%; display: block; }
a { color: inherit; }
:focus-visible { outline: 2px solid var(--yellow); outline-offset: 3px; border-radius: 2px; }

.shell { max-width: var(--maxw); margin: 0 auto; padding-inline: var(--pad); }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

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

/* =========================================================================
   THE FLOOR — their tile, used as a floor rather than as a divider
   ========================================================================= */
.floor {
  height: 26px;
  background-color: var(--void);
  background-image: conic-gradient(
    var(--cream) 90deg, transparent 90deg 180deg,
    var(--cream) 180deg 270deg, transparent 270deg);
  background-size: 26px 26px;
}
.floor--tall { height: 44px; background-size: 44px 44px; }
/* Finer, for the strip directly under the reels, so the two of them do not
   read as the same rule twice. */
.checker--thin { height: 15px; background-size: 15px 15px; }

/* =========================================================================
   THE HEADER.

   Three tries got this wrong. First a black status strip, then a red slab
   under it, then both merged into one red block — every version was system
   furniture standing in front of the brand, and none of it did anything for
   somebody who had just arrived. The page also had no navigation at all.

   So it carries what belongs at the top of a shop's page: whether they are
   open, where the four sections are, the phone, and the one button that
   starts an order. The break notice is a single slim line underneath, and
   only while the break is still ahead.
   ========================================================================= */
/* The bar follows the page down now, so it has to earn every pixel of its
   height: one row, 52px, solid ground (no backdrop blur — ten of those cost
   this page 3fps on a phone once). */
.top {
  position: sticky; top: 0; z-index: 60;
  background: var(--void-2);
  border-bottom: 1px solid var(--line);
}
.top__in {
  max-width: var(--maxw); margin: 0 auto;
  padding-block: 9px; padding-inline: var(--pad);
  display: flex; align-items: center; gap: clamp(12px, 2vw, 26px);
  /* Was DM Mono, all of it. A whole header in monospace reads as a terminal
     — retro, technical, and nothing at all like a pink donut shop. The mono
     is for small labels and prices; the header speaks in the body face. */
  font-family: var(--body); font-size: 14px;
}

/* The wordmark used to hide until the big one in the hero had scrolled past,
   so the bar had two appearances. Ty: "warum muss es 2 versionen geben? Lass
   doch das Logo da stehen." It stands. The whole is-past machinery existed
   for this one rule and has gone with it — no scroll listener, no measuring
   of where the hero's wordmark ends. */
.top__brand {
  display: flex; align-items: center; gap: 9px; margin-right: 4px;
  text-decoration: none; color: var(--cream);
  font-family: var(--display); font-weight: 800;
  font-size: 15px; letter-spacing: -.02em; white-space: nowrap;
}
.top__brand img { width: 26px; height: 26px; border-radius: 50%; flex: 0 0 auto; }
/* The same mark in the bar, at the bar's size. */
.top__brand em {
  display: inline-block; vertical-align: baseline;
  height: .607em; width: .437em;
  margin-inline: .09em; position: relative; top: .02em;
  background: var(--pink);
  -webkit-mask: url(assets/brand/amp.svg) center / contain no-repeat;
          mask: url(assets/brand/amp.svg) center / contain no-repeat;
  overflow: hidden; text-indent: -10em;
}

/* The long half of the status ("Open again tomorrow from 11:30am") is
   nowrap, so when the bar ran out of room it did not shrink — it simply
   painted over the navigation next to it. Clipped now, and the detail
   drops out while the navigation is still there, because the navigation
   is the thing you cannot guess. */
.top__now {
  margin: 0; display: flex; align-items: center; gap: 9px;
  min-width: 0; overflow: hidden;
}
.top__state { color: var(--cream); white-space: nowrap; }
.top__detail { color: var(--ash); white-space: nowrap; }
.top__detail:not(:empty)::before { content: "\00b7"; margin-right: 9px; color: var(--ash-dim); }

/* Navigation is back. It was pulled out because the bar did not stick, so
   it only ever appeared alongside the hero's own three buttons pointing at
   the same three places. A bar that follows you is the one place where it
   is not a duplicate. */
.top__nav { display: flex; gap: clamp(14px, 1.8vw, 24px); margin-left: auto; }
.top__nav a {
  color: var(--ash); text-decoration: none; white-space: nowrap;
  transition: color .18s ease;
}
.top__nav a:hover { color: var(--cream); }

.top__acts { display: flex; align-items: center; gap: clamp(10px, 1.4vw, 18px); }
.top__tel {
  color: var(--cream); text-decoration: none; white-space: nowrap;
  font-variant-numeric: tabular-nums;      /* the digits line up without mono */
}
.top__tel:hover { text-decoration: underline; }
.top__telshort { display: none; }

/* ---------- the cart, in the bar ----------
   Empty it is an outline that says what it is; with something in it, it
   turns yellow and carries the count and the running total. */
.cartlink {
  display: inline-flex; align-items: center; gap: 9px;
  padding: 7px 14px 7px 12px; border-radius: 999px;
  border: 1px solid var(--line); background: transparent;
  color: var(--ash); text-decoration: none; white-space: nowrap;
  font-weight: 600;
  transition: border-color .2s ease, background-color .2s ease, color .2s ease;
}
.cartlink:hover { border-color: var(--yellow); color: var(--cream); }

/* Their box, in eleven pixels: a lid line over a body. */
.cartlink__box {
  width: 15px; height: 13px; border-radius: 2px;
  border: 1.5px solid currentColor;
  position: relative; flex: 0 0 auto;
}
.cartlink__box::before {
  content: ""; position: absolute; left: -1.5px; right: -1.5px; top: 2px;
  height: 1.5px; background: currentColor;
}

.cartlink.has {
  background: var(--yellow); border-color: var(--yellow); color: var(--void);
}
.cartlink.has:hover { background: #FFE879; border-color: #FFE879; color: var(--void); }
.cartlink__n {
  min-width: 20px; height: 20px; padding: 0 5px; border-radius: 999px;
  background: var(--void); color: var(--yellow);
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 12px; font-weight: 800; font-variant-numeric: tabular-nums;
}
.cartlink__n[hidden], .cartlink__sum[hidden] { display: none; }
.cartlink__sum { font-variant-numeric: tabular-nums; font-weight: 700; }

/* The count changing is the only feedback that adding worked, so it has to
   be visible from the corner of your eye. */
.cartlink.bump { animation: cartBump .42s cubic-bezier(.2,1.5,.4,1); }
@keyframes cartBump {
  0%   { transform: none; }
  40%  { transform: scale(1.11); }
  100% { transform: none; }
}

@media (max-width: 1180px) { .top__detail { display: none; } }
@media (max-width: 1000px) { .top__nav { display: none; } .top__now { margin-right: auto; } }
@media (min-width: 1001px) and (max-width: 1180px) { .top__now { margin-right: auto; } }
@media (max-width: 560px) {
  .top__telfull { display: none; }
  .top__telshort { display: inline; }
  .cartlink__label { display: none; }
  .cartlink__sum { display: none !important; }
  .top__in { gap: 10px; }
}

@media (prefers-reduced-motion: reduce) {
  .cartlink { transition: none; }
  .cartlink.bump { animation: none; }
}

/* The break: one sentence, a coral dot in front of it. The uppercase mono
   chip that used to sit here was the most retro thing on the page. It sits
   under the sticky bar and scrolls away, because the bar has to stay slim. */
.top__note {
  margin: 0;
  background: rgba(225,76,95,.10);
  border-bottom: 1px solid rgba(225,76,95,.22);
  color: var(--cream);
  font-family: var(--body); font-size: 14px;
  padding-block: 10px; padding-inline: var(--pad);
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
}
.top__note[hidden] { display: none; }
.top__tag {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--coral); flex: 0 0 auto;
  align-self: center;
  font-size: 0; overflow: hidden;          /* the words go, the marker stays */
}
.top__sub { color: var(--ash); }
.top__sub:not(:empty)::before { content: "\2014"; margin-right: 10px; color: var(--ash-dim); }

.dot { width: 8px; height: 8px; border-radius: 50%; background: var(--ash-dim); flex: 0 0 auto; }
.dot--open  { background: #5BD68A; box-shadow: 0 0 0 3px rgba(91,214,138,.18); }
.dot--soon  { background: var(--yellow); box-shadow: 0 0 0 3px rgba(249,219,73,.18); }
.dot--shut  { background: var(--ash-dim); box-shadow: 0 0 0 3px rgba(255,255,255,.07); }
.dot--break { background: var(--coral); box-shadow: 0 0 0 3px rgba(225,76,95,.20); }


/* =========================================================================
   THE ROOM — hero. Type on the left, their reels standing on the counter.
   Deliberately not a full-bleed photo with a headline over it.
   ========================================================================= */
.room { background: var(--void); padding-top: clamp(48px, 8vw, 92px); overflow: hidden; }
.room__grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(40px, 6vw, 72px);
  align-items: end;
}
@media (min-width: 940px) { .room__grid { grid-template-columns: 1fr 1.12fr; align-items: center; } }
/* Grid items default to min-width:auto, so a flex row of fixed-ish tiles
   grows its own track and pushes past the viewport. Pin it to zero. */
.room__grid > * { min-width: 0; }

.room__eyebrow {
  font-family: var(--mono); font-size: 11.5px; letter-spacing: .2em;
  text-transform: uppercase; color: var(--yellow); margin: 0 0 20px;
}
.wordmark {
  font-family: var(--display); font-weight: 800;
  font-size: clamp(3rem, 11vw, 7.6rem);
  line-height: .82; letter-spacing: -.045em;
  color: var(--yellow); margin: 0 0 24px; text-transform: uppercase;
}
/* <em> is italic by browser default, and Gabarito has no italic — the browser
   then slants it by hand, which is precisely the cheap look this whole change
   was about. Every <em> that wears the display face says normal explicitly;
   this catches any that is added later and forgets to. */
.top__brand em, .room__lede em, .sec-title em, .strip__one em, .dbox__lid em,
.wordmark em { font-style: normal; }

/* The ampersand used to need a whole recipe here: Fraunces' italic swaps in
   a swash Et-ligature, and Ty was blunt about it — "kein Mensch kennt die
   Version des & Zeichens was du gewaehlt hast. Das muss schon mit dem Logo
   harmonieren." Gabarito has one ampersand and it is the ordinary one, so
   all that is left is the size and the pink. */
/* THEIR ampersand, not Gabarito's.

   Ty: "dieses & zeichen ist anders als in deren offiziellem branding/LOGO."
   It is. So this one is traced out of assets/brand/logo.png - the shape is
   theirs, drawn with a marker, with the loop and the tail - and painted as a
   mask so the colour still comes from the stylesheet and nothing is a
   picture that would blur when the heading grows.

   In their logo the ampersand stands 57px against a 64px capital, so 0.89 of
   the cap height. The cap height of Gabarito was measured in the browser,
   not assumed, and the number below follows from the two. */
.wordmark .amp {
  display: inline-block; vertical-align: baseline;
  /* Gabarito's capital measures .681em here, and their ampersand stands at
     .891 of a capital, so: .681 x .891 = .607em tall, and the viewBox's
     1000:1390 gives the width. */
  height: .607em; width: .437em;
  margin-inline: .08em; position: relative; top: .02em;
  background: var(--pink);
  -webkit-mask: url(assets/brand/amp.svg) center / contain no-repeat;
          mask: url(assets/brand/amp.svg) center / contain no-repeat;
  /* The character stays in the markup for anything reading the page aloud;
     it is pushed out of the box and clipped rather than set to font-size 0,
     which would have collapsed the em-based height above to nothing. */
  overflow: hidden; text-indent: -10em;
}
.room__lede {
  font-size: clamp(1.05rem, 2.2vw, 1.35rem);
  max-width: 26ch; margin: 0 0 30px; line-height: 1.35;
}
/* No italic: Gabarito has none, and a browser-slanted fake is exactly the
   cheap look being fixed. The pink does the emphasising. */
.room__lede em { font-family: var(--boujee); font-style: normal; font-weight: 600; color: var(--pink); }

/* ---------- the counter -------------------------------------------------
   A fanned hand of cards rather than three polite thumbnails in a row:
   they overlap, the middle one is dominant, all three are playing, and
   each drifts on its own slow cycle so the block is never quite still.
   Tiles share the column rather than claiming a fixed width — a fixed
   width pushed the third card off the edge, where the body's overflow
   clip silently ate it. */
.counter {
  position: relative;
  display: flex;
  justify-content: center;
  align-items: center;
  padding-block: 30px;
  perspective: 1400px;
}
.counter .tile {
  flex: 1 1 0;
  min-width: 0;
  max-width: 268px;
  will-change: transform;
}
/* negative margins do the overlap; the middle card sits on top */
.counter .tile:nth-child(1) { margin-right: -5%; transform-origin: bottom right; }
.counter .tile:nth-child(3) { margin-left:  -5%; transform-origin: bottom left; }
.counter .tile:nth-child(2) { z-index: 3; }
.counter .tile:nth-child(1),
.counter .tile:nth-child(3) { z-index: 1; filter: brightness(.82) saturate(.9); }
/* The tiles in the hero stack used to jump forward when the cursor passed
   over them. Ty: "there is no reason for the ones in the back to come to
   the front by hovering" — right, there is not. They are a photograph of a
   counter, not a control. */

.counter .tile:nth-child(1) { animation: driftL 11s ease-in-out infinite; }
.counter .tile:nth-child(2) { animation: driftC  9s ease-in-out infinite; }
.counter .tile:nth-child(3) { animation: driftR 13s ease-in-out infinite; }

@keyframes driftL {
  0%,100% { transform: rotate(-7deg) translateY(20px) scale(.9); }
  50%     { transform: rotate(-8.5deg) translateY(10px) scale(.91); }
}
@keyframes driftC {
  0%,100% { transform: rotate(1.5deg) translateY(-10px) scale(1.1); }
  50%     { transform: rotate(0deg) translateY(-22px) scale(1.11); }
}
@keyframes driftR {
  0%,100% { transform: rotate(8deg) translateY(26px) scale(.9); }
  50%     { transform: rotate(6.5deg) translateY(14px) scale(.91); }
}
@media (prefers-reduced-motion: reduce) {
  .counter .tile { animation: none; }
  .counter .tile:nth-child(1) { transform: rotate(-7deg) translateY(20px) scale(.9); }
  .counter .tile:nth-child(2) { transform: rotate(1.5deg) translateY(-10px) scale(1.1); }
  .counter .tile:nth-child(3) { transform: rotate(8deg) translateY(26px) scale(.9); }
}
@media (max-width: 620px) {
  .counter .tile:nth-child(3) { display: none; }
  /* two cards, sized so the drift animation's 1.11 scale still never
     reaches the bezel — at 190px the peak of the cycle clipped */
  .counter .tile { max-width: 158px; }
  .counter .tile:nth-child(1) { margin-right: -8%; }
}

/* A 9:16 frame. The reels are 720x1280, so at this width they are sharp
   rather than upscaled the way the 360px grid thumbnails were. */
.tile {
  position: relative;
  aspect-ratio: 9 / 16;
  border-radius: 12px;
  overflow: hidden;
  background: var(--void-3);
  border: 1px solid var(--line);
  box-shadow: 0 24px 50px -22px rgba(0,0,0,.95);
  transition: transform .3s cubic-bezier(.2,.7,.3,1), border-color .3s ease;
}
.tile img, .tile video { width: 100%; height: 100%; object-fit: cover; }
.tile:hover { border-color: var(--yellow); }

/* =========================================================================
   THE REEL WALL — the brother's work, given the room it earns
   ========================================================================= */
.wall { background: var(--void-2); padding-block: clamp(56px, 8vw, 96px); }
.wall__head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 24px; flex-wrap: wrap; margin-bottom: 28px;
}
.wall__credit {
  font-family: var(--mono); font-size: 11.5px; color: var(--ash);
  border: 1px solid var(--line); border-radius: 999px; padding: 7px 14px;
  text-decoration: none; white-space: nowrap;
}
.wall__credit:hover { border-color: var(--yellow); color: var(--yellow); }

.reels {
  display: flex; gap: 16px;
  overflow-x: auto; overflow-y: hidden;
  padding-block: 6px 20px;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
  scrollbar-color: var(--line) transparent;
}
.reels::-webkit-scrollbar { height: 6px; }
.reels::-webkit-scrollbar-thumb { background: var(--line); border-radius: 99px; }
.reel { flex: 0 0 auto; width: clamp(168px, 44vw, 232px); scroll-snap-align: start; }
/* An invisible marker riding at the end of the rail. When it comes into
   view the next batch of clips is pulled in — which is what carries the
   wall on a phone, where there are no arrows. */
.reels__end { flex: 0 0 1px; width: 1px; height: 1px; }
/* Same for the wall: a clip is something you watch, not something that
   reacts to being approached. */
/* .reel__cap stand hier: eine Zeile unter jedem Clip, die sagte, was
   darin zu sehen ist. Sie stimmte nicht verlaesslich mit dem Clip ueberein,
   unter dem sie stand, und eine falsche Bildunterschrift ist schlechter als
   keine. */
/* The pink wall lived here: an earlier version of today's board, on the
   red of their shop wall. It has not been in the markup for a long time,
   and its bare `.board` rule was quietly turning the new board into a
   grid. Gone, along with .pinkwall, .slat and the stamp keyframes. */


/* =========================================================================
   SECTION FURNITURE
   ========================================================================= */
.band { padding-block: clamp(56px, 8vw, 104px); }
/* Two classes were being used in the markup that had never been written:
   .band--raise did nothing, so every section sat on the identical ground
   and only the checkerboard strip ever separated them — which is a large
   part of why the whole lower half read as one repeated section. The
   grounds alternate now, and most of the strips have gone. */
.band--raise { background: var(--void-2); }
/* The eyebrow used to sit ABOVE the heading in small mono caps with a
   hairline ruled across the column — the arrangement every generated
   landing page ships with. It sits UNDER the heading now, in the studio's
   own label spec (11px / 800 / .24em), finishing the sentence rather than
   announcing it. No rule. */
.eyebrow {
  font-family: var(--body); font-size: 11px; font-weight: 800;
  letter-spacing: .24em; text-transform: uppercase;
  color: var(--yellow); margin: 0 0 22px; display: block;
}
.eyebrow::after { content: none; }

/* Five sections in a row carried a huge all-caps headline with an italic
   serif tail hanging off it. One of those is a signature; five is a
   template, and it was the loudest thing on the page. Sentence case now,
   at the size the ranking block already used and looked right at —
   which is also, measured, what akibon.studio sets its own sections in. */
h2.sec-title {
  font-family: var(--display); font-weight: 800;
  font-size: clamp(1.7rem, 3.2vw, 2.5rem);
  line-height: 1.06; letter-spacing: -.028em;
  margin: 0 0 12px; text-wrap: balance; text-transform: none;
  max-width: 24ch;
}
/* The emphasis stays — it just stops changing typeface to do it. Same
   face, same weight, their pink. The italic serif lives in the hero only,
   where one of it reads as a signature. */
h2.sec-title em {
  font-family: inherit; font-style: normal; font-weight: 800;
  text-transform: none; color: var(--pink); letter-spacing: inherit;
}
/* The one lead-in sentence a section gets, and all three of them are set
   from here. They had drifted to three different sizes in three places —
   1.03rem, .97rem and 1.2rem — which is why one section kept reading as the
   weak one next to the others. */
.sec-lede, .direct__note, .deliver__note {
  color: var(--ash);
  font-size: clamp(1.06rem, 1.3vw, 1.28rem); line-height: 1.5;
}
.sec-lede b, .direct__note b, .deliver__note b { color: var(--cream); font-weight: 700; }
.sec-lede { max-width: 46ch; margin: 18px 0 38px; }

/* ---------- buttons ---------- */
.row { display: flex; gap: 11px; flex-wrap: wrap; }
.btn {
  font-family: var(--body); font-weight: 600; font-size: .98rem;
  text-decoration: none; padding: 13px 23px; border-radius: 999px;
  border: 1.5px solid transparent; display: inline-flex; align-items: center; gap: 8px;
  /* Zentriert, nicht linksbuendig. Solange sich ein Knopf nach seinem Inhalt
     richtet, faellt das nicht auf - erst wo er gestreckt wird (im Raster von
     .when__go ueber die ganze Breite), klebte die Schrift links. Deshalb hier
     an der Wurzel und nicht an der einen Stelle: gestreckt wird an mehreren. */
  justify-content: center; text-align: center;
  cursor: pointer;
  transition: transform .18s cubic-bezier(.2,.8,.3,1), background-color .18s ease, color .18s ease, border-color .18s ease;
}
.btn:hover { transform: translateY(-2px); }
.btn:active { transform: translateY(0); }
.btn--y { background: var(--yellow); color: var(--void); }
.btn--y:hover { background: #FFE879; }
.btn--c { background: var(--wall); color: #fff; }
.btn--c:hover { background: var(--coral); }
.btn--w { background: #fff; color: var(--wall); }
.btn--w:hover { background: var(--cream); }
.btn--ghost { border-color: var(--line); color: var(--cream); }
.btn--ghost:hover { border-color: var(--yellow); color: var(--yellow); }

/* =========================================================================
   MENU — a narrow editorial column beside one tall frame
   ========================================================================= */
@media (min-width: 980px) { .menu-wrap { grid-template-columns: minmax(0,1.35fr) minmax(0,.65fr); } }


/* Nothing is pinned at the top any more, so this no longer has to
   duck under a bar that is not there. */
.box--pop { border-color: var(--yellow); background: var(--void-3); }

/* =========================================================================
   CATERING
   ========================================================================= */
.cater-grid { display: grid; grid-template-columns: 1fr; gap: clamp(34px, 5vw, 60px); }
@media (min-width: 940px) { .cater-grid { grid-template-columns: .85fr 1.15fr; } }
/* The four kinds of order are what gives somebody the idea — but only if
   the eye lands on them. As four lines of text they get skipped, so each
   one carries its own tower of boxes at the size that order really is. */
.cater-list { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 12px; margin: 0 0 clamp(30px, 4vw, 48px); }

.cater-pick {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0;
  text-align: left;
  width: 100%;
  background: var(--void-2);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 20px 20px 18px;
  cursor: pointer;
  font-family: var(--body);
  color: var(--cream);
  transition: border-color .2s ease, background-color .2s ease, transform .2s cubic-bezier(.2,.9,.3,1);
}
.cater-pick:hover { border-color: var(--yellow); background: var(--void-3); transform: translateY(-3px); }
.cater-pick.on { border-color: var(--yellow); background: var(--void-3); }

/* -------------------------------------------------------------------------
   THE FOUR SCENES. Full width of the card, not a thumbnail beside the text:
   at 76px the picture was texture and the eye skipped it. Each has its own
   ground, and the wedding one is ivory so that in a row of dark cards there
   is one your eye stops on.
   ------------------------------------------------------------------------- */
.scene {
  display: block;
  width: 100%;
  border-radius: 9px;
  overflow: hidden;
  background: var(--void-3);
  margin-bottom: 14px;
}
.scene svg { display: block; width: 100%; height: auto; }

/* Coloured light rather than flat panels — the way a room actually looks
   when somebody has put LEDs in it. Each card keeps its own two or three
   colours so they still read apart, and every bloom stays under the lid's
   contrast so the writing on it never gets lost. */
.scene--office {
  background:
    radial-gradient(58% 80% at 16% 12%, rgba(86,150,255,.30), transparent 62%),
    radial-gradient(52% 74% at 88% 22%, rgba(120,225,255,.20), transparent 64%),
    linear-gradient(#2C3137, #1B1F24);
}
.scene--birthday {
  background:
    radial-gradient(58% 80% at 14% 14%, rgba(255,54,158,.44), transparent 62%),
    radial-gradient(56% 76% at 88% 20%, rgba(104,110,255,.40), transparent 64%),
    radial-gradient(78% 92% at 50% 112%, rgba(249,219,73,.26), transparent 66%),
    linear-gradient(#2E1524, #190C14);
}
/* Uplighting is what a venue actually does for a wedding — warm from below,
   a wash of violet across the back. */
.scene--wedding {
  background:
    radial-gradient(70% 86% at 50% 112%, rgba(255,196,96,.30), transparent 64%),
    radial-gradient(56% 74% at 84% 16%, rgba(168,120,255,.24), transparent 64%),
    linear-gradient(#26302A, #171D19);
}
.scene--wholesale {
  background:
    radial-gradient(56% 78% at 82% 16%, rgba(255,170,60,.22), transparent 62%),
    radial-gradient(52% 70% at 14% 84%, rgba(120,255,190,.14), transparent 64%),
    linear-gradient(#37332C, #211E1A);
}

/* The whole card lifts, and the picture leans in a little under it. */
.cater-pick .scene svg { transition: transform .45s cubic-bezier(.2,.9,.3,1); }
.cater-pick:hover .scene svg { transform: scale(1.035); }

@media (prefers-reduced-motion: reduce) {
  .cater-pick .scene svg { transition: none; }
}

.cater-pick__body { flex: 1 1 auto; min-width: 0; }
.cater-pick h3 {
  font-family: var(--display); font-weight: 700;
  font-size: 1.2rem; margin: 0 0 3px; letter-spacing: -.015em;
}
.cater-pick .min {
  display: block;
  font-family: var(--mono); font-size: 10.5px; letter-spacing: .08em;
  text-transform: uppercase; color: var(--yellow); margin-bottom: 8px;
}
.cater-pick p { margin: 0; color: var(--ash); font-size: .92rem; }
.cater-pick__go {
  display: block;
  font-family: var(--mono); font-size: 10px; letter-spacing: .1em;
  text-transform: uppercase; color: var(--ash-dim); margin-top: 12px;
  transition: color .2s ease;
}
.cater-pick:hover .cater-pick__go, .cater-pick.on .cater-pick__go { color: var(--yellow); }
.cater-pick__go span { display: inline-block; transition: transform .2s ease; }
.cater-pick:hover .cater-pick__go span { transform: translateX(4px); }
@media (prefers-reduced-motion: reduce) {
  .cater-pick, .cater-pick__go span, .mini__box { transition: none; }
}

form { display: flex; flex-direction: column; gap: 15px; }
.field { display: flex; flex-direction: column; gap: 8px; }
.pair { display: grid; grid-template-columns: 1fr 1fr; gap: 15px; }
@media (max-width: 520px) { .pair { grid-template-columns: 1fr; } }
/* 10,5px mit Laufweite ist keine Beschriftung, das ist ein Muster. */
label {
  font-family: var(--body); font-weight: 700;
  font-size: 12px; letter-spacing: .1em; text-transform: uppercase;
  color: var(--ash-quiet); display: block;
}
input, textarea, select {
  font-family: var(--body); font-size: 1rem; color: var(--cream);
  background: var(--void-2); border: 1px solid var(--line);
  border-radius: 6px; padding: 12px 14px; width: 100%;
  transition: border-color .16s ease;
}
input:hover, textarea:hover, select:hover { border-color: var(--ash-dim); }
input:focus, textarea:focus, select:focus { border-color: var(--yellow); outline: none; }
textarea { resize: vertical; min-height: 104px; }
select {
  appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--ash) 50%), linear-gradient(135deg, var(--ash) 50%, transparent 50%);
  background-position: calc(100% - 20px) 21px, calc(100% - 14px) 21px;
  background-size: 6px 6px; background-repeat: no-repeat;
}
/* Das Schlimmste auf der Seite: 13,3px auf 3,23:1. Es steht unter jedem
   Knopf und unter jeder Summe, also ist es das Kleingedruckte, das am
   haeufigsten gelesen wird. Groesser, heller, mehr Zeilenabstand. */
.form-note {
  font-size: .91rem; line-height: 1.6;
  color: var(--ash-quiet); margin: 0;
}
.form-ok { border: 1px solid var(--yellow); background: rgba(249,219,73,.08); border-radius: 6px; padding: 16px 18px; font-size: .95rem; }

/* =========================================================================
   VISIT — the streetcar photo is their one landscape frame, so it gets to
   be the only wide image on the site
   ========================================================================= */
.visit { display: grid; grid-template-columns: 1fr; gap: clamp(32px, 5vw, 56px); align-items: start; }
@media (min-width: 940px) { .visit { grid-template-columns: 1fr 1fr; } }
.score { display: flex; align-items: baseline; gap: 14px; margin-bottom: 26px; }
.score__num { font-family: var(--display); font-weight: 800; font-size: 3.2rem; line-height: .8; color: var(--yellow); letter-spacing: -.05em; font-variant-numeric: tabular-nums; }
.score__meta { font-family: var(--mono); font-size: 11.5px; letter-spacing: .1em; text-transform: uppercase; color: var(--ash); }
.score__stars { color: var(--yellow); letter-spacing: .16em; display: block; margin-bottom: 4px; font-size: .95rem; }
.hours { list-style: none; margin: 0 0 24px; padding: 0; }
.hours li {
  display: flex; justify-content: space-between; gap: 16px;
  padding-block: 10px; border-bottom: 1px solid var(--line);
  font-family: var(--mono); font-size: .9rem; font-variant-numeric: tabular-nums;
}
.hours li.today { color: var(--yellow); }
/* This list used the class name .day, and so did the pick-up chips in the
   cart — so every weekday here picked up a border, a radius and padding
   and looked like a button you could press. Ty: "warum sind das buttons?
   die Tage". They are not; they are a list of days. Renamed, because two
   different things must never share a class name. */
.hours li.today .hours__day::before { content: "\25B8 "; }
.hours .shut { color: var(--ash-dim); }
.wide { border-radius: 8px; overflow: hidden; border: 1px solid var(--line); }
.wide img { width: 100%; aspect-ratio: 16/9; object-fit: cover; }
.wide__cap { font-family: var(--mono); font-size: 11px; color: var(--ash-dim); margin: 9px 2px 0; letter-spacing: .05em; }

/* =========================================================================
   FOOTER — stands on the floor
   ========================================================================= */
footer { background: var(--void-2); padding-block: 56px 34px; }

/* Was a flex row with space-between: three blocks of different widths, so
   the middle gap yawned open and nothing lined up with anything. A grid
   gives every column the same top edge and a fixed share of the width. */
/* Four columns and twenty links became one row. Everything that went was
   a second copy of something already on the screen: the hours stand
   complete in the Visit section above, and the open/closed line plus the
   navigation are in the bar that follows you down the page. */
.foot {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: clamp(20px, 4vw, 52px);
  padding-bottom: clamp(26px, 3vw, 38px);
}
.foot__logo { display: block; flex: 0 0 auto; }
.foot__logo img { width: clamp(96px, 11vw, 124px); height: auto; display: block; }

.foot__where {
  font-style: normal; display: grid; gap: 5px;
  margin-right: auto;
}
.foot__where a {
  color: var(--ash); text-decoration: none; font-size: .95rem;
  transition: color .18s ease;
}
.foot__where a:hover { color: var(--cream); }

.foot__links { display: flex; flex-wrap: wrap; gap: clamp(14px, 2vw, 26px); align-items: center; }
.foot__links a,
.foot__link {
  background: none; border: 0; padding: 0; cursor: pointer;
  font-family: var(--body); font-size: .95rem;
  color: var(--ash); text-decoration: none;
  transition: color .18s ease;
}
.foot__links a:hover,
.foot__link:hover { color: var(--yellow); }

.colophon {
  margin-top: 38px; padding-top: 20px; border-top: 1px solid var(--line);
  font-family: var(--mono); font-size: 12.5px; color: var(--ash-quiet);
  display: flex; flex-wrap: wrap; gap: 12px; justify-content: space-between;
}

/* =========================================================================
   ?check — Tyrone's pre-flight list. The client never sees it.
   ========================================================================= */
.check {
  position: fixed; right: 16px; bottom: 16px; z-index: 200;
  width: min(360px, calc(100vw - 32px)); max-height: 70svh; overflow-y: auto;
  background: var(--void-3); border: 1px solid var(--coral); border-radius: 8px;
  padding: 18px 20px; font-family: var(--mono); font-size: 12px; line-height: 1.55;
  box-shadow: 0 20px 60px -20px rgba(0,0,0,.9);
}
.check h5 { margin: 0 0 10px; color: var(--coral); font-size: 11px; letter-spacing: .14em; text-transform: uppercase; }
.check ul { margin: 0; padding-left: 16px; color: var(--ash); }
.check li { margin-bottom: 7px; }
.check b { color: var(--cream); font-weight: 400; }
.check .ok { color: #5BD68A; }
.check button {
  margin-top: 12px; background: none; border: 1px solid var(--line); color: var(--ash);
  font-family: var(--mono); font-size: 11px; padding: 5px 11px; border-radius: 4px; cursor: pointer;
}
.check button:hover { border-color: var(--coral); color: var(--coral); }

/* ---------- reveal ---------- */
.rise { opacity: 0; transform: translateY(16px); transition: opacity .6s ease, transform .6s cubic-bezier(.2,.8,.3,1); }
.rise.seen { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) { .rise { opacity: 1; transform: none; } }

/* =========================================================================
   THE CATERING QUOTE
   Boxes stack as you drag, with a real price on them. A form that asks an
   office manager to guess the number is a form they close.
   ========================================================================= */
.quote { margin-bottom: 34px; }
.quote__label {
  font-family: var(--mono); font-size: 10.5px; letter-spacing: .13em;
  text-transform: uppercase; color: var(--ash); display: block; margin-bottom: 16px;
}

.quote__range {
  appearance: none; -webkit-appearance: none;
  width: 100%; height: 4px; padding: 0; border: 0;
  background: var(--line); border-radius: 99px; cursor: pointer;
}
.quote__range::-webkit-slider-thumb {
  appearance: none; -webkit-appearance: none;
  width: 22px; height: 22px; border-radius: 50%;
  background: var(--yellow); border: 3px solid var(--void-2);
  box-shadow: 0 2px 8px rgba(0,0,0,.6); cursor: grab;
}
.quote__range::-moz-range-thumb {
  width: 22px; height: 22px; border-radius: 50%;
  background: var(--yellow); border: 3px solid var(--void-2);
  box-shadow: 0 2px 8px rgba(0,0,0,.6); cursor: grab;
}
.quote__range:focus-visible { outline: 2px solid var(--yellow); outline-offset: 6px; }

/* A stack you can count, in the same pink card as the box in the builder.
   Towers of five, growing upward, each slab showing its own lid seam and
   the shadow of the one above it. */
/* Stack left, answer right — one row rather than a narrow tower with the
   whole right half of the column empty beside it. */
.quote__body {
  display: grid;
  grid-template-columns: 1fr;
  gap: 22px;
  align-items: end;
  margin: 26px 0 20px;
}
@media (min-width: 620px) {
  .quote__body { grid-template-columns: auto 1fr; gap: 34px; }
}

.quote__stack {
  display: flex;
  align-items: flex-end;
  gap: 14px;
  min-height: 132px;
  padding-left: 2px;
}
.qcol { display: flex; flex-direction: column-reverse; gap: 2px; }

.qbox {
  position: relative;
  width: 62px;
  height: 21px;
  border-radius: 3px;
  background: linear-gradient(#F7B8D2 0%, #F0A3C4 46%, #DE8CB0 100%);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.7),
    0 2px 4px rgba(0,0,0,.5);
  animation: stackUp .3s cubic-bezier(.2,1.5,.4,1) both;
}
/* the seam where the lid meets the base */
.qbox::after {
  content: "";
  position: absolute; left: 4px; right: 4px; top: 7px;
  height: 1px;
  background: rgba(140,45,80,.32);
}
@keyframes stackUp { from { transform: translateY(-14px) scaleY(.5); opacity: 0; } to { transform: none; opacity: 1; } }

.quote__out {
  display: flex; flex-direction: column; align-items: flex-start;
  gap: 8px; min-width: 0;
}
.quote__n {
  display: block; font-family: var(--display); font-weight: 800;
  font-size: clamp(1.8rem, 4vw, 2.6rem); line-height: 1;
  letter-spacing: -.03em; font-variant-numeric: tabular-nums;
}
.quote__sub { display: block; color: var(--ash); font-size: .88rem; margin-top: 2px; }
.quote__price {
  margin-top: 4px;
  font-family: var(--mono); font-weight: 700; font-size: clamp(1.8rem, 4vw, 2.4rem);
  color: var(--yellow); font-variant-numeric: tabular-nums; white-space: nowrap;
}
.quote__note { color: var(--ash-dim); font-size: .86rem; margin: 14px 0 0; }

@media (prefers-reduced-motion: reduce) { .qbox { animation: none; } }

/* The leader-dot row needs name / dots / price on one line, but with a
   104px donut beside it there is not always a line's worth of room — a
   long name plus the SIGNATURE flag pushed the price 41px off a phone.
   The name may now shrink, and the row may wrap before it overflows. */
@media (max-width: 480px) {
}

/* =========================================================================
   MENU — bigger donuts, and hovering a row actually does something
   ========================================================================= */
@media (max-width: 560px) { .dn--row { width: 78px; height: 78px; } .menu li { gap: 14px; } }

/* ---------- the preview the rows drive ---------- */
@keyframes peekIn { from { opacity: 0; transform: scale(1.04); } to { opacity: 1; transform: none; } }
@media (max-width: 979px) { .peek { display: none; } }
@media (prefers-reduced-motion: reduce) { .peek__frame.swap { animation: none; } .dn--row, .said { transition: none; } }



/* =========================================================================
   THE PRAISE STRIP
   Seven cards in a grid is the most generic shape social proof has. This is
   one line of what people said, running past like the floor does — no
   section of its own, and the coral still breaks up the black.
   ========================================================================= */
.strip {
  background: var(--wall);
  color: #fff;
  overflow: hidden;
  padding-block: 16px;
  border-block: 1px solid rgba(0,0,0,.25);
}
.strip__run {
  display: flex;
  width: max-content;
  animation: runPast 68s linear infinite;
}
.strip:hover .strip__run { animation-play-state: paused; }

.strip__one { display: inline-flex; align-items: baseline; gap: 12px; padding-inline: 26px; white-space: nowrap; }
.strip__mark { color: var(--yellow); font-size: .8rem; }
.strip__one em {
  font-family: var(--boujee);
  font-style: normal;
  font-weight: 600;
  font-size: 1.3rem;
  line-height: 1;
}
.strip__one b {
  font-family: var(--mono);
  font-weight: 400;
  font-size: 11px;
  letter-spacing: .06em;
  color: rgba(255,255,255,.62);
}

@keyframes runPast { from { transform: translateX(0); } to { transform: translateX(-50%); } }

/* No motion: let it be a normal wrapped line instead of a frozen one. */
@media (prefers-reduced-motion: reduce) {
  .strip__run { animation: none; width: auto; flex-wrap: wrap; gap: 4px 0; }
  .strip__one:nth-child(n+8) { display: none; }
}

/* The score is a standing fact about the shop, like the phone number. */

/* Reports the slider rather than asking again — styled like the inputs it
   sits beside so the row still reads as one row. */
.readback {
  display: block;
  font-family: var(--body);
  font-size: 1rem;
  color: var(--cream);
  background: var(--void-3);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 12px 14px;
  font-variant-numeric: tabular-nums;
}

/* ---------- the like count on a reel ---------- */
.reel__likes {
  display: inline-block;
  color: var(--yellow);
  font-family: var(--mono);
  font-size: 11px;
  margin-right: 9px;
  font-variant-numeric: tabular-nums;
}

/* =========================================================================
   COMMENTS ON THE CLIP
   A box of testimonials sitting on its own was never attached to anything.
   These are the comments left under this exact post, so they surface over
   this exact clip — one after another, unhurried, and only when asked.
   ========================================================================= */
.said {
  position: absolute;
  inset: auto 0 0 0;
  z-index: 3;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 13px;
  padding: 48px 14px 16px;
  background: linear-gradient(to top, rgba(20,11,17,.97) 40%, rgba(20,11,17,.82) 70%, transparent);
  /* Wieder exakt wie im Backup. Ich hatte hier opacity: 1 hart gesetzt und
     damit .tile.talking ausgehebelt - und mit ihm das gestaffelte Auftauchen
     der Zeilen UND das Ausblenden der Zaehler. Die Kommentare waren zwar da,
     aber ohne alles, was sie gut gemacht hat.

     Der Unterschied zum Backup ist nur noch die Farbe des Verlaufs: die
     folgt dem Grund der Seite, der nicht mehr schwarz ist. Sichtbar sind
     sie trotzdem, weil .talking jetzt vom Beobachter kommt und nicht mehr
     vom Mauszeiger - das war Tys urspruengliche Beschwerde. */
  opacity: 0;
  pointer-events: none;
  transition: opacity .45s ease;
}
.tile.talking .said { opacity: 1; }

/* The handle used to run into the comment and both lost. It sits above
   now, small and quiet, so the sentence itself is the thing you read. */
.said__row {
  display: block;
  color: #fff;
  font-size: .96rem;
  line-height: 1.32;
  text-shadow: 0 1px 4px rgba(0,0,0,.95);
  opacity: 0;
  transform: translateY(8px);
}
.said__row b {
  display: block;
  font-family: var(--mono);
  font-weight: 400;
  font-size: 9.5px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--yellow);
  margin-bottom: 2px;
  opacity: .85;
}

/* Each one waits its turn — 260ms apart, so they arrive rather than appear. */
.tile.talking .said__row {
  animation: sayUp .55s cubic-bezier(.2,.9,.3,1) forwards;
  animation-delay: calc(var(--n) * .26s + .12s);
}
@keyframes sayUp { to { opacity: 1; transform: none; } }

@media (prefers-reduced-motion: reduce) {
  .said, .said__row { transition: none; }
  .tile.talking .said__row { animation: none; opacity: 1; transform: none; }
}

/* =========================================================================
   BUILD A BOX

   The first version was a pink rectangle with dark circles in it, which is
   a diagram of a box rather than a box. This one has a lid standing up
   behind it, cardboard walls with a seam, dimples pressed into the base,
   and it is tilted just enough that you are looking *into* it. The donut
   you pick flies across and drops in.
   ========================================================================= */
.builder {
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(34px, 5vw, 56px);
  align-items: start;
}
@media (min-width: 940px) { .builder { grid-template-columns: 1.25fr .75fr; } }

.builder__hint { color: var(--ash); font-size: .92rem; margin: 16px 0 20px; max-width: 38ch; }
.field--size { max-width: 22rem; }

/* ---------- the box ---------- */
/* Perspective applies to the lid alone now — nothing you click sits in it. */
.builder__box { perspective: 1300px; perspective-origin: 50% 100%; }
.builder__box .dbox { width: 100%; }

/* The box used to sit in a rotateX(11deg) + preserve-3d layer. It looked
   right and it broke the one thing that matters: Chromium hit-tests
   unreliably inside a 3D-transformed subtree, so donuts could not be taken
   back out — and elementFromPoint uses the same faulty projection, which is
   why every test I ran said it was fine.

   The tilt is decoration. Taking a donut out is the function. The lid still
   folds back in 3D, but the base and everything you click is flat. */
.dbox { transition: transform .5s cubic-bezier(.2,.9,.3,1); }
.is-full .dbox { transform: translateY(-3px); }

/* the lid, hinged at the back and folded open */
.dbox__lid {
  position: relative;
  height: clamp(88px, 12vw, 132px);
  border-radius: 12px 12px 0 0;
  background: linear-gradient(178deg, #E07FA8 0%, #EE9FC1 42%, #F5B4D0 88%, #DA85AC 100%);
  transform-origin: bottom center;
  transform: rotateX(62deg);
  display: grid;
  place-content: center;
  box-shadow: inset 0 3px 0 rgba(255,255,255,.6), 0 -12px 26px -14px rgba(0,0,0,.8);
}
/* the crease where the card is folded back */
.dbox__lid::after {
  content: "";
  position: absolute; left: 6%; right: 6%; bottom: 0;
  height: 2px;
  background: linear-gradient(90deg, transparent, rgba(140,45,80,.45) 12%, rgba(140,45,80,.45) 88%, transparent);
}
.dbox__lid span {
  font-family: var(--display);
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: -.02em;
  font-size: clamp(1.3rem, 3vw, 2.1rem);
  color: #8E2E52;
  opacity: .9;
  text-shadow: 0 1px 0 rgba(255,255,255,.35);

  /* The lid is folded back by 62 degrees, so everything printed on it is
     squashed to cos(62) = 47% of its height — which is why the wordmark
     read as compressed. Pre-stretch by exactly the inverse, 1/cos(62) =
     2.13, and it lands at its true proportions once the fold flattens it.
     Tied to the angle above: change one, change the other. */
  transform: scaleY(2.13);
  transform-origin: center;
}
.dbox__lid em {
  font-family: var(--boujee); font-style: normal;
  font-weight: 500; text-transform: none;
}

/* The base. It was solid pink — a 700-pixel-wide flat pink rectangle with
   six paler circles on it, which is what an unfinished mockup looks like,
   and it was the largest thing on its screen.

   Bakery boxes are kraft card. So the base is kraft now, with the grain of
   the board running through it, and their pink stays where it belongs: on
   the lid, under the wordmark. */
.dbox__base {
  position: relative;
  border-radius: 4px 4px 12px 12px;
  padding: clamp(16px, 2.4vw, 24px);
  background:
    /* the grain of the board */
    repeating-linear-gradient(94deg,
      rgba(255,255,255,.05) 0 2px, rgba(0,0,0,.035) 2px 5px),
    linear-gradient(158deg, #DCB68C 0%, #C99D6E 48%, #B4854F 100%);
  box-shadow:
    inset 0 14px 22px -12px rgba(72,44,18,.6),     /* light falling in */
    inset 0 -3px 0 rgba(255,255,255,.28),
    0 30px 55px -26px rgba(0,0,0,.95);
}
/* the cut edge of the card along the front */
.dbox__base::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 9px;
  border-radius: 0 0 12px 12px;
  background: linear-gradient(#A97B47, #8E6536);
}

.dbox__slots { display: grid; gap: clamp(10px, 1.6vw, 18px); grid-auto-rows: auto; }
.dbox__slots--1  { grid-template-columns: 1fr; max-width: 44%; margin: 0 auto; }
.dbox__slots--6  { grid-template-columns: repeat(3, 1fr); }
.dbox__slots--12 { grid-template-columns: repeat(4, 1fr); }

/* an empty slot is a dimple pressed into the card, not a hole */
/* A <button> and a <span> do not size the same way in a grid cell, which
   left filled slots a few pixels short of empty ones. Both are blocks at
   full column width, and the square comes from aspect-ratio alone. */
.slot {
  display: block;
  width: 100%;
  aspect-ratio: 1 / 1;
  box-sizing: border-box;
  border-radius: 50%;
  border: 0;
  padding: 0;
  background: radial-gradient(circle at 50% 34%, #8E6535 0%, #7E5A2F 60%, #A2794A 100%);
  box-shadow:
    inset 0 5px 9px rgba(62,38,14,.5),
    inset 0 -2px 0 rgba(255,255,255,.26);
}

.slot--full {
  background: none;
  box-shadow: 0 7px 14px rgba(48,28,10,.6);
  cursor: pointer;
  position: relative;
  transition: transform .18s cubic-bezier(.2,1.3,.4,1);
}

.slot--full:hover { transform: translateY(-3px) scale(1.05); }
.slot--full::after {
  content: "×";
  position: absolute; inset: 0;
  display: grid; place-content: center;
  font-family: var(--mono); font-size: 1.4rem; color: #fff;
  background: rgba(20,11,17,.55); border-radius: 50%;
  opacity: 0; transition: opacity .16s ease;
}
.slot--full:hover::after { opacity: 1; }

/* it lands, then settles — a squash and a small bounce */
.slot--full.land { animation: land .42s cubic-bezier(.22,1.4,.36,1); }
@keyframes land {
  0%   { transform: translateY(-30px) scale(1.12, .82); opacity: .9; }
  55%  { transform: translateY(2px)   scale(.94, 1.06); }
  100% { transform: none; }
}

.dbox__tally {
  display: flex; justify-content: space-between; align-items: baseline;
  font-family: var(--mono); font-size: 1rem; color: var(--ash);
  margin: 26px 2px 0; font-variant-numeric: tabular-nums;
}
#rTotal { color: var(--yellow); font-size: 1.35rem; }
.is-full #rCount { color: var(--yellow); }

/* ---------- the rack you pick from ---------- */
.rack {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(88px, 1fr));
  gap: 14px 10px;
}

.pick {
  background: none; border: 0; padding: 0; cursor: pointer;
  font-family: var(--body); color: var(--cream);
  display: flex; flex-direction: column; align-items: center; gap: 7px;
  transition: transform .2s cubic-bezier(.2,1,.3,1), opacity .2s ease;
}
.pick:disabled { opacity: .3; cursor: not-allowed; }
.pick:not(:disabled):hover { transform: translateY(-5px); }
.pick:not(:disabled):active { transform: translateY(-1px) scale(.96); }

.pick .dn {
  width: 100%;
  aspect-ratio: 1;
  box-shadow: 0 8px 18px -6px rgba(0,0,0,.8);
  transition: box-shadow .2s ease;
}
.pick:not(:disabled):hover .dn { box-shadow: 0 14px 26px -8px rgba(0,0,0,.9); }

/* Same pairing as a card on the board above: one face, one size, cream for
   the name and yellow for the money. The price used to drop into the
   monospace at .74rem here, which is the size at which that face stops
   being readable and starts being texture. */
.pick__name {
  font-family: var(--display); font-weight: 700;
  font-size: .82rem; line-height: 1.24; letter-spacing: -.012em;
  text-align: center; color: var(--cream);
}
.pick__price {
  font-family: var(--display); font-weight: 700; font-size: .82rem;
  color: var(--yellow); font-variant-numeric: tabular-nums;
}

/* ---------- the donut in flight ---------- */
/* A clone that travels from the rack into the slot. Fixed to the viewport
   so neither container can clip it on the way. */
.flyer {
  position: fixed;
  z-index: 120;
  border-radius: 50%;
  pointer-events: none;
  overflow: hidden;
  box-shadow: 0 18px 34px -10px rgba(0,0,0,.9);
  transition: transform .52s cubic-bezier(.34,.62,.3,1), opacity .12s ease .4s;
}
.flyer img { width: 100%; height: 100%; object-fit: cover; }

@media (prefers-reduced-motion: reduce) {
  .dbox, .slot--full, .pick, .pick .dn { transition: none; }
  .slot--full.land { animation: none; }
  .flyer { transition: none; }
}

/* ---------- the donut itself ----------
   These base rules went out with the old builder block and took the round
   crop with them, which is why a donut in a slot rendered as a square. */
.dn {
  display: block;
  border-radius: 50%;
  overflow: hidden;
  flex: 0 0 auto;
  background: var(--void-3);
}
.dn img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* No photograph yet. This was a drawn glaze — a soft coloured ball — and
   six of them at once read as six images that had failed to load. Their
   own mark stands in the gap instead: obviously a drawing, obviously
   theirs, and it holds the column so the list never goes ragged. */
.dn--mark {
  position: relative;
  background-color: var(--void-3);
  box-shadow: inset 0 0 0 1px var(--line);
}
/* Held well back. Six of these appear together in the box rack, and at full
   strength six identical pink donuts shout down the nine real photographs
   beside them. A watermark reads as "their picture goes here"; a bright
   drawing reads as a picture that is already there. */
.dn--mark::before {
  content: "";
  position: absolute; inset: 13%;
  background: url("assets/brand/icon.png") 50% 52% / contain no-repeat;
  opacity: .42;
}

.dn--pick { width: 100%; aspect-ratio: 1; }
.dn--md   { width: 100%; height: 100%; }
/* .dn is defined after the modifiers, so its border-radius:50% was winning
   over .dn--peek's 10px — which is why the aside showed a circle sitting
   inside a square card. Two classes beat one; no reordering needed. */

/* The three box cards used to sit next to the menu looking clickable and
   doing nothing, which reads as broken. Choosing one sets the builder to
   that size and takes you to it. */
@media (prefers-reduced-motion: reduce) { .box, .box__go span { transition: none; } }

/* A full box says so and says what to do, instead of every control on
   screen quietly going dead. */
.rack.is-locked .pick { opacity: .42; }
.rack.is-locked .pick:hover { transform: none; }
.hint--full { color: var(--yellow) !important; }

/* Tapping a flavour into a box with no room shakes the box rather than
   swallowing the tap — the answer is in there, not in the rack. */
.dbox.nope { animation: nope .42s cubic-bezier(.3,.1,.3,1); }
@keyframes nope {
  0%,100% { transform: translateX(0); }
  20%     { transform: translateX(-7px); }
  45%     { transform: translateX(6px); }
  70%     { transform: translateX(-3px); }
}

/* While it is full, the way out is visible without having to hover to find it. */
.is-full .slot--full::after { opacity: .34; }
.is-full .slot--full:hover::after { opacity: 1; }

@media (prefers-reduced-motion: reduce) { .dbox.nope { animation: none; } }

/* =========================================================================
   FINDING IT
   "Will I recognise it?" is half the decision for a small shop on a long
   street. A map answers where; the window, the stools and the streetcar
   answer what it looks like when you get there.
   ========================================================================= */
/* One row of three, all the same height. The old block was a stack of big
   pictures and ate the screen; this reads as a strip of the street, and the
   captions sit ON the image so they cost no height at all. */
.place {
  display: grid;
  grid-template-columns: 1fr;
  gap: 10px;
  margin-bottom: clamp(24px, 3.4vw, 38px);
}
@media (min-width: 700px) {
  .place { grid-template-columns: 1.55fr 1fr 1fr; }
}

.place__shot { position: relative; margin: 0; border-radius: 10px; overflow: hidden; }
.place__shot img {
  width: 100%;
  height: clamp(170px, 20vw, 250px);
  object-fit: cover;
  display: block;
  transition: transform .5s cubic-bezier(.2,.9,.3,1), filter .4s ease;
}
/* On one column the lead shot is the one worth the extra height. */
@media (max-width: 699px) {
  .place__shot--lead img { height: clamp(200px, 52vw, 280px); }
}
.place__shot:hover img { transform: scale(1.03); filter: brightness(1.05); }

.place__shot figcaption {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: 22px 12px 9px;
  font-family: var(--mono); font-size: 11px; letter-spacing: .04em;
  color: #fff;
  background: linear-gradient(to top, rgba(8,7,6,.86), rgba(8,7,6,0));
  pointer-events: none;
}

.map {
  border-radius: 10px;
  overflow: hidden;
  border: 1px solid var(--line);
  background: var(--void-2);
  aspect-ratio: 16 / 11;
}
.map iframe { width: 100%; height: 100%; border: 0; display: block; filter: saturate(.85); }

@media (prefers-reduced-motion: reduce) { .place__shot img { transition: none; } }

/* Re-stated after the background above, which came later in the file and
   was flattening the highlighted card back to the others. */
.box--pop { background: var(--void-3); border-color: var(--yellow); }

/* -------------------------------------------------------------------------
   BACK TO THE TOP. Hidden by default and hidden from the tab order with it.
   It never moves sideways when it hides: a fixed element parked off the
   right edge is exactly what gave this page a phantom horizontal scrollbar
   once already, so the hide is opacity and a few pixels of lift, no more.

   The entrance and the hover used to fight over the same transform, which
   is what made it snap. Now the button itself only ever animates in and
   out, and the hover moves the arrow inside it.
   ------------------------------------------------------------------------- */
.totop {
  position: fixed;
  right: clamp(14px, 3vw, 26px);
  bottom: clamp(14px, 3vw, 26px);
  z-index: 70;
  width: 46px; height: 46px;
  display: grid; place-items: center;
  border: 1px solid rgba(20,11,17,.25);
  border-radius: 50%;
  background: var(--yellow);
  box-shadow: 0 6px 18px rgba(0,0,0,.45);
  cursor: pointer;
  opacity: 0; visibility: hidden;
  transform: translateY(14px) scale(.9);
  transition:
    opacity .30s ease,
    transform .42s cubic-bezier(.16,1,.3,1),
    box-shadow .25s ease,
    visibility 0s .30s;
}
.totop.is-on {
  opacity: 1; visibility: visible; transform: none;
  transition:
    opacity .30s ease,
    transform .42s cubic-bezier(.16,1,.3,1),
    box-shadow .25s ease,
    visibility 0s;
}
.totop.is-on:hover { box-shadow: 0 10px 26px rgba(249,219,73,.30); }
.totop.is-on:active { transform: scale(.94); transition-duration: .12s; }
.totop:focus-visible { outline: 2px solid var(--cream); outline-offset: 3px; }

/* the arrow, drawn rather than typed, so it never becomes a font's idea of one */
.totop__arrow {
  width: 11px; height: 11px;
  border-left: 2.5px solid var(--void);
  border-top: 2.5px solid var(--void);
  transform: rotate(45deg);
  margin-top: 3px;
  transition: transform .28s cubic-bezier(.2,1.35,.4,1);
}
.totop.is-on:hover .totop__arrow { transform: translateY(-3px) rotate(45deg); }

@media (prefers-reduced-motion: reduce) {
  .totop, .totop__arrow { transition: opacity .01s, visibility 0s; }
  .totop { transform: none; }
  .totop.is-on:hover .totop__arrow { transform: rotate(45deg); }
}

/* "Ask" is a phone call, not a shrug. A price nobody has confirmed still
   gets you a way to find it out. */

/* Likes and comments where they live on the post: top corners, over the
   clip. A pill each so they hold on a bright frame as well as a dark one,
   and pointer-events off so they never eat a tap meant for the video. */
.reel__stats {
  position: absolute; inset: 10px 10px auto 10px; z-index: 2;
  display: flex; justify-content: space-between; align-items: flex-start;
  pointer-events: none;
}
.reel__stat {
  font-family: var(--mono); font-size: 11px; line-height: 1;
  color: #fff; background: rgba(20,11,17,.58);
  backdrop-filter: blur(5px);
  border: 1px solid rgba(251,243,226,.20);
  border-radius: 99px; padding: 5px 9px;
  font-variant-numeric: tabular-nums;
}
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
/* while the comments run they would sit on top of each other */
.tile.talking .reel__stats { opacity: 0; transition: opacity .3s ease; }


/* The preview notice inside a submitted form: the first half has to read as
   a status, not as a confirmation somebody can skim past. */
.form-ok strong { color: var(--yellow); font-weight: 600; }

/* The privacy sheet. Reads like a page, not like a legal notice nobody opens. */
.sheet {
  border: 0; padding: 0; max-width: 640px; width: min(92vw, 640px);
  background: var(--void-2); color: var(--cream);
  border-radius: 14px;
  box-shadow: 0 30px 80px rgba(0,0,0,.7);
}
.sheet::backdrop { background: rgba(6,5,4,.72); backdrop-filter: blur(3px); }
.sheet__in { padding: clamp(24px, 4vw, 38px); position: relative; }
.sheet__x {
  position: absolute; top: 14px; right: 16px;
  background: none; border: 0; color: var(--ash);
  font-size: 26px; line-height: 1; cursor: pointer; padding: 4px 8px;
}
.sheet__x:hover { color: var(--cream); }
.sheet h2 {
  font-family: var(--display); font-weight: 800; font-size: clamp(1.4rem, 3vw, 1.8rem);
  margin: 0 0 14px; letter-spacing: -.02em; max-width: 22ch;
}
.sheet h3 {
  font-family: var(--mono); font-size: 10.5px; letter-spacing: .14em;
  text-transform: uppercase; color: var(--yellow);
  margin: 26px 0 8px;
}
.sheet p, .sheet li { font-size: .95rem; line-height: 1.6; color: var(--ash); margin: 0 0 10px; }
.sheet__lede { color: var(--cream) !important; }
.sheet ul { margin: 0 0 10px; padding-left: 20px; }
.sheet strong { color: var(--cream); font-weight: 600; }
.sheet a { color: var(--yellow); }
/* what still needs the shop's own answer, marked rather than made up */
.sheet__todo {
  display: block; margin-top: 6px;
  border-left: 2px solid var(--coral); padding-left: 10px;
  color: var(--ash-dim); font-size: .86rem;
}
.sheet__note {
  margin-top: 26px; padding-top: 16px; border-top: 1px solid var(--line);
  font-size: .86rem !important; color: var(--ash-dim) !important;
}

/* =========================================================================
   PHONE

   Everything below is what the desktop layout gets wrong once it is 375px
   wide. It was not a rescale problem — three of these are real bugs that
   only appear on a touch device.
   ========================================================================= */

/* --- the reel wall -------------------------------------------------------
   168px was a thumbnail, not a video. One clip nearly fills the screen now
   with the next one peeking, which is how a phone carousel should read, and
   the snap points make it land cleanly. */
@media (max-width: 700px) {
  .reel { width: min(72vw, 300px); }
  .reels { gap: 12px; scroll-padding-left: var(--pad); }
}

/* On a touch device the tile in view gets .talking permanently — there is no
   hover to end it — so the comment layer sat over the whole clip and you
   could not see a single donut. It becomes a caption strip: the two newest
   comments along the bottom, the clip itself left alone. */
@media (hover: none) {
  .said {
    padding: 22px 12px 12px;
    gap: 7px;
    background: linear-gradient(to top, rgba(20,11,17,.94) 52%, rgba(20,11,17,.55) 78%, transparent);
  }
  .said__row { font-size: .86rem; line-height: 1.28; }
  .said__row:nth-child(n+3) { display: none; }
  .said__row b { font-size: 8.5px; margin-bottom: 1px; }

  /* the like/comment pills were hidden whenever a tile was "talking", which
     on a phone is always — they can stay now that the comments are a strip */
  .tile.talking .reel__stats { opacity: 1; }
}

/* --- the hero row --------------------------------------------------------
   Three pills wrapped 1 + 2 and looked ragged. The main action takes the
   full width, the two supporting ones share the line under it. */
@media (max-width: 560px) {
  .room .row { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
  .room .row .btn {
    width: 100%; justify-content: center; text-align: center;
    /* half of a 375px screen is not much: tighten the pill rather than let
       "Order for a crowd" break across two lines */
    padding-inline: 12px; font-size: .93rem; white-space: nowrap;
  }
  .room .row .btn:first-child { grid-column: 1 / -1; padding-inline: 23px; font-size: .98rem; }
}

/* --- the footer ----------------------------------------------------------
   "Privacy" has to be a <button> because it opens a dialog, and a button
   carries the user agent's own horizontal padding — so it sat a few pixels
   to the right of the two real links above it. */
.foot__link { padding-inline: 0; }

/* --- the map -------------------------------------------------------------
   OpenStreetMap draws its own attribution inside the frame and it wrapped
   onto two lines over a short map. Required credit, so it stays; the map
   gets the height to carry it instead. */
@media (max-width: 700px) {
  .map { aspect-ratio: 4 / 5; }
}

/* --- the map cover -------------------------------------------------------
   Sits over the embed and takes the wheel and the touches, so neither a
   scroll nor a swipe that happens to be over the map does anything to the
   map. One click hands it over.

   This used to be switched off entirely on a pointer device — "on a pointer
   device the map never blocked anything", said the comment, which was simply
   wrong: a wheel over an embedded map zooms it and the page stops moving
   under you. Ty: "Die map soll nicht direkt scrollbar sein, das triggert."
   The cover is on everywhere now. */
.map { position: relative; }
.map__cover {
  position: absolute; inset: 0; z-index: 2;
  display: grid; place-items: center;
  background: transparent;
  border: 0; cursor: pointer; padding: 0;
  transition: background-color .2s ease;
}
.map__cover span {
  font-family: var(--body); font-weight: 600; font-size: .86rem;
  color: var(--void); background: var(--cream);
  padding: 9px 16px; border-radius: 999px;
  box-shadow: 0 4px 14px rgba(0,0,0,.34);
  opacity: 0; transform: translateY(6px);
  transition: opacity .22s ease, transform .22s ease;
}
.map__cover:hover, .map__cover:focus-visible { background: rgba(20,11,17,.22); }
.map__cover:hover span, .map__cover:focus-visible span { opacity: 1; transform: none; }
.map--live .map__cover { display: none; }

/* On a touch screen there is no hover to reveal the label, so it stands. */
@media (hover: none) {
  .map__cover span { opacity: 1; transform: none; }
  .map__cover { align-items: end; padding-bottom: 46px; }  /* clear of OSM's credit */
}

/* The way back out. Top left, because OpenStreetMap puts its own zoom
   buttons top right and its credit along the bottom. */
.map__tools {
  position: absolute; top: 10px; left: 10px; z-index: 3;
  display: flex; gap: 7px;
}
.map__tools[hidden] { display: none; }
.map__tool {
  font-family: var(--body); font-weight: 600; font-size: .78rem;
  color: var(--void); background: var(--cream);
  border: 0; border-radius: 999px; padding: 7px 13px; cursor: pointer;
  box-shadow: 0 3px 10px rgba(0,0,0,.34);
  transition: background-color .18s ease;
}
.map__tool:hover { background: var(--yellow); }
.map__tool:focus-visible { outline: 2px solid var(--void); outline-offset: 2px; }

/* --- the header on a phone ----------------------------------------------
   Status and phone number wrapped onto two lines. They fit on one at this
   size; the detail gives way first if a longer status ever needs the room. */
@media (max-width: 560px) {
  .top__in { flex-wrap: nowrap; gap: 10px; font-size: 12.5px; }
  .top__now { min-width: 0; flex: 0 1 auto; }
  .top__state { white-space: nowrap; }
  .top__detail {
    min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .top__tel { flex: 0 0 auto; }
}

/* --- weight off the phone ------------------------------------------------
   Measured at the reel wall on a phone viewport: 3 frames per second, with
   NO video playing. So it was never the video decode — it was ten live
   backdrop-filter blurs, one behind each stat pill, each one forcing the
   compositor to re-sample what is underneath on every frame while the strip
   moves. A solid translucent ground looks all but identical at that size and
   costs nothing.

   The tile shadows go the same way: a 50px blur radius under five cards that
   are being scrolled is paint work for an effect nobody looks at on a 375px
   screen. */
@media (max-width: 700px) {
  .reel__stat { backdrop-filter: none; background: rgba(20,11,17,.78); }
  .sheet::backdrop { backdrop-filter: none; background: rgba(6,5,4,.82); }
  .reels .tile { box-shadow: 0 8px 18px -10px rgba(0,0,0,.8); }
  .reels .tile { will-change: auto; }
}

/* =========================================================================
   THE OTHER COUNTER

   One row, not three cards. The .way rules that built the old grid are gone
   with the markup that used them — the "Reserve and pick it up" card was a
   link back to the board directly above it, and Too Good To Go had already
   come out.
   ========================================================================= */
/* The left column was ~800px wide with the text capped at 46ch inside it, so
   a third of the row was empty down the middle and the copy looked timid at
   .97rem. Ty: "there is literally no need for u to have the text so small...
   du kannst mehr breite nutzen und es dafuer groesser machen."

   Wider card, smaller gap, and the sentence set at a size worth reading.
   52ch is still inside the comfortable measure for running text — the fill
   comes from the type growing into the column, not from the line getting
   longer than an eye can track. */
.deliver {
  display: grid; gap: 26px; align-items: center;
}
/* The note used to be three lines of argument and is now one line of answer,
   so the weight shifts: the card takes the room the paragraph gave back
   instead of leaving a hole where the paragraph was. */
@media (min-width: 880px) {
  .deliver { grid-template-columns: 1fr minmax(380px, 520px); gap: clamp(32px, 3.4vw, 56px); }
}
/* One line, and it is the line that decides whether to press the button.
   Size and colour come from the shared .sec-lede rule. */
.deliver__note { margin: 20px 0 0; }

/* Uber's own wordmark on Uber's own near-black, so the link reads as their
   store front rather than as a button we drew.

   The first version of this was a flat box with everything set in DM Mono —
   the rating, the stars and the link all in the same small technical type,
   which is what made it look, in Ty's words, hässlich. Mono is for meters
   and clock times, not for a shop's rating. Now: the mark, a rule, the
   score at a size worth reading, and a real filled button at the bottom.
   Green appears once, on the thing you press. */
.deliver__card {
  display: flex; flex-direction: column; align-items: stretch; gap: 20px;
  padding: 28px 28px 24px; border-radius: 18px; text-decoration: none;
  background: linear-gradient(168deg, #1B2E33 0%, #142328 62%);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.07), 0 10px 30px rgba(0,0,0,.3);
  transition: transform .22s ease, box-shadow .22s ease;
}
.deliver__card:hover {
  transform: translateY(-4px);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 18px 44px rgba(0,0,0,.42);
}
/* The page-wide focus ring is yellow, which on Uber's near-black looked like
   a warning label around the card. Theirs is green. */
.deliver__card:focus-visible {
  outline: 2px solid #06C167; outline-offset: 3px; border-radius: 18px;
}
.deliver__logo { display: block; width: 146px; height: 24px; }
.deliver__rule { height: 1px; background: rgba(255,255,255,.1); }

.deliver__rate {
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  font-family: var(--body);
}
.deliver__stars {
  color: #FFFFFF; font-size: 17px; letter-spacing: .1em; line-height: 1;
  position: relative; top: -1px;
}
.deliver__rate b {
  font-family: var(--display); font-weight: 700; font-size: 1.7rem;
  line-height: 1; letter-spacing: -.02em; color: #FFFFFF;
}
.deliver__rate i {
  font-style: normal; font-size: .92rem; color: #9FB3AD; margin-left: auto;
}

/* Uber Eats green, once, on the one thing there is to press. */
.deliver__go {
  display: flex; align-items: center; justify-content: center; gap: 9px;
  padding: 15px 20px; border-radius: 999px;
  background: #06C167; color: #0B1A1E;
  font-family: var(--body); font-weight: 700; font-size: 1.02rem;
  letter-spacing: -.01em;
  transition: background-color .2s ease;
}
.deliver__card:hover .deliver__go { background: #05D873; }
.deliver__arrow { transition: transform .2s ease; }
.deliver__card:hover .deliver__arrow { transform: translateX(3px); }

/* the footer's own live line and hours table */
.foot__id .foot__logo { margin-bottom: 16px; }
.foot__live {
  display: flex; align-items: center; gap: 8px;
  font-family: var(--mono); font-size: 11.5px; color: var(--cream) !important;
  margin: 0 0 14px !important;
}
.foot__id a { color: var(--ash); text-decoration: none; }
.foot__id a:hover { color: var(--cream); text-decoration: underline; }

.foot__hours { list-style: none; margin: 0; padding: 0; display: grid; gap: 5px; }
.foot__hours li {
  display: flex; justify-content: space-between; gap: 14px;
  font-family: var(--mono); font-size: 11.5px; color: var(--ash);
}
.foot__hours li span:last-child { font-variant-numeric: tabular-nums; }
.foot__hours .is-today { color: var(--yellow); }

@media (min-width: 761px) {
  .foot { grid-template-columns: minmax(0,1.15fr) minmax(0,.85fr) minmax(0,.8fr) minmax(0,.8fr); }
}

/* =========================================================================
   AFTER AKIBON.STUDIO

   Their own site is the standard this has to clear, so the system is lifted
   from it rather than guessed at:

     label      11px / 800 / 2.4px tracking / uppercase   — the small gold line
     heading    800 weight, NEGATIVE tracking, sentence case, not shouted
     body       15.5px / 400 / 1.62 line-height, muted
     panels     one step up from the ground, hairline at ~6% white

   What it is NOT: giant all-caps headlines over equal-height card grids with
   decorative quote marks. That is the look this page had, and it is the look
   every generated landing page has.

   The palette stays Bad & Boujee's — their yellow, their coral, their black.
   Borrowing Akibon's violet would just make it somebody else's site.
   ========================================================================= */

.label {
  font-family: var(--body);
  font-size: 12px; font-weight: 800;
  letter-spacing: .16em; text-transform: uppercase;
  color: var(--ash-quiet);
  margin: 0;
}
.label--gold  { color: var(--yellow); }
.label--coral { color: var(--coral); }


/* -------------------------------------------------------------------------
   THE 5.0 — their reviews
   ------------------------------------------------------------------------- */
.said-head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 28px; flex-wrap: wrap; margin-bottom: clamp(28px, 3.6vw, 44px);
}
.said-head > :first-child { flex: 1 1 340px; min-width: 0; }
.said-head .sec-title { max-width: 20ch; }

.score {
  display: grid; justify-items: center; gap: 3px;
  text-decoration: none; flex: 0 0 auto;
  border: 1px solid var(--line); border-radius: 13px;
  padding: 16px 26px; background: var(--void-2);
  transition: border-color .2s ease, transform .2s ease;
}
.score:hover { border-color: var(--yellow); transform: translateY(-2px); }
.score__n {
  font-family: var(--display); font-weight: 800; font-size: 2.5rem;
  line-height: 1; color: var(--cream); letter-spacing: -.04em;
}
.score__stars { color: var(--yellow); font-size: .8rem; letter-spacing: .14em; }
.score__sub {
  font-family: var(--body); font-size: 10.5px; font-weight: 800;
  letter-spacing: .16em; text-transform: uppercase; color: var(--ash-quiet);
}

/* Columns, not a grid: eight real reviews of different lengths were being
   stretched to equal heights, which is exactly what makes a set of genuine
   quotes read as a template. They flow at their own size now. */
/* The newest review, set as the section's lead. Real sentences at a size
   that admits they are the point of the section. */
.lead-say { margin: 0 0 clamp(34px, 4vw, 52px); max-width: 30ch; }
.lead-say blockquote {
  margin: 0 0 16px;
  font-family: var(--display); font-weight: 700;
  font-size: clamp(1.3rem, 2.5vw, 1.85rem);
  line-height: 1.24; letter-spacing: -.024em; color: var(--cream);
  text-wrap: pretty;
}
@media (min-width: 900px) {
  .lead-say { max-width: 46ch; }
}

/* Eight reviews. They were eight boxes on a dark ground in three columns —
   the exact arrangement that makes writing somebody actually wrote look
   like filler somebody generated. Two columns, no boxes, no borders: the
   words sit on the page the way words do. Columns rather than a grid so
   they keep their own heights; equal-height cards are what made real
   quotes look like a template in the first place. */
.says { columns: 1; column-gap: clamp(32px, 4vw, 58px); }
@media (min-width: 900px) { .says { columns: 2; } }

.say {
  break-inside: avoid; -webkit-column-break-inside: avoid;
  margin: 0 0 clamp(30px, 3.4vw, 44px);
}
.say blockquote {
  margin: 0 0 12px;
  font-size: 1.04rem; line-height: 1.66; color: var(--cream);
  letter-spacing: -.004em;
}

.say__by, .lead-say .say__by { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px; }
.say__who {
  font-family: var(--display); font-weight: 700;
  font-size: .95rem; letter-spacing: -.012em; color: var(--cream);
}
.say__meta {
  font-family: var(--mono); font-size: 10.5px; color: var(--ash-quiet);
  letter-spacing: .02em;
}

/* The only ruled thing in the section, and deliberately so: he answers
   every one of them, and that is the argument the section is making. */
.say__reply {
  margin: 14px 0 0; padding: 12px 0 12px 16px;
  border-left: 2px solid var(--coral);
}
.say__reply .label { margin-bottom: 5px; }
.say__reply p:last-child {
  margin: 0; font-size: .92rem; line-height: 1.55; color: var(--ash);
}


/* =========================================================================
   THE BOARD — fifteen flavours, sideways, one section at a time.

   What it replaces: a vertical list of fifteen rows, each carrying a name,
   a price, a description and an orders figure, beside a preview panel and
   three box cards. Every fact at once, and nothing legible.

   Three type roles in this whole section and no more: the section heading,
   the card line, and the small print. The card line is ONE size and ONE
   weight — cream for the name, yellow for the price. Nothing here is set
   in the monospace; that face is for the live readouts at the top of the
   page, not for money on a menu.
   ========================================================================= */
.board { margin-top: clamp(20px, 2.6vw, 30px); }

/* ---------- one section ----------
   The three sections used to be tabs. Tabs hid two thirds of the menu
   behind a control that was easy to miss, so the best-liked flavours were
   never seen. Each section is its own block now, headed the way the drinks
   block is headed, because that is the one that read clearly. */
.grp {
  padding-top: clamp(22px, 2.6vw, 30px);
  border-top: 1px solid var(--line);
  margin-top: clamp(26px, 3vw, 38px);
}
.grp:first-child { margin-top: clamp(14px, 1.8vw, 22px); }

.grp__head {
  display: flex; align-items: center; justify-content: space-between; gap: 18px;
}
.grp__name { font-size: 12px; letter-spacing: .24em; margin: 0; }
.grp__name em {
  font-style: normal; font-size: 11px; font-weight: 800;
  color: var(--ash-quiet); margin-left: 4px; letter-spacing: .14em;
}
.grp__note { margin: 11px 0 0; color: var(--ash); font-size: .95rem; }

/* ---------- arrows ---------- */
.grp__nav { display: flex; align-items: center; gap: 9px; flex: 0 0 auto; }
.grp.is-short .grp__nav,
.grp.is-short .grp__bar { display: none; }
.board__arrow {
  width: 36px; height: 36px; border-radius: 50%;
  border: 1px solid var(--line); background: transparent;
  cursor: pointer; position: relative;
  transition: border-color .2s ease, background-color .2s ease, opacity .2s ease;
}
.board__arrow::before {
  content: ""; position: absolute; top: 50%; left: 50%;
  width: 7px; height: 7px;
  border-top: 1.5px solid var(--cream); border-right: 1.5px solid var(--cream);
  transform: translate(-25%, -50%) rotate(-135deg);
}
.board__arrow--next::before { transform: translate(-75%, -50%) rotate(45deg); }
.board__arrow:hover:not(:disabled) { border-color: var(--yellow); background: var(--void-3); }
.board__arrow:hover:not(:disabled)::before { border-color: var(--yellow); }
.board__arrow:disabled { opacity: .28; cursor: default; }

/* ---------- the rail ----------
   No wheel handler anywhere near this. Turning a wheel over it scrolls the
   page. Sideways is the arrows, a trackpad swipe, a drag, or arrow keys. */
.grp__rail {
  display: flex; gap: 18px;
  overflow-x: auto; overflow-y: hidden;
  scroll-snap-type: x proximity;
  /* NO scroll-behavior here on purpose. Every arrow on this page asks for
     a smooth scroll in the script; declaring one in the CSS as well makes
     the two cancel, and the rail does not move at all. Measured: with the
     declaration scrollBy lands on 2, without it on 342. */
  padding: 20px 2px 4px; margin-inline: -2px;
  scrollbar-width: none; -ms-overflow-style: none;
  overscroll-behavior-x: contain;
}
.grp__rail::-webkit-scrollbar { display: none; }
.grp__rail:focus-visible { outline: 2px solid var(--yellow); outline-offset: 6px; border-radius: 4px; }

.grp__bar {
  height: 2px; margin-top: 14px; border-radius: 2px;
  background: var(--line); overflow: hidden;
}
.grp__bar span {
  display: block; height: 100%; background: var(--yellow);
  transform: scaleX(0); transform-origin: left;
  transition: transform .3s cubic-bezier(.2,.9,.3,1);
}

/* ---------- one flavour ---------- */
.fl {
  flex: 0 0 clamp(196px, 21vw, 252px);
  scroll-snap-align: start;
  opacity: 0; transform: translateY(16px);
  transition: opacity .5s ease, transform .55s cubic-bezier(.2,.9,.3,1);
  transition-delay: calc(var(--i) * 65ms);
  cursor: default;
}
.grp__rail.is-in .fl { opacity: 1; transform: none; }
/* Der Fokus-Ring war gelb und 2px breit - genau die Goldmedaille. Wer mit
   der Tastatur durch die Reihe geht, haette jede Karte golden gesehen, und
   die Auszeichnung waere wertlos. Jetzt creme und AUSSERHALB der Karte, die
   Medaille sitzt innen: zwei Dinge, zwei Orte, keine Verwechslung. */
.fl:focus-visible { outline: 2px solid var(--cream); outline-offset: 3px; border-radius: 13px; }

.fl__shot {
  position: relative; aspect-ratio: 4 / 5;
  border-radius: 12px; overflow: hidden;
  background: var(--void-3);
}
.fl__shot img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  transition: transform .6s cubic-bezier(.2,.9,.3,1);
}
.fl:hover .fl__shot img { transform: scale(1.055); }

/* The description waits until it is asked for. At rest a card says two
   things; that is the whole point of the section. */
.fl__more {
  position: absolute; inset: auto 0 0 0;
  padding: 34px 15px 15px;
  background: linear-gradient(to top, rgba(20,11,17,.96) 42%, rgba(20,11,17,.72) 74%, transparent);
  transform: translateY(101%); opacity: 0;
  transition: transform .36s cubic-bezier(.2,.9,.3,1), opacity .26s ease;
}
.fl:hover .fl__more,
.fl:focus-within .fl__more,
.fl.is-open .fl__more { transform: none; opacity: 1; }
.fl__more p { margin: 0; font-size: .87rem; line-height: 1.5; color: #EFE8DE; }

/* -------------------------------------------------------------------------
   GOLD, SILBER, BRONZE

   Die drei meistbestellten Sorten JEDER Reihe, ausgezeichnet dort, wo die
   Entscheidung faellt - auf der Karte selbst. Ein Ring um das Bild, und
   beim Daruebergehen steht "Customers choice" in derselben Farbe ueber der
   Beschreibung.

   Der Ring liegt als box-shadow innen, nicht als border: eine Rahmenlinie
   haette die Karte breiter gemacht als ihre Nachbarn, und dann waere die
   Reihe an der Rasterung vorbeigelaufen.
   ------------------------------------------------------------------------- */
.fl--gold,   .fl--gold   .fl__pick-label { --medaille: #F9DB49; }
.fl--silber, .fl--silber .fl__pick-label { --medaille: #D6DCE4; }
.fl--bronze, .fl--bronze .fl__pick-label { --medaille: #D09154; }

/* Der Ring ist eine eigene Ebene, kein inset-Schatten am Kasten.

   Erster Versuch war genau das - und die Messung sagte, er sei gesetzt,
   waehrend auf dem Bildschirm nichts zu sehen war. Ein innerer Schatten
   wird HINTER dem Inhalt gemalt, und der Inhalt ist hier ein formatfuellendes
   Foto. Als Ueberlagerung liegt er darueber, auch ueber der Beschreibung,
   die beim Daruebergehen hochfaehrt.

   Dahinter eine dunkle Haarlinie: die Haelfte der Sorten liegt auf weissem
   Marmor, und Silber auf Weiss ist kein Ring, sondern ein Geruecht. */
.fl--gold .fl__shot::after,
.fl--silber .fl__shot::after,
.fl--bronze .fl__shot::after {
  content: ""; position: absolute; inset: 0; z-index: 2;
  border-radius: inherit; pointer-events: none;
  box-shadow: inset 0 0 0 3px var(--medaille),
              inset 0 0 0 4.5px rgba(20,11,17,.55);
}

.fl__pick-label {
  margin: 0 0 5px !important;
  font-family: var(--body); font-size: 10px !important; font-weight: 800;
  letter-spacing: .2em; text-transform: uppercase;
  color: var(--medaille) !important;
}

.fl__more .fl__liked {
  margin-top: 9px; color: var(--yellow);
  font-size: 10.5px; font-weight: 800; letter-spacing: .14em; text-transform: uppercase;
}

.fl__out {
  position: absolute; top: 11px; left: 11px; z-index: 2;
  font-family: var(--body); font-size: 10px; font-weight: 800;
  letter-spacing: .14em; text-transform: uppercase;
  color: #fff; background: var(--coral);
  padding: 5px 9px; border-radius: 999px;
}
.fl.is-out .fl__shot img { filter: grayscale(1) brightness(.5); }
.fl.is-out .fl__name { color: var(--ash); }
.fl.is-out .fl__price { color: var(--coral); }

/* No photograph: their own mark, held well back. All fifteen have one
   today, so this is a net rather than a look. */
.fl__mark {
  position: absolute; inset: 0;
  background: url("assets/brand/icon.png") 50% 50% / 46% no-repeat;
  opacity: .34;
}

/* Two things, one size, one weight, two colours. */
.fl__line {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 10px; margin-top: 13px;
}
.fl__name {
  font-family: var(--display); font-weight: 700; font-size: 1rem;
  letter-spacing: -.017em; color: var(--cream); margin: 0;
}
.fl__price {
  font-family: var(--display); font-weight: 700; font-size: 1rem;
  color: var(--yellow); font-variant-numeric: tabular-nums; flex: 0 0 auto;
}

@media (max-width: 720px) {
  .fl { flex-basis: 68vw; }
}

@media (prefers-reduced-motion: reduce) {
  .fl { transition: none; transition-delay: 0s; opacity: 1; transform: none; }
  .grp__rail { scroll-behavior: auto; }
  /* Die Kante wechselt dann sofort statt weich - die Maske bleibt. */
  .reels, .grp__rail, .gr__rail { transition: none; }
  .grp__rail .fl { opacity: 1; }
  .fl__more { transition: none; }
}

/* ---------- coffee and milk ----------
   These were the last two rows of the donut list, which is not how the shop
   lists them and is not how anybody thinks about them. Their own menu has
   Drinks as a heading of its own. */
.sips {
  margin-top: clamp(26px, 3vw, 38px);
  padding-top: clamp(22px, 2.6vw, 30px);
  border-top: 1px solid var(--line);
}
/* There are two of them. Stacking two things down a page that is already
   long enough is a waste of the width sitting empty beside them. */
.sips__list {
  list-style: none; margin: 14px 0 0; padding: 0;
  display: grid; gap: 0 clamp(26px, 4vw, 60px);
  grid-template-columns: 1fr 1fr; max-width: 900px;
}
.sips__list li {
  display: flex; align-items: baseline; gap: 12px;
  padding: 13px 0 0; border-top: 1px solid var(--line);
}
@media (max-width: 620px) {
  .sips__list { grid-template-columns: 1fr; }
}
.sips__name {
  font-family: var(--display); font-weight: 700; font-size: 1rem;
  letter-spacing: -.017em; color: var(--cream); flex: 0 0 auto;
}
.sips__desc { color: var(--ash); font-size: .88rem; flex: 1 1 auto; min-width: 0; }
.sips__price {
  font-family: var(--display); font-weight: 700; font-size: 1rem;
  color: var(--yellow); font-variant-numeric: tabular-nums;
}

/* =========================================================================
   ADDING ONE — the button on a card
   ========================================================================= */
/* It sits over the photograph rather than in the line under it, because the
   line is two things at one size and putting a third thing in it was what
   made the old menu unreadable. Always there on a touch screen; on a mouse
   it lifts in, so a rail at rest is still just photographs. */
.fl { position: relative; }
/* Plus, count, minus — one group in the corner. The minus and the number
   only exist once there is something in the box, so a rail nobody has
   touched still shows a single quiet plus per card. */
.fl__pick {
  position: absolute; top: 10px; right: 10px; z-index: 3;
  display: flex; align-items: center; gap: 4px;
  padding: 3px; border-radius: 999px;
  background: rgba(20,11,17,.62);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.22);
  opacity: 0; transform: scale(.82) translateY(-4px);
  transition: opacity .22s ease, transform .26s cubic-bezier(.2,1.3,.4,1);
}
.fl:hover .fl__pick,
.fl:focus-within .fl__pick,
.fl.is-open .fl__pick,
.fl.in-box .fl__pick { opacity: 1; transform: none; }
@media (hover: none) { .fl__pick { opacity: 1; transform: none; } }

.fl__less { display: none; }
.fl.in-box .fl__less { display: grid; }

.fl__less, .fl__add {
  width: 30px; height: 30px; border-radius: 999px;
  border: 0; padding: 0; cursor: pointer; background: transparent;
  display: grid; place-content: center;
  transition: background-color .18s ease;
}
.fl__less:hover, .fl__add:hover { background: var(--yellow); }
.fl__minus { display: block; width: 13px; height: 2px; border-radius: 1px; background: var(--cream); transition: background-color .18s ease; }
.fl__less:hover .fl__minus { background: var(--void); }

.fl__plus { position: relative; width: 14px; height: 14px; display: block; }
.fl__plus::before, .fl__plus::after {
  content: ""; position: absolute; inset: 0; margin: auto;
  background: var(--cream); border-radius: 1px;
  transition: background-color .2s ease;
}
.fl__plus::before { width: 14px; height: 2px; }
.fl__plus::after  { width: 2px;  height: 14px; }
.fl__add:hover .fl__plus::before,
.fl__add:hover .fl__plus::after { background: var(--void); }

/* How many of this one you already have, sitting between the two controls
   instead of floating off the corner. It only exists once there is a number
   to show. */
.fl__got {
  display: none; min-width: 16px; padding: 0 2px;
  text-align: center; color: var(--yellow);
  font-family: var(--body); font-size: 13px; font-weight: 800;
  font-variant-numeric: tabular-nums;
}
.fl.in-box .fl__got { display: block; }
.fl.in-box .fl__shot { box-shadow: 0 0 0 2px var(--yellow); }

/* It landed. One frame of feedback at the place your finger already is. */
.fl__add.just { animation: gotOne .42s cubic-bezier(.2,1.5,.4,1); }
@keyframes gotOne {
  0%   { transform: none; }
  38%  { transform: scale(1.22); }
  100% { transform: none; }
}
/* A phone has no pointer to hover with, so "appears on hover" means "never
   appears". On a touch screen the button is simply there — which is also
   why it sits on a dark disc rather than relying on the photograph behind
   it being dark enough. */
@media (hover: none) {
  .fl__add { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .fl__add { transition: none; opacity: 1; transform: none; }
  .fl__add.just { animation: none; }
}

/* =========================================================================
   ORDER DIRECT — what stands where the builder stood
   ========================================================================= */
/* The same proportions as the delivery row across the way, on purpose: both
   are "here is where you order", and they were reading as two different
   drafts. Ty: "screenshot 1 ist clean und perfekt... screenshot 2 muss auch
   auf dem level clean sein." */
.direct { display: grid; gap: 26px; align-items: center; }
@media (min-width: 900px) {
  .direct { grid-template-columns: 1fr minmax(380px, 520px); gap: clamp(32px, 3.4vw, 56px); }
}
/* One line, same size and weight as the delivery note — from the same rule. */
.direct__note { margin: 20px 0 0; max-width: 40ch; }

/* Mark, hairline, state, button — the anatomy of the Uber card, in the
   shop's own colours rather than Uber's. */
.direct__go {
  display: flex; flex-direction: column; align-items: stretch; gap: 20px;
  padding: 28px 28px 24px; border-radius: 18px;
  background: linear-gradient(168deg, var(--void-3) 0%, var(--void-2) 62%);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.06), 0 10px 30px rgba(0,0,0,.28);
}
.direct__mark {
  margin: 0;
  font-family: var(--body); font-size: 11px; font-weight: 800;
  letter-spacing: .24em; text-transform: uppercase; color: var(--yellow);
}
.direct__rule { height: 1px; background: rgba(255,255,255,.1); }
.direct__state {
  margin: 0;
  font-family: var(--display); font-weight: 700;
  font-size: 1.7rem; line-height: 1;
  letter-spacing: -.02em; color: var(--cream);
}
.direct__state.is-empty { color: var(--ash-quiet); }
.direct__btn {
  display: flex; align-items: center; justify-content: center; gap: 9px;
  padding: 15px 20px; border-radius: 999px; text-decoration: none;
  background: var(--yellow); color: var(--void);
  font-family: var(--body); font-weight: 700; font-size: 1.02rem;
  letter-spacing: -.01em;
  transition: background-color .2s ease, transform .2s ease;
}
.direct__btn:hover { background: #FFE86B; transform: translateY(-2px); }
.direct__btn:focus-visible { outline: 2px solid var(--cream); outline-offset: 3px; }
.direct__arrow { transition: transform .2s ease; }
.direct__btn:hover .direct__arrow { transform: translateX(3px); }

/* =========================================================================
   THE CART PAGE
   ========================================================================= */
.backlink {
  display: inline-flex; align-items: center; gap: 8px;
  color: var(--ash); text-decoration: none; font-size: .93rem;
  margin-bottom: 20px;
  transition: color .18s ease;
}
.backlink::before {
  content: ""; width: 7px; height: 7px;
  border-left: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg);
}
.backlink:hover { color: var(--cream); }

h1.sec-title { font-family: var(--display); font-weight: 800;
  font-size: clamp(1.9rem, 4vw, 2.9rem); line-height: 1.04;
  letter-spacing: -.03em; margin: 0 0 12px; }

.checkout { display: grid; gap: clamp(30px, 4vw, 58px); align-items: start; }
/* The title used to sit above the whole grid, which pushed the form down
   past it and left a tall empty strip beside it. It lives in the left
   column now, so the form starts level with the back link. */
.checkout__lines > .backlink { margin-bottom: 18px; }
.checkout__lines > #lines { margin-top: clamp(22px, 3vw, 34px); }
@media (min-width: 940px) { .checkout { grid-template-columns: minmax(0,1.25fr) minmax(0,.75fr); } }

/* ---------- one line in the box ---------- */
.ln {
  display: grid; align-items: center;
  grid-template-columns: 74px minmax(0,1fr) auto auto 30px;
  gap: clamp(12px, 1.8vw, 20px);
  padding: 16px 0; border-bottom: 1px solid var(--line);
}
.ln:first-child { padding-top: 0; }
.ln__shot {
  width: 74px; aspect-ratio: 4/5; border-radius: 9px;
  overflow: hidden; background: var(--void-3); position: relative;
}
.ln__shot img { width: 100%; height: 100%; object-fit: cover; display: block; }
.ln__name {
  font-family: var(--display); font-weight: 700; font-size: 1rem;
  letter-spacing: -.017em; color: var(--cream); margin: 0;
}
.ln__desc {
  margin: 4px 0 0; color: var(--ash); font-size: .86rem; line-height: 1.45;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.ln__each { margin: 6px 0 0; color: var(--ash-quiet); font-size: .875rem; }

.ln__qty {
  display: inline-flex; align-items: center; gap: 2px;
  border: 1px solid var(--line); border-radius: 999px; padding: 3px;
}
.ln__qty button {
  width: 28px; height: 28px; border-radius: 50%; border: 0; padding: 0;
  background: transparent; color: var(--cream); cursor: pointer;
  font-size: 15px; line-height: 1;
  transition: background-color .18s ease, color .18s ease;
}
.ln__qty button:hover { background: var(--yellow); color: var(--void); }

/* Nach dem Abschicken ist die Menge eine Angabe, kein Regler. Kein Rahmen,
   der zum Druecken einlaedt - die Zahl steht einfach da. */
.ln__qty--fest {
  border-color: transparent; padding: 3px 0;
  color: var(--ash); font-family: var(--mono);
}

/* Der Weg zur naechsten Bestellung. Steht unter der abgeschickten Liste,
   weil dort eben noch die Plus-Knoepfe waren. */
.ln-again {
  margin-top: 22px; padding-top: 20px; border-top: 1px solid var(--line);
  display: flex; flex-wrap: wrap; align-items: center; gap: 14px 18px;
}
.ln-again p { margin: 0; flex: 1 1 220px; color: var(--ash); font-size: .92rem; }
.ln-again .btn { flex: 0 0 auto; }
.ln__qty span {
  min-width: 24px; text-align: center;
  font-family: var(--display); font-weight: 700; font-size: .95rem;
  font-variant-numeric: tabular-nums; color: var(--cream);
}

.ln__sum {
  font-family: var(--display); font-weight: 700; font-size: 1rem;
  color: var(--yellow); font-variant-numeric: tabular-nums; text-align: right;
  min-width: 62px;
}
.ln__x {
  width: 30px; height: 30px; border-radius: 50%; border: 0; padding: 0;
  background: transparent; color: var(--ash-dim); cursor: pointer;
  font-size: 19px; line-height: 1;
  transition: background-color .18s ease, color .18s ease;
}
.ln__x:hover { background: rgba(225,76,95,.14); color: var(--coral); }

/* On a phone the line folds into two rows. It was `grid-column: 3 / -1`
   for the total, which is a 30px column: "$12.30" was rendered as "$12.3"
   with the last character cut off, and a document-level overflow check does
   not see it, because the clipping happens inside the cell. Named areas and
   an auto-width column instead, so the money can never be trimmed. */
@media (max-width: 620px) {
  .ln {
    grid-template-columns: 62px minmax(0,1fr) auto;
    grid-template-areas:
      "shot body x"
      "shot qty  sum";
    row-gap: 12px;
  }
  .ln__shot { grid-area: shot; width: 62px; align-self: start; }
  .ln__body { grid-area: body; }
  .ln__x    { grid-area: x;   align-self: start; justify-self: end; }
  .ln__qty  { grid-area: qty; justify-self: start; }
  .ln__sum  { grid-area: sum; text-align: right; }
  .ln__desc { display: none; }
}

.ln-none { padding: clamp(30px, 5vw, 56px) 0; }
.ln-none p { margin: 0 0 20px; color: var(--ash); font-size: 1.05rem; }

.tot {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 16px; padding-top: 20px; margin-top: 4px;
}
.tot[hidden] { display: none; }
.tot__label {
  font-family: var(--body); font-size: 12px; font-weight: 800;
  letter-spacing: .16em; text-transform: uppercase; color: var(--ash-quiet);
}
.tot__n {
  font-family: var(--display); font-weight: 800;
  font-size: clamp(1.5rem, 3vw, 2.1rem); letter-spacing: -.03em;
  color: var(--yellow); font-variant-numeric: tabular-nums;
}

.checkout__when {
  padding: clamp(22px, 2.6vw, 30px);
  border: 1px solid var(--line); border-radius: 14px;
  background: var(--void-2);
  position: sticky; top: 84px;
}
.checkout__when[hidden] { display: none; }
.checkout__h {
  font-family: var(--display); font-weight: 700;
  font-size: 1.15rem; letter-spacing: -.02em; margin: 0 0 18px; color: var(--cream);
}
@media (max-width: 939px) { .checkout__when { position: static; } }

/* =========================================================================
   OVER THE LINE — the order that needs a phone call
   ========================================================================= */
.bigorder {
  padding: clamp(22px, 2.6vw, 30px);
  border: 1px solid rgba(225,76,95,.3); border-radius: 14px;
  background:
    radial-gradient(90% 130% at 12% 0%, rgba(225,76,95,.14), transparent 64%),
    var(--void-2);
}
.bigorder[hidden] { display: none; }
.bigorder .label { margin-bottom: 10px; }
.bigorder__say { margin: 0 0 20px; color: var(--ash); font-size: .95rem; line-height: 1.6; }
.bigorder .form-note { margin: 14px 0 0; }
/* The rows were siblings with nothing between them, so a bigger label made
   the whole form read as one crowded block. One column, one rhythm. */
#pickPanel { display: flex; flex-direction: column; gap: 22px; }
#pickPanel .form-note { margin-top: -8px; }
#pickPanel[hidden] { display: none; }
.checkout__when[hidden] { display: none; }

/* What was sent, and the ways to send it again. The order is printed out
   in full underneath, because a person who has just handed their name and
   number to a shop should be able to see exactly what went. */
.form-ok.sent { text-align: left; }
.form-ok.sent p { margin: 10px 0 0; color: var(--ash); font-size: .94rem; line-height: 1.6; }
/* Zwei gleich breite Knoepfe nebeneinander. auto-fit statt "1fr 1fr", damit
   sie untereinander rutschen, wo die Spalte zu schmal wird, statt aus ihr
   herauszulaufen - die Bestaetigung steht am Handy in voller Breite, auf dem
   Schreibtisch in einer knapp 520px breiten Spalte. */
.sent__row {
  display: grid; gap: 10px; margin-top: 20px;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
/* Enger als ein normaler Knopf, sonst passen keine zwei nebeneinander.
   Gemessen in der Spalte, die sie bewohnen: 287px breit, also 138px je
   Knopf - und ein Standardknopf verbraucht allein 49px Polsterung plus 25px
   fuer Zeichen und Abstand. Darum hier 14px Polsterung, kleinere Schrift und
   kurze Worte; das Zeichen traegt die Bedeutung mit. */
.sent__row .btn {
  gap: 7px; width: 100%; min-width: 0;
  padding-inline: 14px; font-size: 14.5px; white-space: nowrap;
}
.sent__row .btn svg { flex: 0 0 auto; }

.sent__copy {
  margin: 20px 0 0; padding: 16px 18px;
  background: rgba(0,0,0,.34); border: 1px solid var(--line); border-radius: 10px;
  font-family: var(--mono); font-size: 12.5px; line-height: 1.7; color: var(--ash);
  white-space: pre-wrap; word-break: break-word;
}

/* The way out for anyone without WhatsApp, offered before they commit to it
   rather than after they have hit the wall. */
.form-note a {
  color: var(--cream); text-decoration: none;
  border-bottom: 1px solid rgba(251,243,226,.34);
}
.form-note a:hover { border-bottom-color: var(--cream); }

/* =========================================================================
   THE PICK-UP FORM — order, and checks worth the name
   ========================================================================= */
.when__head { display: grid; gap: 8px; }
.when__req { margin: 0; color: var(--ash); font-size: .9rem; line-height: 1.55; }

/* Which ones they cannot do without, said on the label itself rather than
   with an asterisk nobody reads a key for. */
label i {
  font-style: normal; font-weight: 700; font-size: 10.5px;
  letter-spacing: .1em; color: var(--ash-dim);
  margin-left: 7px;
}
label i.opt { color: var(--ash-dim); }

.field__err, .field__hint {
  margin: 0; font-size: .85rem; line-height: 1.5;
}
.field__err { color: var(--coral); }
.field__err.is-note { color: var(--ash-quiet); }
.field__err[hidden], .field__hint[hidden] { display: none; }
.field__hint { color: var(--ash-quiet); }

.field.is-bad input,
.field.is-bad select,
.field.is-bad textarea { border-color: var(--coral); }

textarea { resize: vertical; min-height: 64px; line-height: 1.5; }

.when__go { display: grid; gap: 12px; }
/* A button that cannot do anything should not look like it can. */
.btn:disabled,
.btn[disabled] {
  opacity: .42; cursor: not-allowed; transform: none;
}
.btn:disabled:hover { transform: none; }

/* ---------- after it has been handed over ---------- */
.form-ok.sent { text-align: left; }
.sent__ref {
  margin: 0 0 12px; font-family: var(--body); font-size: 12px; font-weight: 800;
  letter-spacing: .16em; text-transform: uppercase; color: var(--ash-quiet);
}
.sent__ref b {
  font-family: var(--mono); font-size: 15px; letter-spacing: .06em;
  color: var(--yellow); margin-left: 8px;
}
.form-ok.sent h3 {
  font-family: var(--display); font-weight: 800; font-size: 1.3rem;
  letter-spacing: -.022em; margin: 0 0 16px; color: var(--cream);
}
.sent__steps {
  margin: 0; padding-left: 20px;
  display: grid; gap: 10px;
  color: var(--ash); font-size: .94rem; line-height: 1.6;
}
.sent__steps b { color: var(--cream); }

/* ---------- the pick-up day ----------
   Was a date input, which offers a year — meaningless when the window is
   fourteen days, and the reason the form needed error messages for 2002 and
   for 0344 at all. Only days they are open appear here, and nothing can be
   typed, so a whole class of mistakes stopped existing. */
/* Three goes at this control. A date input, which offered a year for a
   fortnight-long window. Then a sideways strip, which hid half the days
   behind a gesture nobody is told about. Then eleven tiles at once, which
   is a wall — and which treats a rare case like a common one, because
   almost nobody reserves donuts two weeks out.

   Now: the three days people actually mean, in one quiet row, and the rest
   behind one word. */
.days {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
}
.days .day.is-extra { display: none; }
.days.is-open .day.is-extra { display: grid; }

.day {
  padding: 8px 6px 9px;
  border: 1px solid var(--line); border-radius: 10px;
  background: transparent; cursor: pointer;
  display: grid; gap: 1px; justify-items: center;
  transition: border-color .18s ease, background-color .18s ease;
}
.day b {
  font-family: var(--body); font-size: 10px; font-weight: 800;
  letter-spacing: .1em; text-transform: uppercase; color: var(--ash-quiet);
}
.day i {
  font-family: var(--display); font-style: normal; font-weight: 700;
  font-size: 1.02rem; letter-spacing: -.02em; color: var(--cream);
  font-variant-numeric: tabular-nums;
}

/* Quiet on purpose: it is a way out, not the thing to do. */
.days__more {
  justify-self: start; margin-top: 10px;
  background: none; border: 0; padding: 0; cursor: pointer;
  font-family: var(--body); font-size: .88rem; color: var(--ash-quiet);
  text-decoration: underline; text-underline-offset: 3px;
  text-decoration-color: rgba(192,184,172,.4);
  transition: color .18s ease;
}
.days__more:hover { color: var(--cream); }
.days__more[hidden] { display: none; }
.day:hover { border-color: var(--ash-dim); background: var(--void-3); }
.day.is-on {
  background: var(--yellow); border-color: var(--yellow);
}
.day.is-on b { color: rgba(20,11,17,.72); }
.day.is-on i { color: var(--void); }
.day:focus-visible { outline: 2px solid var(--yellow); outline-offset: 3px; }

.days__none { margin: 0; color: var(--ash); font-size: .9rem; line-height: 1.55; }

/* The two variable axes that were set here — SOFT for round terminals and
   WONK for letters leaning out of true — belonged to Fraunces. Gabarito is
   variable on weight alone and already round by drawing, so the rules are
   gone rather than left sitting there declaring settings nothing reads. */

/* The still under every clip. A <video> drops its poster the moment it has
   metadata and paints frame zero instead — and these clips open on a dark
   frame, so a browser that refuses autoplay showed a row of black boxes.
   The tile carries the photograph itself; the video sits on top of it. */
.tile.has-still { background-size: cover; background-position: center; }
.tile.is-still video { opacity: 0; }
.tile video { transition: opacity .3s ease; }

/* ---------- the wall's arrow heads ----------
   Pinned to the middle of each edge, over the strip, so the wall says by
   itself that it holds more than fits. */
/* Three columns: a gutter, the clips, a gutter. The arrows were absolutely
   positioned over the rail before, which put them on top of the first and
   last clip. Both tracks are a fixed width and stay reserved even while a
   button is hidden, so nothing shifts sideways when the back arrow appears. */
.wall__rail {
  position: relative;
  display: grid; align-items: center;
  grid-template-columns: 46px minmax(0, 1fr) 46px;
  column-gap: 14px;
}
/* Each item names its own column. Without that, a hidden back arrow drops
   out of the flow and auto-placement slides the clips into the 46px gutter
   track — the rail came out 46 pixels wide. */
.wall__more { grid-column: 3; }
.reels      { grid-column: 2; }
.wall__more {
  width: 46px; height: 46px; border-radius: 50%; position: relative;
  border: 1px solid var(--line); background: rgba(20,11,17,.86);
  cursor: pointer; transition: background-color .2s ease, border-color .2s ease;
}
.wall__more[hidden] { display: none; }
.wall__more--back { grid-column: 1; }
.wall__more::before {
  content: ""; position: absolute; top: 50%; left: 50%;
  width: 11px; height: 11px;
  border-top: 2px solid var(--cream); border-right: 2px solid var(--cream);
  transform: translate(-70%, -50%) rotate(45deg);
}
.wall__more--back::before { transform: translate(-30%, -50%) rotate(-135deg); }
.wall__more:hover { background: var(--yellow); border-color: var(--yellow); }
.wall__more:hover::before { border-color: var(--void); }
/* Two 46px gutters out of a 375px screen is most of a clip. On a phone the
   arrows go and the swipe does the work — and because the rail pulls the
   next batch in as you approach the end, swiping reaches every clip. */
@media (max-width: 720px) {
  .wall__rail { grid-template-columns: minmax(0, 1fr); }
  .wall__more { display: none; }
  /* and the rail moves back to the only column there is, or the grid grows
     an implicit second one and the whole wall sits outside the screen */
  .reels { grid-column: 1; }
}

/* If autoplay was refused the tile becomes a play button, because sitting
   there showing a still and offering nothing is the worse failure. */
.tile.can-play { cursor: pointer; }
.tile.can-play::after {
  content: ""; position: absolute; top: 50%; left: 50%;
  width: 54px; height: 54px; margin: -27px 0 0 -27px; z-index: 5;
  border-radius: 50%; background: rgba(20,11,17,.72);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.28);
  transition: background-color .2s ease;
}
.tile.can-play::before {
  content: ""; position: absolute; top: 50%; left: 50%; z-index: 6;
  margin: -9px 0 0 -5px;
  border-style: solid; border-width: 9px 0 9px 14px;
  border-color: transparent transparent transparent var(--cream);
}
.tile.can-play:hover::after { background: var(--yellow); }
.tile.can-play:hover::before { border-left-color: var(--void); }

/* =========================================================================
   THE GOOGLE REVIEWS — hellogreek.ca's widget, measured off the live page

   The first version of this had the arrangement right and everything else
   wrong. Every number below was read out of their rendered Trustindex
   widget rather than remembered:

     card 270px wide, 206px tall and IDENTICAL for every review
     inner white #FFF, 12px radius, 20px padding
     shadow 1px 4px 10px rgba(0,0,0,.1), 0 0 2px rgba(0,0,0,.05)
     name 600 / 15px          date 13px / #6B6B6B
     text 15px / 21.75px, four lines, then cut

   White cards on our own ground rather than dark ones in our palette: a
   review is somebody else's word, and it reads as somebody else's word when
   it does not wear our paint. It is also, for better or worse, what people
   recognise as a Google review.
   ========================================================================= */
.gr {
  display: grid; gap: clamp(26px, 3.4vw, 46px); align-items: start;
  margin-top: clamp(26px, 3vw, 38px);
}
@media (min-width: 900px) { .gr { grid-template-columns: 232px minmax(0, 1fr); } }

/* Their summary block is small: a mark, the name, the stars, the count, and
   an outlined button. The 3.6rem "5.0" that stood here said the same thing
   the headline two lines above already says. */
.gr__sum {
  display: grid; grid-template-columns: auto 1fr; gap: 4px 14px;
  align-items: center;
}
.gr__logo {
  width: 48px; height: 48px; border-radius: 10px; display: block;
  background: var(--void-3);
}
.gr__id { min-width: 0; }
.gr__name {
  margin: 0; font-family: var(--body); font-weight: 700; font-size: .95rem;
  color: var(--cream); letter-spacing: -.01em;
}
.gr__stars {
  margin: 3px 0 0; color: #F5B72F; font-size: .92rem; letter-spacing: .1em;
}
.gr__count { margin: 3px 0 0; color: var(--ash); font-size: .82rem; }
.gr__write {
  grid-column: 1 / -1; justify-self: start; margin-top: 14px;
  display: inline-block; padding: 9px 18px; border-radius: 999px;
  border: 1px solid var(--line); color: var(--cream); text-decoration: none;
  font-family: var(--body); font-weight: 600; font-size: .88rem;
  transition: border-color .2s ease, background-color .2s ease;
}
.gr__write:hover { border-color: var(--yellow); background: var(--void-3); }

/* Arrow, cards, arrow. On hellogreek the circles sit slightly over the first
   and last card; here they get their own tracks, because an arrow standing on
   the content is the exact thing Ty had just told me off about on the reel
   wall. Both gutters stay reserved, so nothing shifts when one disappears. */
.gr__deck {
  display: grid; align-items: center;
  grid-template-columns: 30px minmax(0, 1fr) 30px;
  column-gap: 12px;
}
/* Not a scroll container. scrollTo({behavior:"smooth"}) was measured doing
   nothing at all on this rail — 500ms after the call, scrollLeft was still 0
   while a direct assignment moved it at once — and a carousel that jumps is
   what "die animationen ... falsch" meant. So the cards ride on a track that
   a transform moves, where the transition is real. touch-action keeps
   vertical page scrolling with the page while a sideways drag is ours. */
/* ---------- der Verlauf an einer angeschnittenen Kante ----------
   Gilt fuer ALLE drei Schienen, die seitlich laufen: die Reel-Wand, die
   Reihen auf der Tafel und die Bewertungen. Ty: "ueberall wo man seitlich
   scrollt will ich dass nicht so hart kantik abgeschnitten sein sondern so
   einen fade haben."

   Einen Verlauf bekommt nur die Kante, die gerade wirklich eine Karte
   zerschneidet. An einer buendigen Kante waere er kein Auslaufen, sondern
   ein Schleier auf einer vollstaendigen Karte - deshalb bleibt links hart,
   solange die Schiene am Anfang steht. Welche Seite dran ist, setzt das
   Skript als data-fade, aus demselben Scroll-Zustand, der auch schon die
   Pfeile ausgraut.

   --fade-l/--fade-r sind mit @property angemeldet, weil eine nicht
   angemeldete Custom Property springt statt zu animieren - die Kante waere
   dann ein Aufploppen. Wo @property fehlt, schaltet sie hart um; die Maske
   selbst stimmt trotzdem. */
@property --fade-l { syntax: "<length>"; inherits: false; initial-value: 0px; }
@property --fade-r { syntax: "<length>"; inherits: false; initial-value: 0px; }

.reels, .grp__rail, .gr__rail {
  /* 92px war auf dem Schreibtisch richtig und auf dem Handy viel zu breit:
     die Bewertungsschiene ist dort nur 267px breit, zwei Verlaeufe zu 92px
     haetten 69 % davon verwaschen - und man sieht dort immer nur EINE Karte.
     Gemessen bei 375px Fensterbreite: 46px laesst Name, Sterne und Text
     lesbar und die Kante trotzdem weich. Oben bleibt es bei den 92px. */
  --fade-w: clamp(46px, 6.5vw, 92px);
  --fade-l: 0px;
  --fade-r: 0px;
  transition: --fade-l .28s ease, --fade-r .28s ease;
  -webkit-mask-image: linear-gradient(to right, transparent 0,
                        #000 var(--fade-l),
                        #000 calc(100% - var(--fade-r)), transparent 100%);
          mask-image: linear-gradient(to right, transparent 0,
                        #000 var(--fade-l),
                        #000 calc(100% - var(--fade-r)), transparent 100%);
}
[data-fade~="links"]  { --fade-l: var(--fade-w); }
[data-fade~="rechts"] { --fade-r: var(--fade-w); }

.gr__rail {
  grid-column: 2;
  overflow: hidden;
  padding: 6px 2px 10px; margin-inline: -2px;
  touch-action: pan-y; cursor: grab;
}
.gr__rail:active { cursor: grabbing; }
.gr__track {
  display: flex; gap: 18px; align-items: stretch;
  will-change: transform;
}
.gr__rail:focus-visible { outline: 2px solid var(--yellow); outline-offset: 6px; border-radius: 4px; }

.gr__arrow {
  grid-column: 3;
  width: 30px; height: 30px; border-radius: 50%; position: relative;
  border: 0; background: #FFFFFF; cursor: pointer;
  box-shadow: 0 1px 4px rgba(0,0,0,.28);
  transition: transform .2s ease, opacity .2s ease;
}
.gr__arrow { grid-column: 1; }
.gr__arrow--next { grid-column: 3; }
.gr__arrow::before {
  content: ""; position: absolute; top: 50%; left: 50%;
  width: 7px; height: 7px;
  border-top: 1.6px solid #3C3C3C; border-right: 1.6px solid #3C3C3C;
  transform: translate(-25%, -50%) rotate(-135deg);
}
.gr__arrow--next::before { transform: translate(-75%, -50%) rotate(45deg); }
.gr__arrow:hover:not(:disabled) { transform: scale(1.08); }
/* Never disabled any more: the rail loops, so there is always a next one
   and always a previous one. */
.gr__arrow:disabled { opacity: 0; pointer-events: none; }

/* ---------- one review ----------
   min-height rather than height: the four-line cut caps the tallest element
   on the card, so this floor is what makes a two-word review the same size
   as a four-line one. */
.grc {
  flex: 0 0 clamp(250px, 27vw, 292px);
  min-height: 206px; box-sizing: border-box;
  display: flex; flex-direction: column;
  background: #FFFFFF; border-radius: 12px; padding: 20px;
  text-decoration: none;
  box-shadow: 1px 4px 10px rgba(0,0,0,.1), 0 0 2px rgba(0,0,0,.05);
  transition: transform .2s ease, box-shadow .2s ease;
}
.grc:hover { transform: translateY(-3px); box-shadow: 1px 8px 20px rgba(0,0,0,.22); }
.grc__top { display: flex; align-items: center; gap: 11px; }
.grc__face {
  width: 40px; height: 40px; border-radius: 50%; flex: 0 0 auto;
  background: var(--f); color: #FFFFFF;
  display: grid; place-content: center;
  font-family: var(--body); font-weight: 600; font-size: 1.05rem;
}
.grc__who { display: grid; gap: 0; min-width: 0; margin-right: auto; }
.grc__who b {
  font-family: var(--body); font-weight: 600; font-size: 15px; line-height: 21px;
  color: #1A1A1A;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.grc__who i {
  font-style: normal; font-size: 13px; line-height: 18px; color: #6B6B6B;
}
.grc__g { flex: 0 0 auto; display: block; }

/* Measured twice. On the original the star is a 17px image and the tick is
   15px, so the tick is the smaller, quieter thing. Mine had it the other way
   round: the star glyph's ink at 15px is 14.1px tall, against a 15px tick —
   and a filled disc reads heavier than a star outline of the same height, so
   the tick sat there looking like the main event. Ty: "Ist es normal das der
   haken so komisch positioniert ist?"

   Stars up to 17px (ink 15.9), tick down to 14px: the same 0.88 ratio the
   original has. The 1px nudge puts the disc on the star's ink centre rather
   than on the text box's centre — a star has almost no descender, so those
   two are not the same line. */
.grc__stars {
  margin: 12px 0 9px; color: #F5B72F; font-size: 17px; letter-spacing: .06em;
  display: flex; align-items: center; gap: 7px;
}
/* The blue tick at the end of the star row. It is an <svg> in the markup
   now: as two CSS rules — a circle plus a rotated border for the check — the
   check's position was two typed pixel values, and it sat off centre inside
   its own circle. */
.grc__ok { display: block; flex: 0 0 auto; position: relative; top: 1px; }
/* Four lines, then an ellipsis — the rest of that review is one tap away on
   Google, which is where it lives anyway. */
.grc__text {
  margin: 0; color: #1A1A1A; font-size: 15px; line-height: 21.75px;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 4;
  overflow: hidden;
}
/* 76vw was wider than the rail it sits in once the two 30px arrow gutters
   come off a 375px screen, so no card was ever fully visible. Matching the
   rail exactly means one whole review at a time, which is what the phone
   version of the original does too. */
@media (max-width: 620px) {
  .gr__deck { grid-template-columns: 28px minmax(0, 1fr) 28px; column-gap: 8px; }
  .grc { flex-basis: 100%; }
}

/* =========================================================================
   DIE SEITWAERTS-SCHIENEN AUF EINEM ZEIGEGERAET

   Mit Maus oder Trackpad scrollt der Browser diese Schienen nicht mehr
   selbst - das Skript bewegt sie, und nur wenn die Geste eindeutig
   seitwaerts ist. Sonst nahm eine minimale Seitwaerts-Abweichung beim
   Runterlesen die Schiene mit. Programmgesteuertes scrollLeft funktioniert
   bei overflow:hidden unveraendert, Pfeile und Nachladen bleiben also heil.

   Auf einem Touchscreen bleibt es ein ganz normaler Scroller: ein Finger
   bekommt seine Achsensperre schon vom System, und dort gibt es kein Rad.
   ========================================================================= */
@media (hover: hover) and (pointer: fine) {
  .reels, .grp__rail { overflow-x: hidden; }

  /* Und keine Rasterung. Sie wurde am Gestenende wieder eingeschaltet und
     riss die Schiene dann ohne Uebergang auf die naechste Karte - 68px in
     einem Bild, genau in dem Moment, in dem der Finger schon runterscrollt.
     Ty: "buggt die Anzeige und rastet sich ein... soll clean sein und 0
     ruckartig." Ein Rad haelt an, wo es anhaelt. Auf einem Touchscreen
     bleibt die Rasterung, dort gehoert sie hin und laeuft mit Schwung. */
  .reels, .grp__rail { scroll-snap-type: none; }
}
