/* ═══════════════════════════════════════════════════════════════════════════
   Sweet Family Coffee

   Ground is a warm near-black plum, cream is the ink, berry is the field, and
   one sorbet accent does all the pointing. Two chapters break to cream — the
   order flow and the opening hours — because those are the two places a reader
   has to actually read.

   Everything here is written for this site. Design tokens live in :root; no raw
   hex below this block.
   ═══════════════════════════════════════════════════════════════════════════ */

:root {
  /* ── ground ────────────────────────────────────────────────────────────── */
  --plum:        #14060C;   /* page ground — warm near-black, never #000 */
  --plum-up:     #1E0A13;   /* one step lifted, for cards on the ground */
  --plum-edge:   #2C1420;   /* hairlines on the ground */
  --berry:       #8E1B4E;   /* the brand */
  --berry-lift:  #B23063;
  --cream:       #FBF2E9;   /* the ink — warm, never #fff */
  --cream-warm:  #F4E6D8;   /* cream chapters sit on this */
  --shell:       #FBF7F2;   /* the hero only — matched to its photograph's backdrop */
  --sorbet:      #FF6B3D;   /* the accent. Sparing. */
  --leaf:        #7FB069;   /* open. Only ever the status dot. */

  /* ── functional ────────────────────────────────────────────────────────── */
  /* The three ink levels are set by contrast, not by eye: on their own ground
     they measure 15.9 : 8.0 : 5.2 against plum and 13.9 : 6.3 : 5.1 against
     cream. Level 3 carries 11px uppercase labels, so it cannot go dimmer than
     4.5:1 however much better a fainter grey would look. */
  --ink:         var(--cream);
  --ink-2:       rgba(251, 242, 233, 0.66);
  --ink-3:       rgba(251, 242, 233, 0.52);
  --rule:        rgba(251, 242, 233, 0.14);
  --surface:     rgba(251, 242, 233, 0.045);
  /* A warm hairline, not a neutral one. Only the outer rule of the two hours
     cards uses it; it is berry at the weight where the line still reads as a frame
     rather than as a coloured border. It does not invert, because the only
     chapter it appears in is a cream one. */
  --rule-warm:   rgba(142, 27, 78, 0.32);

  /* inverted set, swapped in wholesale by .light */
  --ink-d:       #1B0810;
  --ink-d2:      rgba(27, 8, 16, 0.68);
  --ink-d3:      rgba(27, 8, 16, 0.62);
  --rule-d:      rgba(27, 8, 16, 0.16);

  /* ── type ──────────────────────────────────────────────────────────────── */
  --serif: "Fraunces", "Georgia", "Times New Roman", serif;
  --sans:  "Space Grotesk", "Helvetica Neue", Arial, sans-serif;
  /* One place on the page uses a third face: the closing band's lettering.
     Allura, because it is one of the few calligraphic faces with a latin-ext
     cut that actually draws ě ř ů — several thinner, closer matches do not, and
     a missing caron on a Czech page is a fallback glyph in the middle of a word.
     If this is ever swapped, check the replacement against ě š č ř ž ý á í é ú ů ó
     before anything else. Nothing but .band__word may use it. */
  --script: "Allura", "Segoe Script", "Brush Script MT", cursive;

  /* Display sits at ~200px against 17px body — a ratio of 12:1. That gap is the
     whole hierarchy; do not close it to make something fit.
     It is bound to the viewport's *shorter* dimension as well as its width, so
     the hero still fits a laptop screen without the type being timid on a
     wide one. */
  --t-display: clamp(2.7rem, min(12.4vw, 19vh), 14rem);
  /* the hero's caps lockup — bound to height too, so the split hero fits a laptop */
  --t-shout:   clamp(2.6rem, min(7vw, 11.5vh), 7rem);
  --t-title:   clamp(2.5rem, 6.4vw, 6rem);
  --t-lede:    clamp(1.15rem, 1.6vw, 1.5rem);
  --t-body:    1.0625rem;
  --t-small:   0.8125rem;
  --t-micro:   0.6875rem;

  /* ── measure ───────────────────────────────────────────────────────────── */
  --wrap: 1360px;
  --gut:  clamp(1.25rem, 5vw, 4.5rem);
  /* One radius for every button, so they cannot drift apart. Full pill, which
     is the shape the header phone pill and the circle CTA already speak.
     For a squarer look this is the only value to change — try 0.5rem. */
  --r-btn: 999px;
  /* One radius for panels, as --r-btn is the one radius for controls. It exists
     for the contact chapter's map card, which is the only thing on the site big
     enough to need a corner rather than a hairline — the boards are 5px and the
     mosaic is deliberately square. */
  --r-panel: clamp(16px, 2.2vw, 28px);
  --bay:  clamp(5.5rem, 12vh, 11rem);   /* vertical rhythm between chapters */

  /* ── motion ────────────────────────────────────────────────────────────── */
  --expo:  cubic-bezier(0.16, 1, 0.3, 1);
  --quart: cubic-bezier(0.25, 1, 0.5, 1);
  --swing: cubic-bezier(0.87, 0, 0.13, 1);

  --head-now: 82px;
  --progress: 0;
}

/* ── reset ────────────────────────────────────────────────────────────────── */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-padding-top: calc(var(--head-now) + 2rem);
  background: var(--plum);
}

body {
  margin: 0;
  background: var(--plum);
  color: var(--ink);
  font-family: var(--sans);
  font-size: var(--t-body);
  font-weight: 350;
  line-height: 1.62;
  letter-spacing: 0.002em;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

html.is-locked, html.is-locked body { overflow: hidden; }

img, svg { display: block; max-width: 100%; }
img { height: auto; }

h1, h2, h3, p, dl, dd, dt, figure, blockquote, ol, ul { margin: 0; padding: 0; }
ol, ul { list-style: none; }
address { font-style: normal; }

a { color: inherit; text-decoration: none; }

button {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}

::selection { background: var(--sorbet); color: var(--plum); }

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

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

.skip {
  position: fixed; top: 0; left: 50%;
  transform: translate(-50%, -110%);
  z-index: 200;
  padding: 0.9rem 1.6rem;
  background: var(--sorbet); color: var(--plum);
  font: 500 var(--t-micro)/1 var(--sans);
  letter-spacing: 0.16em; text-transform: uppercase;
  transition: transform 0.4s var(--expo);
}
.skip:focus { transform: translate(-50%, 0); }

.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

/* ── shared type ──────────────────────────────────────────────────────────── */

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

.eyebrow {
  display: flex;
  align-items: center;
  gap: 0.75em;
  font-family: var(--sans);
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.eyebrow i {
  flex: 0 0 auto;
  width: 6px; height: 6px;
  background: var(--sorbet);
  transform: rotate(45deg);
}

.title {
  font-family: var(--serif);
  font-size: var(--t-title);
  font-weight: 400;
  font-variation-settings: "opsz" 144, "SOFT" 40, "WONK" 1;
  line-height: 0.94;
  letter-spacing: -0.03em;
  text-wrap: balance;
}
/* the italic's exit stroke eats the word space after it — give it back */
.title em {
  font-style: italic;
  font-variation-settings: "opsz" 144, "SOFT" 100, "WONK" 1;
  color: var(--sorbet);
  padding-right: 0.05em;
}
.light .title em { color: var(--berry); }

.lede {
  max-width: 46ch;
  font-size: var(--t-lede);
  font-weight: 300;
  line-height: 1.55;
  color: var(--ink-2);
  text-wrap: pretty;
}

.secthead { display: grid; gap: 1.4rem; margin-bottom: clamp(3rem, 7vw, 6rem); }
.secthead--mid { justify-items: center; text-align: center; }
.secthead--mid .lede { text-align: center; }

/* ── buttons ──────────────────────────────────────────────────────────────── */

/* ── the hover gesture, once, for the whole site ───────────────────────────
   Hard invert: on hover a button swaps its fill and its ink for each other and
   takes a 1px edge. Nothing slides and nothing wipes — the only properties that
   animate are background-color, color and border-color.

   Four custom properties carry it, so a variant only has to declare its two
   colour pairs and inherits the gesture:
     --btn-bg / --btn-fg        rest
     --btn-bg-on / --btn-fg-on  hover, focus-visible
     --btn-edge / --btn-edge-on the 1px edge in each state
   The edge exists at rest as `transparent`, so it can never shift the layout
   when it takes a colour. box-sizing is border-box, so it costs no width. */

.btn {
  --btn-bg: var(--sorbet);
  --btn-fg: var(--plum);
  --btn-bg-on: var(--plum);
  --btn-fg-on: var(--sorbet);
  --btn-edge: transparent;
  --btn-edge-on: var(--btn-fg-on);
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.7em;
  padding: 1.05rem 1.8rem;
  border: 1px solid var(--btn-edge);
  border-radius: var(--r-btn);
  background: var(--btn-bg);
  color: var(--btn-fg);
  font-family: var(--sans);
  font-size: var(--t-small);
  font-weight: 500;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  /* colours only — a button never moves */
  transition: background-color 0.4s var(--expo), color 0.4s var(--expo),
              border-color 0.4s var(--expo);
}
.btn .ico { width: 17px; height: 17px; flex: 0 0 auto; }

.btn:hover, .btn:focus-visible {
  background: var(--btn-bg-on);
  color: var(--btn-fg-on);
  border-color: var(--btn-edge-on);
}

/* sorbet on plum both ways round — 7.0:1 in each state */
.btn--line {
  --btn-bg: transparent;
  --btn-fg: var(--ink);
  --btn-bg-on: var(--ink);
  --btn-fg-on: var(--plum);
  --btn-edge: var(--rule);
  --btn-edge-on: var(--ink);
}
/* filled berry inverts to outlined berry on the cream chapters — 7.1:1 */
.btn--dark {
  --btn-bg: var(--berry);
  --btn-fg: var(--cream);
  --btn-bg-on: transparent;
  --btn-fg-on: var(--berry);
  --btn-edge: transparent;
  --btn-edge-on: var(--berry);
}

/* ── circle CTA ───────────────────────────────────────────────────────────
   Label, a rule running right from it, terminating in a hairline ring. Lifted
   from the client's reference hero; pointed at a phone call rather than a
   catalogue this business does not have. See DESIGN.md §9. */

.circle-cta {
  display: inline-flex;
  align-items: center;
  gap: 0.9rem;
  width: fit-content;
}
.circle-cta__label {
  font-size: var(--t-small);
  font-weight: 500;
  letter-spacing: 0.06em;
  color: var(--ink);
  white-space: nowrap;
}
.circle-cta__rule {
  display: block;
  width: clamp(2.5rem, 6vw, 5rem);
  height: 1px;
  background: currentColor;
  opacity: 0.35;
  transform-origin: left;
  transition: transform 0.55s var(--expo), opacity 0.4s var(--quart);
}
.circle-cta__ring {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 56px; height: 56px;
  margin-left: -0.9rem;   /* the rule runs into the ring, not up to it */
  border: 1px solid currentColor;
  border-radius: 50%;
  color: var(--ink-3);
  transition: background-color 0.5s var(--expo), color 0.5s var(--quart),
              border-color 0.5s var(--quart);
}
.circle-cta__ring svg { width: 18px; height: 18px; color: var(--ink); transition: color 0.5s var(--quart); }

/* Same gesture as .btn: the ring inverts rather than growing. The rule only
   goes solid — it used to stretch, which was the one bit of this control that
   moved rather than swapped, and it read as a different idea from the buttons. */
.circle-cta:hover .circle-cta__rule,
.circle-cta:focus-visible .circle-cta__rule { opacity: 0.9; }
.circle-cta:hover .circle-cta__ring,
.circle-cta:focus-visible .circle-cta__ring { background: var(--ink); border-color: var(--ink); }
.circle-cta:hover .circle-cta__ring svg,
.circle-cta:focus-visible .circle-cta__ring svg { color: var(--shell); }

.row__cta { margin-top: clamp(3rem, 6vw, 5rem); display: flex; justify-content: center; }

/* ── grain ────────────────────────────────────────────────────────────────── */
/* A fixed film over the whole page. It is what keeps large flat plum fields
   from looking like a screen fill. Never interactive. */

.grain {
  position: fixed;
  inset: -50%;
  z-index: 90;
  pointer-events: none;
  opacity: 0.32;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)' opacity='0.55'/%3E%3C/svg%3E");
}

/* ── curtain ──────────────────────────────────────────────────────────────── */
/* Only exists when JS is on (html.js), but is removed by CSS on a fixed
   timeline with fill-mode forwards — so a script error later cannot trap it
   over the page. */

.curtain { display: none; }

html.js .curtain {
  display: grid;
  place-items: center;
  position: fixed;
  inset: 0;
  z-index: 150;
  background: var(--berry);
  animation: curtain-lift 0.85s var(--swing) 0.55s forwards;
}
html.js .curtain__mark {
  display: block;
  width: 74px;
  animation: mark-in 0.7s var(--expo) both, mark-out 0.35s var(--quart) 0.5s forwards;
}
html.js .curtain__mark svg { width: 100%; height: auto; }

@keyframes curtain-lift {
  to { transform: translateY(-101%); visibility: hidden; }
}
@keyframes mark-in  { from { opacity: 0; transform: scale(0.82); } to { opacity: 1; transform: none; } }
@keyframes mark-out { to   { opacity: 0; transform: scale(1.08); } }

/* ── header ───────────────────────────────────────────────────────────────── */

.head {
  position: fixed;
  inset: 0 0 auto;
  z-index: 100;
  transition: transform 0.55s var(--expo), background-color 0.5s var(--quart),
              backdrop-filter 0.5s var(--quart);
}
.head.is-stuck {
  background: rgba(20, 6, 12, 0.82);
  backdrop-filter: blur(14px) saturate(1.2);
}
.head.is-away { transform: translateY(-100%); }

/* Over the light hero the bar carries dark ink; the moment it takes its plum
   fill it flips back to cream. Swapping the functional tokens is the whole
   switch — no child rule knows which state it is in. */
.head:not(.is-stuck) {
  --ink: var(--ink-d);
  --ink-2: var(--ink-d2);
  --ink-3: var(--ink-d3);
  --rule: var(--rule-d);
  color: var(--ink-d);
}

.head__in {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: clamp(1rem, 3vw, 2.5rem);
  max-width: var(--wrap);
  margin-inline: auto;
  padding: 1.35rem var(--gut);
  transition: padding 0.5s var(--quart);
}
.head.is-stuck .head__in { padding-block: 0.95rem; }

.head__logo { display: flex; align-items: center; gap: 0.8rem; justify-self: center; }
.head__logo svg { width: 40px; height: 40px; transition: transform 0.6s var(--expo); }
.head__logo:hover svg { transform: rotate(-12deg) scale(1.06); }

.head__word {
  display: grid;
  font-family: var(--serif);
  font-variation-settings: "opsz" 24, "SOFT" 60, "WONK" 1;
  font-size: 0.9rem;
  font-weight: 600;
  line-height: 1.08;
  letter-spacing: 0.005em;
}
.head__word em {
  font-style: normal;
  font-weight: 300;
  color: var(--ink-2);
  letter-spacing: 0.14em;
  font-size: 0.78em;
  text-transform: uppercase;
  font-family: var(--sans);
}

/* The two side columns are equal thirds, so the gap either side of the emblem is
   whatever each group does not use. Kontakt sits in the left group, not with the
   other information links, for that reason and no other: four short words on the
   left against two words plus the phone pill on the right is what makes the two
   leftovers comparable, and the emblem read as centred. Moving it back to the
   right group puts a 370px hole between "Nabídka" and the emblem.
   Both groups keep the page gutter — see the `--gut` note over `.dock`. */
.head__nav { display: flex; align-items: center; gap: clamp(1rem, 2.2vw, 2.1rem); }
.head__nav a {
  position: relative;
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-2);
  padding-block: 0.4rem;
  transition: color 0.35s var(--quart);
}
.head__nav a::after {
  content: "";
  position: absolute;
  left: 0; bottom: 0;
  width: 100%; height: 1px;
  background: var(--sorbet);
  transform: scaleX(0);
  transform-origin: right;
  transition: transform 0.5s var(--expo);
}
.head__nav a:hover { color: var(--ink); }
.head__nav a:hover::after,
.head__nav a[aria-current="true"]::after { transform: scaleX(1); transform-origin: left; }
.head__nav a[aria-current="true"] { color: var(--ink); }

.head__act { display: flex; align-items: center; justify-content: flex-end; gap: clamp(1rem, 2.2vw, 2.1rem); }

.head__tel {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.7rem 1.15rem;
  border: 1px solid var(--rule);
  border-radius: 999px;
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.14em;
  transition: background-color 0.4s var(--expo), color 0.4s var(--expo),
              border-color 0.4s var(--expo);
}
.head__tel .ico { width: 15px; height: 15px; color: var(--sorbet); transition: color 0.4s var(--expo); }
/* the pill inverts the same way — hairline outline becomes a solid sorbet fill.
   The glyph is explicitly coloured, so it has to be inverted explicitly too. */
.head__tel:hover, .head__tel:focus-visible {
  background: var(--sorbet);
  border-color: var(--sorbet);
  color: var(--plum);
}
.head__tel:hover .ico, .head__tel:focus-visible .ico { color: var(--plum); }

.head__prog {
  display: block;
  height: 1px;
  background: var(--sorbet);
  transform: scaleX(var(--progress));
  transform-origin: left;
}

.burger { display: none; width: 44px; height: 44px; place-items: center; }
.burger__box { display: grid; gap: 6px; width: 22px; }
.burger__box i {
  display: block; height: 1.5px; background: var(--ink);
  transition: transform 0.45s var(--expo), opacity 0.3s var(--quart);
}
.burger[aria-expanded="true"] .burger__box i:first-child { transform: translateY(3.75px) rotate(45deg); }
.burger[aria-expanded="true"] .burger__box i:last-child  { transform: translateY(-3.75px) rotate(-45deg); }

@media (max-width: 1180px) {
  .head__nav { display: none; }
  .burger { display: grid; }
  .head__tel span { display: none; }
  /* icon-only, so it has to carry its own 44px target */
  .head__tel { padding: 0; width: 44px; height: 44px; display: grid; place-items: center; }
  /* with the links gone the emblem has no reason to stay centred */
  .head__in { grid-template-columns: 1fr auto; }
  .head__logo { justify-self: start; }
}

/* ── full-bleed menu ──────────────────────────────────────────────────────── */

.veil {
  position: fixed;
  inset: 0;
  z-index: 99;
  display: grid;
  align-content: center;
  gap: 3rem;
  padding: calc(var(--head-now) + 2rem) var(--gut) 3rem;
  background: var(--berry);
  opacity: 0;
  transition: opacity 0.45s var(--quart);
  overflow-y: auto;
}
.veil[hidden] { display: none; }
.veil.is-open { opacity: 1; }

.veil__nav { display: grid; }
.veil__nav a {
  display: flex;
  align-items: baseline;
  gap: 1.1rem;
  padding: 0.35em 0;
  border-bottom: 1px solid rgba(251, 242, 233, 0.16);
  font-family: var(--serif);
  font-variation-settings: "opsz" 144, "SOFT" 60, "WONK" 1;
  font-size: clamp(2rem, 8vw, 3.6rem);
  font-weight: 400;
  line-height: 1;
  letter-spacing: -0.025em;
  opacity: 0;
  transform: translateY(28px);
}
.veil__nav a i {
  font-family: var(--sans);
  font-style: normal;
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.2em;
  color: rgba(251, 242, 233, 0.55);
}
.veil.is-open .veil__nav a {
  animation: veil-row 0.7s var(--expo) forwards;
  animation-delay: calc(0.08s + var(--row) * 0.055s);
}
@keyframes veil-row { to { opacity: 1; transform: none; } }

.veil__foot {
  display: flex;
  flex-wrap: wrap;
  gap: 1.6rem;
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(251, 242, 233, 0.7);
}
.veil__foot a { border-bottom: 1px solid rgba(251, 242, 233, 0.3); padding-bottom: 2px; }

/* ═══ hero ════════════════════════════════════════════════════════════════
   Reference-fidelity build of the client's supplied screenshot: copy left,
   photograph bleeding the right half, a caps serif lockup, and the label →
   rule → ring CTA. Measured geometry and the deliberate departures are in
   DESIGN.md §9. This is the one chapter that opens light. */

.hero {
  position: relative;
  display: grid;
  grid-template-columns: 52% 48%;
  align-items: stretch;
  min-height: 100svh;
  background: var(--shell);
  /* the hero is a light chapter — swap the functional ink wholesale */
  --ink: var(--ink-d);
  --ink-2: var(--ink-d2);
  --ink-3: var(--ink-d3);
  --rule: var(--rule-d);
  --surface: rgba(27, 8, 16, 0.035);
  color: var(--ink-d);
  overflow: hidden;
}
.hero ::selection { background: var(--berry); color: var(--cream); }

/* The reference indents its copy column to ~8.4% of the viewport — further in
   than this site's usual gutter, and worth matching because that indent is
   what gives the lockup its air. */
.hero__copy {
  display: flex;
  align-items: stretch;
  padding: calc(var(--head-now) + clamp(1.5rem, 4vh, 3rem)) clamp(1.5rem, 4vw, 3.5rem)
           clamp(1.75rem, 4vh, 3.5rem) clamp(1.25rem, 8vw, 9rem);
}
.hero__stack {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: start;
  width: 100%;
}

.hero__eyebrow { margin-bottom: clamp(1.25rem, 3.5vh, 2.5rem); }

/* ── the caps lockup ────────────────────────────────────────────────────────
   The reference sets a high-contrast serif in caps, line two larger than line
   one. Fraunces only reads that way with the soft and wonk axes off — left on,
   it goes warm and bouncy and stops matching the reference. */

.hero__title {
  font-family: var(--serif);
  font-size: var(--t-shout);
  font-weight: 400;
  font-variation-settings: "opsz" 144, "SOFT" 0, "WONK" 0;
  line-height: 0.94;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--ink);
}
.hero__line { display: block; }
.hero__line--big { font-size: 1.34em; letter-spacing: 0.005em; }

/* overflow:hidden would eat the acute on í and the descender on y — the
   negative margin gives the mask that room back without moving the baseline */
.msk { display: block; overflow: hidden; padding: 0.14em 0 0.2em; margin: -0.14em 0 -0.2em; }
.msk__i { display: block; }

html.js .msk__i { transform: translateY(112%); }
html.js .hero__line .msk__i     { animation: rise 1.05s var(--expo) 0.95s forwards; }
html.js .hero__line--big .msk__i { animation: rise 1.05s var(--expo) 1.05s forwards; }
@keyframes rise { to { transform: none; } }

.hero__sub {
  max-width: 40ch;   /* three lines, as in the reference — four made the hero overflow */
  margin-top: clamp(1.5rem, 4vh, 2.75rem);
  font-size: var(--t-lede);
  font-weight: 300;
  line-height: 1.62;
  color: var(--ink-2);
  text-wrap: pretty;
}

.hero .circle-cta { margin-top: clamp(1.75rem, 4.5vh, 3.25rem); }

.hero__socials { display: flex; gap: 0.55rem; margin-top: clamp(1.5rem, 4vh, 2.75rem); margin-left: -0.6rem; }

/* the reference parks its social row in the bottom-left corner, detached from
   the lockup — only once there is a full-height column to park it in */
@media (min-width: 981px) {
  .hero__stack { padding-bottom: 3.5rem; }
  .hero__socials { position: absolute; left: -0.6rem; bottom: 0; margin-top: 0; }
}
.hero__socials a {
  display: grid;
  place-items: center;
  width: 40px; height: 40px;
  border-radius: 50%;
  color: var(--ink-2);
  transition: color 0.35s var(--quart), background-color 0.35s var(--quart);
}
.hero__socials svg { width: 19px; height: 19px; }
.hero__socials a:hover { color: var(--ink); background: rgba(27, 8, 16, 0.06); }

/* ── the cake ──────────────────────────────────────────────────────────────
   A free object, not a photograph in a panel. The image is a cut-out with a
   transparent background, so the page's own ground runs behind it and there is
   no frame edge anywhere — which is the whole point: a rectangular photo panel
   against the copy column reads as a divider line however closely the two
   whites are matched.

   Consequences of that, all deliberate:
   - no `overflow: hidden` and no `object-fit: cover`. Nothing is cropped; the
     object is laid out at its own aspect and centred in its half.
   - the shadow is `drop-shadow`, not `box-shadow`. It follows the alpha, so it
     hugs the cake instead of drawing the box we just removed.
   - the reveal is opacity and a small rise. The old clip-path wipe would have
     cut a hard rectangular edge across a shape that no longer has one, and it
     would have clipped the shadow with it. */

/* The cut-out is landscape (1.16) inside a portrait half, so width binds it.
   The right inset is `--gut`, the same edge the nav and the rows keep, so the
   object sits inside the page's margins rather than being pinned to the glass. */
.hero__shot {
  position: relative;
  display: grid;
  place-items: center;
  margin: 0;
  padding-right: var(--gut);
}
.hero__shot img {
  width: 100%;
  height: auto;
  max-height: 74svh;
  object-fit: contain;
  filter: drop-shadow(0 30px 34px rgba(27, 8, 16, 0.14))
          drop-shadow(0 6px 10px rgba(27, 8, 16, 0.07));
}
html.js .hero__shot {
  opacity: 0;
  animation: shot-in 1.1s var(--expo) 0.75s forwards;
}
@keyframes shot-in {
  from { opacity: 0; transform: translateY(2.2rem) scale(0.97); }
  to   { opacity: 1; transform: none; }
}

.status {
  display: grid;
  gap: 0.4rem;
  margin-top: clamp(1.25rem, 3vh, 2.25rem);
  padding: 0.85rem 1.2rem;
  width: fit-content;
  max-width: 100%;
  border: 1px solid var(--rule);
  border-radius: 3px;
  background: var(--surface);
  backdrop-filter: blur(6px);
}
.status__row {
  display: grid;
  grid-template-columns: auto minmax(0, 10rem) minmax(0, 1fr);
  align-items: baseline;
  gap: 0.75rem;
  font-size: var(--t-small);
}
.status__label {
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.status__text { color: var(--ink); }

.dot {
  position: relative;
  align-self: center;
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--ink-3);
}
.dot[data-state="open"]   { background: var(--leaf); }
.dot[data-state="soon"]   { background: var(--sorbet); }
.dot[data-state="closed"] { background: var(--berry-lift); }
.dot[data-state="open"]::after,
.dot[data-state="soon"]::after {
  content: "";
  position: absolute; inset: -4px;
  border-radius: 50%;
  border: 1px solid transparent;
  animation: halo 2.4s var(--quart) infinite;
}
.dot[data-state="open"]::after { border-color: var(--leaf); }
.dot[data-state="soon"]::after { border-color: var(--sorbet); }
@keyframes halo {
  0%   { transform: scale(0.7); opacity: 0.9; }
  70%  { transform: scale(1.9); opacity: 0; }
  100% { transform: scale(1.9); opacity: 0; }
}

/* Below the split, the two halves stack: copy first, photograph under it as a
   wide band. The reference has no stacked state to copy, so the crop changes
   from a tall panel to a landscape band rather than squeezing the same frame. */
@media (max-width: 980px) {
  .hero {
    grid-template-columns: 1fr;
    align-content: start;
    min-height: 0;
  }
  .hero__copy {
    padding: calc(var(--head-now) + 1.5rem) var(--gut) clamp(1.75rem, 4vh, 2.5rem);
  }
  /* tighter than the split layout — every rem saved here is a rem of the
     photograph that clears the fold on a phone */
  .hero__eyebrow    { margin-bottom: 1rem; }
  .hero__sub        { margin-top: 1.1rem; }
  .hero .circle-cta { margin-top: 1.5rem; }
  .status           { margin-top: 1.25rem; }
  .hero__socials    { margin-top: 1.25rem; }
  /* stacked: the cake keeps its own aspect rather than being cropped into a
     band, so it is bounded by height instead and stays a free object */
  .hero__shot { order: 2; padding: 0 var(--gut) clamp(1rem, 3vh, 2rem); }
  .hero__shot img { max-height: min(42svh, 340px); width: auto; max-width: 100%; }
}

@media (max-width: 560px) {
  .status__row { grid-template-columns: auto 1fr; row-gap: 0.15rem; }
  .status__label { grid-column: 2; }
  .status__text  { grid-column: 2; }
  .circle-cta__rule { width: 2.25rem; }
}

/* ═══ ticker ══════════════════════════════════════════════════════════════ */

.tick {
  overflow: hidden;
  background: var(--berry);
  border-block: 1px solid rgba(20, 6, 12, 0.25);
  padding-block: 1.15rem;
}
.tick__row { display: flex; width: max-content; animation: roll 42s linear infinite; }
.tick__set { display: flex; align-items: center; }
.tick__set span {
  font-family: var(--serif);
  font-variation-settings: "opsz" 60, "SOFT" 80, "WONK" 1;
  font-size: clamp(1.3rem, 2.6vw, 2.1rem);
  font-weight: 400;
  letter-spacing: -0.01em;
  white-space: nowrap;
  color: var(--cream);
}
.tick__set i {
  display: block;
  width: 7px; height: 7px;
  margin-inline: clamp(1.4rem, 3.5vw, 3rem);
  background: var(--sorbet);
  transform: rotate(45deg);
}
@keyframes roll { to { transform: translate3d(-50%, 0, 0); } }

/* ═══ chapters ════════════════════════════════════════════════════════════ */

.row { position: relative; padding-block: var(--bay); }

/* The two cream chapters. Swapping the functional tokens is the whole switch —
   nothing below needs to know which ground it is sitting on. */
.light {
  background: var(--cream-warm);
  --ink: var(--ink-d);
  --ink-2: var(--ink-d2);
  --ink-3: var(--ink-d3);
  --rule: var(--rule-d);
  --surface: rgba(27, 8, 16, 0.035);
  color: var(--ink-d);
}
.light ::selection { background: var(--berry); color: var(--cream); }

/* ── the lit statement ────────────────────────────────────────────────────
   Set at reading size and lit word by word as it crosses the middle of the
   screen. Without JS it is simply a paragraph at full contrast. */

.lit {
  max-width: 22ch;
  margin: 0 auto clamp(5rem, 11vw, 9rem);
  font-family: var(--serif);
  font-variation-settings: "opsz" 96, "SOFT" 50, "WONK" 1;
  font-size: clamp(1.9rem, 4.4vw, 3.6rem);
  font-weight: 300;
  line-height: 1.16;
  letter-spacing: -0.02em;
  text-align: center;
  text-wrap: balance;
}
@media (min-width: 900px) { .lit { max-width: 26ch; } }

.lit__w { transition: color 0.5s var(--quart); }
html.js .lit__w { color: var(--ink-3); }
html.js .lit__w.is-lit { color: var(--ink); }

/* ── four-up ──────────────────────────────────────────────────────────────── */

.four {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: clamp(1.5rem, 3vw, 2.6rem);
}
.four__item { display: grid; gap: 1.1rem; align-content: start; }

/* every second card drops half a step — the row reads as a rhythm, not a rack */
.four__item:nth-child(even) { margin-top: clamp(1.5rem, 4vw, 3.5rem); }

.four__shot {
  display: block;
  position: relative;
  aspect-ratio: 4 / 5;
  overflow: hidden;
  border-radius: 999px 999px 4px 4px;
  background: var(--plum-up);
}
.four__shot img {
  width: 100%; height: 100%;
  object-fit: cover;
  transition: transform 1s var(--expo);
}
.four__item:hover .four__shot img { transform: scale(1.06); }

.four__title {
  display: flex;
  align-items: baseline;
  gap: 0.7rem;
  font-family: var(--serif);
  font-variation-settings: "opsz" 48, "SOFT" 40, "WONK" 1;
  font-size: clamp(1.2rem, 1.9vw, 1.6rem);
  font-weight: 500;
  line-height: 1.1;
  letter-spacing: -0.015em;
}
.four__no {
  font-family: var(--sans);
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.16em;
  color: var(--sorbet);
}
.four__item p { font-size: 0.9375rem; color: var(--ink-2); text-wrap: pretty; }

@media (max-width: 900px) { .four { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 480px) {
  .four { grid-template-columns: 1fr; }
  .four__item:nth-child(even) { margin-top: 0; }
}

/* ═══ objednávka ══════════════════════════════════════════════════════════ */

.order {
  display: grid;
  grid-template-columns: minmax(0, 0.92fr) minmax(0, 1fr);
  gap: clamp(2.5rem, 7vw, 7rem);
  align-items: start;
}
.order__sticky { position: sticky; top: calc(var(--head-now) + 3rem); display: grid; gap: 2rem; }
.order__sticky .eyebrow { margin-bottom: -0.9rem; }

.order__shot {
  aspect-ratio: 5 / 6;
  overflow: hidden;
  border-radius: 4px;
  background: var(--berry);
}
.order__shot img { width: 100%; height: 100%; object-fit: cover; }

.order__note { max-width: 34ch; color: var(--ink-2); font-size: 0.9375rem; }
.order__cta { display: flex; }

.steps { display: grid; padding-top: clamp(1rem, 4vw, 4rem); }
.step {
  position: relative;
  display: grid;
  gap: 0.75rem;
  padding: clamp(1.8rem, 3vw, 2.6rem) 0 clamp(1.8rem, 3vw, 2.6rem) clamp(3.5rem, 7vw, 6rem);
  border-top: 1px solid var(--rule);
}
.step:last-child { border-bottom: 1px solid var(--rule); }
.step__no {
  position: absolute;
  left: 0;
  top: clamp(1.8rem, 3vw, 2.6rem);
  font-family: var(--serif);
  font-variation-settings: "opsz" 96, "SOFT" 20, "WONK" 0;
  font-size: clamp(1.6rem, 3vw, 2.4rem);
  font-weight: 300;
  line-height: 1;
  color: var(--berry);
}
.step__name {
  font-family: var(--serif);
  font-variation-settings: "opsz" 72, "SOFT" 50, "WONK" 1;
  font-size: clamp(1.5rem, 2.8vw, 2.2rem);
  font-weight: 400;
  line-height: 1.05;
  letter-spacing: -0.02em;
}
.step p { max-width: 46ch; color: var(--ink-2); text-wrap: pretty; }

/* the closing row of the list is the call to action, not a fourth step */
.step--end { padding-left: 0; gap: 1.5rem; justify-items: start; border-bottom: 0; }

@media (max-width: 900px) {
  .order { grid-template-columns: 1fr; }
  .order__sticky { position: static; }
  .order__shot { aspect-ratio: 4 / 3; }
}

/* ═══ nabídka · sideways row ══════════════════════════════════════════════ */
/* Phones get the motion, desktop does not. Below 900px the row travels sideways
   as the page scrolls down; from 900px up it is a plain three-up row that never
   moves — see the media query at the foot of this block.

   The pinned mode is opt-in from JS and never the default: a sticky box with
   overflow hidden and nothing driving it would clip cards 2 and 3 into
   unreachable space. See the matching note in js/main.js. */

.hscroll { position: relative; }
.hscroll__pin { overflow: visible; }

.hscroll--on .hscroll__pin {
  position: sticky;
  top: 0;
  height: 100svh;
  display: flex;
  align-items: center;
  overflow: hidden;
}

.trio {
  display: flex;
  gap: clamp(1.25rem, 3vw, 2.5rem);
  padding-inline: var(--gut);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  will-change: transform;
}
.trio::-webkit-scrollbar { display: none; }
.hscroll--on .trio { overflow: visible; }

.trio__item {
  position: relative;
  flex: 0 0 min(78vw, 460px);
  scroll-snap-align: center;
  margin: 0;
  overflow: hidden;
  border-radius: 4px;
  background: var(--plum-up);
}
.trio__shot { display: block; aspect-ratio: 4 / 5; overflow: hidden; }
.trio__shot img {
  width: 100%; height: 100%;
  object-fit: cover;
  transition: transform 1.1s var(--expo);
}
.trio__item:hover .trio__shot img { transform: scale(1.05); }

.trio__item figcaption {
  position: absolute;
  inset: auto 0 0;
  display: grid;
  gap: 0.35rem;
  padding: clamp(1.4rem, 3vw, 2.2rem);
  background: linear-gradient(transparent, rgba(20, 6, 12, 0.92) 62%);
}
.trio__no {
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.2em;
  color: var(--sorbet);
}
.trio__name {
  font-family: var(--serif);
  font-variation-settings: "opsz" 72, "SOFT" 50, "WONK" 1;
  font-size: clamp(1.5rem, 2.4vw, 2rem);
  font-weight: 400;
  line-height: 1.05;
  letter-spacing: -0.02em;
  color: var(--cream);
}
.trio__desc { font-size: 0.9375rem; color: rgba(251, 242, 233, 0.72); }

/* From 900px up the section stops being a scroller of any kind: three cards,
   side by side, inside the page's own container. Nothing here is gated on
   .hscroll--on because JS never arms the pin at this width — the two agree on
   900 and must keep agreeing; the matching number is in js/main.js. */
@media (min-width: 900px) {
  .trio {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    max-width: var(--wrap);
    margin-inline: auto;
    overflow: visible;
    scroll-snap-type: none;
    will-change: auto;
  }
  .trio__item { flex: none; }
}

/* ═══ vitrína ═════════════════════════════════════════════════════════════ */

/* The mosaic is the one block on the page that ignores --wrap and --gut. It is
   a direct child of .row--gallery, so it is already the full width of the page;
   nothing here needs a 100vw escape hatch, and so nothing here can overflow by
   the width of a scrollbar. The header and the CTA are still in .wrap.

   Three values drive it:
     --vgap   the hairline between photographs. Small on purpose — the tiles are
              meant to read as one surface with the plum ground showing through
              at the seams, not as ten cards on a page.
     --vrow   the row unit, tracked to one twelfth of the viewport so a tile's
              column span and row span read directly as its aspect ratio: 5 cols
              by 4 rows is 5:4. It stops growing at 190px, past which the 1200px
              source photographs would be upscaled.
     --vroom  the vertical slack the photograph has inside its frame, which is
              what the drift moves through. Zero below the breakpoint. */

.grid {
  --vgap: clamp(4px, 0.5vw, 9px);
  --vrow: clamp(60px, 8.15vw, 190px);
  --vroom: 0px;
  display: grid;
  gap: var(--vgap);
}

.tile {
  position: relative;
  overflow: hidden;
  background: var(--plum-up);
}
/* The photograph is taller than its frame by --vroom at each edge and sits
   pulled up by that much, so drift has somewhere to travel without ever
   uncovering the tile behind it. --dk is written by drifters() in main.js as a
   fraction, −1…1, so the travel limit lives here in CSS and only here. */
.tile img {
  position: absolute;
  top: calc(-1 * var(--vroom));
  left: 0;
  width: 100%;
  height: calc(100% + 2 * var(--vroom));
  object-fit: cover;
  object-position: var(--pos, 50% 50%);
  translate: 0 calc(var(--dk, 0) * var(--vroom));
  scale: var(--zoom, 1);
  transition: scale 1.2s var(--expo);
}
.tile::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(160deg, rgba(142, 27, 78, 0.5), rgba(20, 6, 12, 0.25));
  opacity: 0;
  transition: opacity 0.6s var(--quart);
}
.tile:hover img { --zoom: 1.05; }
.tile:hover::after { opacity: 1; }

/* Crops that need help. Every source is square except the green cake, so the
   wide slots throw away the top and bottom of the frame — these three name the
   part of the photograph that has to survive that. */
.tile--d { --pos: 50% 62%; }   /* croissants sit low, under a lot of empty air */
.tile--f { --pos: 55% 50%; }   /* the nanuk leans right of centre */
.tile--i { --pos: 50% 56%; }

/* Two honest columns until there is room for the real composition — still full
   bleed, still a hairline apart. The rhythm is: wide, pair, wide, pair, pair,
   pair, which keeps the two panoramic shots doing the work they were picked for. */
@media (max-width: 900px) {
  .grid { grid-template-columns: repeat(2, 1fr); }
  .tile { aspect-ratio: 3 / 4; }
  .tile--a { grid-column: span 2; aspect-ratio: 3 / 2; }
  .tile--d { grid-column: span 2; aspect-ratio: 2 / 1; }
}

/* On a tablet the columns are wide enough that portrait tiles would make the
   vitrína three screens long. Square them up; the rhythm is unchanged. */
@media (min-width: 561px) and (max-width: 900px) {
  .tile { aspect-ratio: 1; }
  .tile--a { aspect-ratio: 2 / 1; }
  .tile--d { aspect-ratio: 5 / 2; }
}

/* The composition. Four bands of differing height, each one filled edge to edge
   so no tile has a ragged bottom — spans are given as explicit row lines rather
   than aspect ratios for exactly that reason. Read the numbers as ratios:
   .tile--a is 5 columns by 4 rows, so 5:4. */
@media (min-width: 901px) {
  .grid {
    --vroom: clamp(14px, 1.9vw, 30px);
    grid-template-columns: repeat(12, 1fr);
    grid-auto-rows: var(--vrow);
  }

  .tile--a { grid-column: 1 / 6;   grid-row: 1 / 5;   }  /* 5:4  */
  .tile--b { grid-column: 6 / 10;  grid-row: 1 / 5;   }  /* 1:1  */
  .tile--c { grid-column: 10 / 13; grid-row: 1 / 5;   }  /* 3:4  */
  .tile--d { grid-column: 1 / 8;   grid-row: 5 / 9;   }  /* 7:4  */
  .tile--e { grid-column: 8 / 13;  grid-row: 5 / 9;   }  /* 5:4  */
  .tile--f { grid-column: 1 / 4;   grid-row: 9 / 14;  }  /* 3:5  */
  .tile--g { grid-column: 4 / 9;   grid-row: 9 / 14;  }  /* 1:1  */
  .tile--h { grid-column: 9 / 13;  grid-row: 9 / 14;  }  /* 4:5  */
  .tile--i { grid-column: 1 / 5;   grid-row: 14 / 17; }  /* 4:3  */
  .tile--j { grid-column: 5 / 13;  grid-row: 14 / 17; }  /* 8:3  */

  /* only hinted where the drift actually runs */
  .tile img { will-change: translate; }
}

/* ═══ otevírací doba ══════════════════════════════════════════════════════ */

/* The boards take slightly more of the row than they used to: the double rule
   costs each card about 30px of chrome, and that came off the day names. */
.hours {
  display: grid;
  grid-template-columns: minmax(0, 0.88fr) minmax(0, 1.12fr);
  gap: clamp(2.5rem, 5vw, 5rem);
  align-items: start;
}
.hours__copy { display: grid; gap: 1.6rem; }
.hours__copy .eyebrow { margin-bottom: -0.6rem; }

.hours__key {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  font-size: var(--t-small);
  color: var(--ink-3);
}
.hours__keydot { width: 18px; height: 3px; background: var(--berry); }

/* One card per schedule. They sit side by side only where a day name, a leader
   and a full time all still fit on one line — which is not one breakpoint but
   two bands, because this column is at its narrowest exactly while the chapter
   is in its two-column layout:

     < 640    stacked, one card per row, full width
     640–900  two-up — the chapter is single-column here, so the row is wide
     900–1340 stacked again — the copy column has taken half the row
     > 1340   two-up

   The upper figure is measured, not chosen: below it the leader is squeezed to
   nothing and the row reads as a bug rather than as a dotted line. */
.hours__boards { display: grid; gap: clamp(1.25rem, 2.5vw, 2rem); }
@media (min-width: 640px) and (max-width: 900px) {
  .hours__boards { grid-template-columns: 1fr 1fr; }
}
@media (min-width: 1340px) { .hours__boards { grid-template-columns: 1fr 1fr; } }

/* The double rule is two elements rather than one border plus an outline: the
   outer box carries the warm hairline, the inner one the neutral rule and the
   fill, and the gap between them is real padding, so it scales with the card. */
.board {
  padding: clamp(0.5rem, 1.1vw, 0.8rem);
  border: 1px solid var(--rule-warm);
  border-radius: 5px;
}
.board__frame {
  padding: clamp(1.2rem, 2.4vw, 1.8rem);
  border: 1px solid var(--rule);
  border-radius: 2px;
  background: rgba(251, 242, 233, 0.55);
}
.board--alt { border-color: var(--berry); }
.board--alt .board__frame {
  background: var(--berry);
  color: var(--cream);
  border-color: rgba(251, 242, 233, 0.22);
}

.board__cap {
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-3);
  padding-bottom: 1rem;
  margin-bottom: 0.4rem;
  border-bottom: 1px solid var(--rule);
}
.board--alt .board__cap { color: rgba(251, 242, 233, 0.74); border-color: rgba(251, 242, 233, 0.22); }

.board__row {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: baseline;
  gap: 0.7rem;
  padding-block: 0.62rem;
  font-size: 0.9375rem;
}
/* The days are the one place on the site where Fraunces is set small and in
   italic — a printed-card voice for the part of the page that is a list of
   facts. The italic is a pinned instance; see the font hrefs in index.html. */
.board__day {
  font-family: var(--serif);
  font-style: italic;
  font-size: 1.0625rem;
  font-variation-settings: "opsz" 14, "SOFT" 20, "WONK" 1;
  color: var(--ink-2);
}
.board--alt .board__day { color: rgba(251, 242, 233, 0.78); }
.board__dots {
  height: 1px;
  transform: translateY(-3px);
  background-image: radial-gradient(currentColor 1px, transparent 1.2px);
  background-size: 5px 1px;
  background-repeat: repeat-x;
  opacity: 0.3;
}
.board__time {
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
}

.board__row.is-shut .board__time { color: var(--ink-3); }
/* "Zavřeno" on the berry card. It was 0.5, which measured 3.01:1 and failed AA
   — the recessed look was coming out of the contrast budget. 0.7 measures
   4.56:1; the row still reads as the quiet one because the whole row is quiet,
   not because this word is faint. */
.board--alt .board__row.is-shut .board__time { color: rgba(251, 242, 233, 0.7); }

.board__row.is-today {
  position: relative;
  font-weight: 500;
  color: var(--berry);
}
.board__row.is-today .board__day { color: inherit; }
.board__row.is-today::before {
  content: "";
  position: absolute;
  left: calc(-1 * clamp(0.6rem, 1.3vw, 0.95rem)); top: 50%;
  width: 3px; height: 1.1em;
  transform: translateY(-50%);
  background: var(--berry);
}
.board--alt .board__row.is-today { color: var(--cream); }
.board--alt .board__row.is-today::before { background: var(--sorbet); }

@media (max-width: 900px) { .hours { grid-template-columns: 1fr; } }

/* ═══ čísla ═══════════════════════════════════════════════════════════════ */

.count {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: clamp(2rem, 4vw, 3rem);
  border-top: 1px solid var(--rule);
}
.count__item {
  display: grid;
  gap: 0.4rem;
  padding-top: clamp(1.6rem, 3vw, 2.4rem);
  padding-right: clamp(1rem, 2vw, 2rem);
  border-right: 1px solid var(--rule);
}
.count__item:last-child { border-right: 0; }

.count__num {
  font-family: var(--serif);
  font-variation-settings: "opsz" 144, "SOFT" 30, "WONK" 0;
  font-size: clamp(3.75rem, 9.5vw, 9.5rem);
  font-weight: 200;
  line-height: 0.86;
  letter-spacing: -0.05em;
  font-variant-numeric: tabular-nums lining-nums;
}
.count__cap {
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--sorbet);
  margin-top: 0.7rem;
}
.count__item p { font-size: var(--t-small); color: var(--ink-2); }
.count__item a {
  border-bottom: 1px solid var(--rule);
  padding-bottom: 1px;
  transition: border-color 0.4s var(--quart), color 0.4s var(--quart);
}
.count__item a:hover { border-color: var(--sorbet); color: var(--ink); }
.stars { color: var(--sorbet); letter-spacing: 0.12em; }

@media (max-width: 900px) {
  .count { grid-template-columns: repeat(2, 1fr); }
  .count__item:nth-child(2) { border-right: 0; }
}
@media (max-width: 460px) {
  .count { grid-template-columns: 1fr; }
  .count__item { border-right: 0; border-bottom: 1px solid var(--rule); padding-bottom: 1.6rem; }
  .count__item:last-child { border-bottom: 0; }
}

/* ═══ band ════════════════════════════════════════════════════════════════ */

/* The photograph is the section's own background rather than a child layer,
   because the parallax is driven by `background-attachment: fixed` and there is
   no other way to get it. Fixed attachment also means `cover` sizes against the
   viewport instead of against this box, which is what gives the crop its scale
   — a translated child element would move the same way and frame differently.

   The height ceiling matters. Fixed + cover guarantees the image is at least a
   viewport tall, and the JS offsets it by 0.4 of this box's distance from the
   viewport top; the arithmetic only stays gap-free while the section is shorter
   than the viewport. Do not raise 78vh without redoing it.

   Below 1025px there is no parallax at all: iOS has never supported fixed
   attachment properly, and forcing it there costs a full-screen repaint per
   frame to look worse. The fallback is a plain top-anchored cover. */
.band {
  position: relative;
  display: grid;
  place-items: center;
  min-height: clamp(28rem, 78vh, 44rem);
  overflow: hidden;
  text-align: center;
  background: var(--plum) url("../img/pas-dort-cokoladovy.jpg") center top / cover no-repeat scroll;
}
@media (min-width: 1025px) {
  html.js .band { background-attachment: fixed; background-position: 50% center; }
}
/* Enough scrim that a hairline script survives the cream icing and the lit
   magenta backdrop, and no more — the photograph still has to read as one. At
   these stops the palest highlight in the frame sits above 5:1 against cream. */
.band::before {
  content: "";
  position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(rgba(20, 6, 12, 0.58), rgba(20, 6, 12, 0.78));
}
.band__in {
  position: relative;
  z-index: 2;
  display: grid;
  justify-items: center;
  gap: clamp(1.5rem, 4vw, 3rem);
  width: 100%;
  padding: 4rem 0;
}
.eyebrow--light { color: rgba(251, 242, 233, 0.6); }
.title--light { color: var(--cream); }
.title--light em { color: var(--sorbet); }

/* ── the lettering ──────────────────────────────────────────────────────────
   Two lines that do not share an axis: the first ends left of centre, the
   second starts right of it, and they overlap vertically. That offset is the
   composition — centre them both and it reads as an ordinary stacked heading.

   Each line is [swash][word] or [word][swash] in a flex row, and the swash
   takes the remainder. So the line's horizontal position is set by one padding
   on its closed side, and the hairline always runs to the screen edge whatever
   the viewport is.

   Script faces sit small for their font-size and their real weight lives in the
   ascenders, so this runs well above --t-title and on a tight line-height. */
.band__script {
  font-family: var(--script);
  font-weight: 400;
  font-size: clamp(2.3rem, 7.4vw, 7rem);
  line-height: 1;
  letter-spacing: 0;
  color: var(--cream);
  width: 100%;
  margin: 0;
  display: grid;
  justify-items: stretch;
}
.band__line {
  display: flex;
  align-items: center;
  min-width: 0;
}
/* The one pair of numbers that sets the whole composition. Each line is padded
   on its closed side, so this is where its text starts and ends; the swash takes
   whatever is left. They are not symmetrical because the lines are not the same
   length — the second is half as long again as the first — and what has to come
   out even is the picture, not the padding. At the top of the clamp the words
   land at roughly 17–46% and 41–82%, which is the overlap the reference has. */
.band__line--a { padding-right: 54%; }
.band__line--b { padding-left: 41%; margin-top: -0.34em; }

.band__word {
  flex: 0 0 auto;
  white-space: nowrap;
  /* Allura's descenders and the caron on ě both run past the em box; without
     the padding the flex row clips them against the swash. */
  padding: 0.12em 0.06em 0.24em;
}

/* The swash carries the line out to the edge. It is stretched, not scaled, so
   the curve flattens as the gap widens while the stroke stays one hairline. */
.band__swash {
  flex: 1 1 auto;
  min-width: 0;
  height: 0.34em;
  align-self: center;
  /* The swash meets the letters near the baseline, not at the optical centre —
     centred it reads as a strike-through running into the word. The negative
     inline margin closes it back over the word's side bearing, so the stroke
     arrives at the letterform instead of stopping a hair short of it. */
  margin-top: 0.2em;
  margin-inline: -0.05em;
  fill: none;
  stroke: var(--cream);
  stroke-width: 2;
  stroke-linecap: round;
}

/* Under this the offsets stop being a composition and start being a reason the
   long line does not fit. Both lines centre, and the swashes go with them —
   a 40px hairline either side is a smudge, not a flourish. */
@media (max-width: 720px) {
  .band__line--a { padding-right: 0; }
  .band__line--b { padding-left: 0; margin-top: -0.22em; }
  .band__line { justify-content: center; }
  .band__swash { display: none; }
}

/* ═══ kontakt ═════════════════════════════════════════════════════════════
   The closing chapter. It stays on plum rather than breaking to cream, so the
   Google map — which is bright and cannot be themed — is the one lit object in
   the chapter, and the section runs straight into the plum footer as a single
   closing block instead of a cream sandwich between two dark ones.

   Detail left, map right, and the map takes the larger share because it is the
   thing that has to be legible at a glance. */

.kontakt {
  display: grid;
  grid-template-columns: minmax(0, 0.92fr) minmax(0, 1.08fr);
  gap: clamp(2.5rem, 5vw, 5rem);
  align-items: stretch;
}

.kontakt__copy { display: grid; gap: 1.6rem; align-content: start; }
.kontakt__copy .eyebrow { margin-bottom: -0.6rem; }
.kontakt__list { margin-top: 0.6rem; border-bottom: 1px solid var(--rule); }

/* Label column left, the link across the rest. The label is a category and the
   value is the action, so only the value is inside the anchor — but the whole
   row lights up, which is what makes the pair read as one line. */
.kontakt__row {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 8.5rem) minmax(0, 1fr);
  align-items: center;
  gap: 1rem;
  border-top: 1px solid var(--rule);
}
/* The same gesture as the nav underline: a sorbet hairline drawn in from the
   left over the rule that is already there. It sits on the row's own top edge,
   so nothing moves and no layout is disturbed. */
.kontakt__row::after {
  content: "";
  position: absolute;
  top: -1px; left: 0; right: 0;
  height: 1px;
  background: var(--sorbet);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.55s var(--expo);
}
.kontakt__row:hover::after,
.kontakt__row:focus-within::after { transform: scaleX(1); }

.kontakt__lab {
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.kontakt__val { min-width: 0; }

.kontakt__link {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 0.95rem;
  padding: 1.15rem 0;   /* this is what gives the row its height and its target */
}
.kontakt__ico,
.kontakt__go {
  width: 19px; height: 19px;
  flex: 0 0 auto;
  color: var(--ink-3);
  transition: color 0.4s var(--quart), transform 0.5s var(--expo);
}
.kontakt__go { width: 17px; height: 17px; }
.kontakt__link:hover .kontakt__ico,
.kontakt__link:focus-visible .kontakt__ico,
.kontakt__link:hover .kontakt__go,
.kontakt__link:focus-visible .kontakt__go { color: var(--sorbet); }
.kontakt__link:hover .kontakt__go,
.kontakt__link:focus-visible .kontakt__go { transform: translateX(4px); }

.kontakt__text {
  display: block;
  font-size: 1.125rem;
  font-weight: 400;
  line-height: 1.3;
  color: var(--ink);
}
/* the one number on the page that is read out loud down a phone */
.kontakt__text--num {
  font-size: 1.3125rem;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
}
.kontakt__sub {
  display: block;
  margin-top: 0.2rem;
  font-size: var(--t-small);
  color: var(--ink-3);
}

.kontakt__cta { display: flex; flex-wrap: wrap; gap: 0.8rem; margin-top: 0.4rem; }

/* ── the map card ─────────────────────────────────────────────────────────
   Two boxes on purpose: .kontakt__frame clips itself to --r-panel, and the
   flag has to straddle that corner, so the flag is a sibling outside it. */

.kontakt__map { position: relative; display: flex; }

.kontakt__frame {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--rule);
  border-radius: var(--r-panel);
  overflow: hidden;
  background: var(--plum-up);
}

/* --plum-up rather than nothing: the frame is dark before the lazy iframe
   arrives, so the card never flashes a white rectangle onto the plum. */
.kontakt__view {
  position: relative;
  flex: 1 1 auto;
  min-height: clamp(19rem, 46vh, 32rem);
  background: var(--plum-up);
}
.kontakt__iframe {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%; height: 100%;
  border: 0;
}

/* Berry, because this is the brand putting its name on the place — it is the
   same field as the ticker and the takeaway board. Cream on berry is 8.9:1. */
.kontakt__flag {
  position: absolute;
  z-index: 3;
  top: 0; left: 0;
  transform: translate(clamp(0.75rem, 2vw, 1.75rem), -50%);
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.6rem 1.05rem 0.6rem 0.85rem;
  border-radius: var(--r-btn);
  background: var(--berry);
  color: var(--cream);
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  white-space: nowrap;
}
.kontakt__flag svg { width: 15px; height: 15px; }

.kontakt__cap {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.4rem 1.2rem;
  padding: 0.75rem clamp(1rem, 2vw, 1.5rem);
  border-top: 1px solid var(--rule);
  font-size: var(--t-small);
  color: var(--ink-2);
}
.kontakt__capname { display: inline-flex; align-items: center; gap: 0.5rem; }
/* A marker on a map, in the same role as the today marker on the hours board */
.kontakt__capname svg { width: 15px; height: 15px; flex: 0 0 auto; color: var(--sorbet); }
.kontakt__cap a {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding-block: 0.35rem;   /* clears the 24px target at 13px type */
  font-weight: 500;
  color: var(--ink);
  transition: color 0.4s var(--quart);
}
.kontakt__cap a svg { width: 15px; height: 15px; transition: transform 0.5s var(--expo); }
.kontakt__cap a:hover, .kontakt__cap a:focus-visible { color: var(--sorbet); }
.kontakt__cap a:hover svg, .kontakt__cap a:focus-visible svg { transform: translateX(4px); }

/* ── the shield ───────────────────────────────────────────────────────────
   A live Maps frame eats the wheel to zoom and eats a drag to pan, which on a
   phone means the page stops scrolling as soon as a thumb lands on it. So the
   frame starts behind a transparent layer and the first click hands it over,
   for good. Gated on html.js: with the script off there is no shield and the
   map is interactive straight away, because nothing would ever remove it. */

.kontakt__shield { display: none; }

html.js .kontakt__shield {
  display: grid;
  place-items: center;
  position: absolute;
  inset: 0;
  z-index: 2;
}
/* the custom cursor already swells over this — see the cur-hot selector in
   main.js — so only ask for a pointer when that cursor is not running */
html.js:not(.cur-on) .kontakt__shield { cursor: pointer; }
html.js .kontakt__map.is-live .kontakt__shield { display: none; }

.kontakt__hint {
  padding: 0.6rem 1.1rem;
  border-radius: var(--r-btn);
  background: rgba(20, 6, 12, 0.88);
  color: var(--cream);
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  opacity: 0;
  transition: opacity 0.35s var(--quart);
}
.kontakt__shield:hover .kontakt__hint { opacity: 1; }

@media (max-width: 900px) {
  .kontakt { grid-template-columns: 1fr; }
}
@media (max-width: 560px) {
  /* the label stops being a column and becomes a line above its value */
  .kontakt__row { grid-template-columns: 1fr; gap: 0; padding-top: 1.1rem; }
  .kontakt__link { padding-top: 0.5rem; gap: 0.75rem; }
  .kontakt__cta .btn { flex: 1 1 auto; justify-content: center; }
}

/* ═══ footer ══════════════════════════════════════════════════════════════ */

.foot { padding-block: var(--bay); background: var(--plum); }

/* The footer is the mark and the three follow buttons, nothing else. Address,
   hours and phone all live in their own chapters above; repeating them here
   only gave the reader a second, staler copy to disagree with. */
.foot__inner {
  display: grid;
  justify-items: center;
  gap: clamp(1.6rem, 3.5vw, 2.4rem);
}

.foot__socials { display: flex; gap: 0.7rem; }
.foot__socials a {
  display: grid;
  place-items: center;
  width: 44px; height: 44px;
  border: 1px solid var(--rule);
  border-radius: 50%;
  transition: border-color 0.4s var(--quart), background-color 0.4s var(--quart), transform 0.5s var(--expo);
}
.foot__socials svg { width: 19px; height: 19px; }
.foot__socials a:hover { border-color: var(--sorbet); background: rgba(255, 107, 61, 0.1); transform: translateY(-3px); }

.foot__badge { display: block; width: 122px; opacity: 0.9; }

/* ═══ mobile dock ═════════════════════════════════════════════════════════ */

.dock { display: none; }

@media (max-width: 780px) {
  .dock {
    position: fixed;
    inset: auto 0 0;
    z-index: 95;
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    background: rgba(20, 6, 12, 0.92);
    backdrop-filter: blur(14px);
    border-top: 1px solid var(--rule);
    padding-bottom: env(safe-area-inset-bottom);
  }
  .dock a {
    display: grid;
    justify-items: center;
    gap: 0.3rem;
    padding: 0.7rem 0.4rem 0.75rem;
    font-size: 0.625rem;
    font-weight: 500;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--ink-2);
  }
  .dock a:first-child { color: var(--sorbet); }
  .dock svg { width: 20px; height: 20px; }
  /* the dock already carries WhatsApp and Instagram in the thumb zone — the
     hero's row is the same three links twice, and it costs the photograph the
     fold. Drop it wherever the dock exists. */
  .hero__socials { display: none; }
  /* the dock is 4.1rem tall and fixed over the page — add its height so the
     gap left under the icons still reads as one bay, same as the one above */
  .foot { padding-bottom: calc(var(--bay) + 4.1rem + env(safe-area-inset-bottom)); }
}

/* ═══ cursor ══════════════════════════════════════════════════════════════ */
/* Added by JS and only on a fine pointer. The native cursor is hidden only
   once both halves exist, so a failure here can never leave the page without
   a pointer. */

.cur {
  position: fixed;
  top: 0; left: 0;
  z-index: 140;
  pointer-events: none;
  border-radius: 50%;
  will-change: transform;
}
.cur--ring {
  width: 34px; height: 34px;
  margin: -17px 0 0 -17px;
  border: 1px solid rgba(251, 242, 233, 0.55);
  transition: width 0.4s var(--expo), height 0.4s var(--expo),
              margin 0.4s var(--expo), background-color 0.4s var(--quart),
              border-color 0.4s var(--quart), opacity 0.3s;
}
.cur--dot {
  width: 5px; height: 5px;
  margin: -2.5px 0 0 -2.5px;
  background: var(--sorbet);
  transition: opacity 0.3s var(--quart);
}
html.cur-on, html.cur-on a, html.cur-on button, html.cur-on [tabindex] { cursor: none; }

html.cur-hot .cur--ring {
  width: 62px; height: 62px;
  margin: -31px 0 0 -31px;
  background: rgba(255, 107, 61, 0.14);
  border-color: var(--sorbet);
}
html.cur-hot .cur--dot { opacity: 0; }

/* ═══ reveals ═════════════════════════════════════════════════════════════ */
/* Gated on html.js so that with the script off nothing is ever hidden. */

html.js [data-reveal] {
  opacity: 0;
  transform: translateY(34px);
  transition: opacity 0.9s var(--expo), transform 1s var(--expo);
}
html.js [data-reveal].is-in { opacity: 1; transform: none; }

/* the hero's own choreography, on a fixed timeline behind the curtain */
html.js .hero__eyebrow,
html.js .hero__sub,
html.js .hero .circle-cta,
html.js .status,
html.js .hero__socials {
  opacity: 0;
  animation: fade-up 0.95s var(--expo) forwards;
}
html.js .hero__eyebrow      { animation-delay: 0.85s; }
html.js .hero__sub          { animation-delay: 1.3s; }
html.js .hero .circle-cta   { animation-delay: 1.45s; }
html.js .status             { animation-delay: 1.6s; }
html.js .hero__socials      { animation-delay: 1.72s; }
@keyframes fade-up { from { opacity: 0; transform: translateY(24px); } to { opacity: 1; transform: none; } }

/* ═══ reduced motion ══════════════════════════════════════════════════════ */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
  html.js .msk__i,
  html.js [data-reveal] { transform: none; opacity: 1; }
  html.js .hero__shot { opacity: 1; transform: none; }
  html.js .hero__eyebrow, html.js .hero__sub, html.js .hero .circle-cta,
  html.js .status, html.js .hero__socials { opacity: 1; }
  .grain { display: none; }
  .tile img { translate: none !important; }
  /* Fixed attachment is itself the motion here — the picture holds still while
     the page moves over it. Anchor it to the section instead. */
  html.js .band { background-attachment: scroll; background-position: center top; }
}
