/* ============================================================
   S778 homepage styles. Band order is LOCKED (see STATE.md).
   Liquid port: each "section:" block maps 1:1 to sections/*.liquid.
   Two worlds only: cream outer label / charcoal inner label.
   One accent: bronze, behaving like light. Sharp corners 0-2px.
   ============================================================ */
/* Cinzel is SELF-HOSTED AND PATCHED (2026-08-30): Google's Cinzel builds
   Dcroat/dcroat as D + COMBINING MACRON (uni0304), so Croatian Đ/đ rendered
   with the bar floating above the letter (Marko: "why doesnt đ render
   properly"). These woff2s are the official variable font instanced at
   400/500 with the composites rebuilt - Cinzel's own macron bar, scaled
   1.2x, dropped through the stem (bar centre y 330/700 caps, 282/600 small
   caps). Patch recipe: 14... see STATE.md "cinzel dcroat" entry. Fauna One
   and JetBrains Mono stay on the Google CDN (prototype only; ALL fonts
   self-host at the Liquid port). */
@font-face {
  font-family: "Cinzel";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/fonts/cinzel-400.woff2") format("woff2");
}
@font-face {
  font-family: "Cinzel";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/assets/fonts/cinzel-500.woff2") format("woff2");
}
@font-face {
  /* bold cut for the product pages (Marko 2026-09-02, "bolder
     typography"); same Dcroat-patched pipeline as 400/500 -
     generated/15-cinzel-dcroat-patch/patch-cinzel.py now emits 700 */
  font-family: "Cinzel";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/assets/fonts/cinzel-700.woff2") format("woff2");
}
* { margin: 0; box-sizing: border-box; }
html { scroll-behavior: smooth; }
/* responsive pass (Marko 2026-08-29): the page must never scroll sideways.
   Without this, the herb ring's px offsets, the hero film-stage (52vh wide
   on a portrait phone) and the hover tags widened the document past the
   viewport, and mobile Chrome answered by shrink-to-fitting the LAYOUT
   viewport to ~569px on a 390px phone - the entire site rendered zoomed
   out. clip, not hidden: hidden creates a scroll container and would break
   position: sticky inside (the horizon stage); clip only forbids overflow.
   Fixed elements (#the-bottle on <body>) are not affected. */
html, body { overflow-x: clip; }
body {
  background: var(--paper);
  color: var(--charcoal);
  font-family: var(--font-body);
  font-size: var(--step-body);
  line-height: 1.65;
}
img { max-width: 100%; height: auto; display: block; }
.eyebrow {
  font-family: var(--font-display);
  font-size: var(--step-eyebrow);
  letter-spacing: .3em;
  text-transform: uppercase;
  color: var(--bronze-deep);
  margin-bottom: 1.1rem;
}
/* the deep ink is a light-surface colour; on the charcoal bands eyebrows
   take the lit bronze instead (same move the botanika photo band makes) */
.dark .eyebrow, .dark-2 .eyebrow, .inner-label .eyebrow { color: var(--bronze); }
h1, h2, h3 { font-family: var(--font-display); font-weight: 400; letter-spacing: .04em; }
h2 { font-size: var(--step-h2); line-height: 1.15; margin-bottom: 1.1rem; }
p { max-width: 52ch; }

/* ---------- band system (two worlds, no in-between greys) ---------- */
.band {
  position: relative;
  padding: clamp(4rem, 10vh, 7rem) clamp(1.2rem, 5vw, 4rem);
  min-height: 88vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.band.short { min-height: 56vh; }
.light { background: var(--paper); color: var(--charcoal); }
.light-2 { background: var(--paper-warm); color: var(--charcoal); }
.dark { background: var(--charcoal); color: var(--paper); }
.dark-2 { background: var(--charcoal-soft); color: var(--paper); }
.dark .eyebrow, .dark-2 .eyebrow { color: var(--bronze); }

/* media placeholder slots (replaced by real plates/macros as they land) */
.media-slot {
  border: 1px solid rgba(34, 34, 31, .16);
  background: repeating-linear-gradient(45deg, rgba(34, 34, 31, .05) 0 8px, transparent 8px 16px);
  display: grid;
  place-items: center;
  min-height: 15vh;
}
.dark .media-slot, .dark-2 .media-slot {
  border-color: rgba(250, 247, 240, .16);
  background: repeating-linear-gradient(45deg, rgba(250, 247, 240, .06) 0 8px, transparent 8px 16px);
}
.media-slot span {
  font-family: var(--font-mono);
  font-size: .72rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  opacity: .55;
  text-align: center;
  padding: .6rem;
  line-height: 1.8;
}
.tall-slot { min-height: 46vh; }
.wide-slot { min-height: 58vh; }

/* the suhozid staircase divider retired 2026-08-30: torn paper owns every
   section boundary now (Marko: "we apply the new torn paper section divider
   across the entire thing") */

/* ---------- 00 loading gate v5 (fill + crossfade) ----------
   The gate IS the loading screen. One slab of white Brac limestone with
   S778 carved into it: the carve fills from the bottom while the hero film
   buffers, then a lit frame opens at the centre of the glyph and expands
   out until the slab is gone and the visitor is already mid-flight.
   v1 (suhozid wall) parked in parked/gate-v1.md, v2 (crack + halves) in
   parked/gate-v2-crack.md, v3 (rubble collapse) in
   parked/gate-v3-rubble.md. */
.gate {
  position: fixed;
  inset: 0;
  z-index: 100;
  pointer-events: none;
}
/* the face is a real karst photo washed toward the brand paper tone so the
   carved monogram still reads on top of it. The carve rides on this
   element, so fading the gate as one whole is the entire hand-off. */
.g-slab {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  background-color: #efe9dd;
  background-image:
    radial-gradient(120% 90% at 50% 42%, rgba(255, 255, 255, 0) 55%, rgba(120, 110, 96, .3) 100%),
    linear-gradient(rgba(243, 237, 225, .42), rgba(243, 237, 225, .42)),
    url("/assets/rock-face.webp");
  background-size: auto, auto, cover;
  background-position: center;
}
/* carved monogram: four masked copies of the same glyph. The face is the
   recess floor (darker stone, shadowed near its top edge); a blurred dark
   copy peeks out above = the shadowed upper wall; a light copy peeks out
   below = the sunlit lower lip. Recessed, never embossed. */
.g-carve {
  grid-area: 1 / 1;
  /* 64vw floor for phones: at 46vw the carved monogram was a 179px sliver
     of the brand moment on a 390px screen */
  width: min(46vw, 520px);
  aspect-ratio: 4615 / 2108;
  -webkit-mask: url("/assets/monogram-mask-1200.png") center / contain no-repeat;
  mask: url("/assets/monogram-mask-1200.png") center / contain no-repeat;
}
@media (max-width: 600px) {
  .g-carve { width: min(64vw, 520px); }
}
/* soft ambient-occlusion glow behind the whole glyph, so the carve
   separates from the busy photo texture around it */
.c-halo {
  background: rgba(43, 40, 35, .45);
  filter: blur(7px);
  transform: scale(1.012);
}
.c-dark {
  background: rgba(38, 34, 29, .68);
  transform: translateY(-3.6px);
  filter: blur(1.6px);
}
.c-lite {
  background: rgba(255, 253, 244, .95);
  transform: translateY(3.6px);
}
.c-face {
  background-color: #c5b8a1;
  background-image:
    linear-gradient(rgba(43, 40, 35, .4), rgba(43, 40, 35, .08) 48%, rgba(43, 40, 35, .18)),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='c'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.1' numOctaves='2' seed='31' stitchTiles='stitch'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 0.45 0 0 0 0 0.42 0 0 0 0 0.38 0 0 0 0.22 0'/%3E%3C/filter%3E%3Crect width='120' height='120' filter='url(%23c)'/%3E%3C/svg%3E");
}
/* THE LOADING FILL. The recess takes light from the bottom up as the film
   buffers: --fill is the real progress, so the glyph is an honest loader
   rather than a decorative loop. Sits on the recess floor (after .c-face)
   but the lower lip .c-lite still reads over it, so the carve keeps its
   depth even when full. The soft top band is the meniscus - without it the
   fill line is a hard edge and the whole thing reads as a progress bar
   wearing a monogram. */
.c-fill {
  /* Sampled straight off the client's own label (Clients/s778 brac/assets/
     label-front.png, which S778-BRAND-CONTEXT.md names as the authority for
     exact hex). The real monogram is a rose-gold BRONZE gradient, not gold:
     median #AC8A73, running #886A5B in the shadows to #E9C29E on the
     highlights. The first pass was #d9ab63, far too yellow. */
  background-image:
    linear-gradient(rgba(233, 194, 158, .55), rgba(233, 194, 158, 0) 14px),
    linear-gradient(190deg, #c7a387 0%, #ac8a73 55%, #8f6e5c 100%);
  clip-path: inset(calc(100% - var(--fill, 0%)) 0 0 0);
  mix-blend-mode: normal;
}
/* ---------- section: header (floating pill, Marko 2026-08-30) ----------
   "rounded navbar and it wont span edge to edge ... like the condensed
   opus studio one". The pill never owns a band of the page, so content can
   never collide with it or hide under it. Behaviour (scripts/nav.js):
   condenses on scroll DOWN (links fold away, wordmark + tools stay),
   expands on scroll UP or near the top; on the story page it stays
   OFFSTAGE while the hero film plays and enters after (film end / first
   scroll). Progressive enhancement law: the CSS default is on-screen and
   expanded; JS only ever ADDS the temporary states. The old edge-to-edge
   difference-blend bar is gone: the pill carries its own frosted paper
   ground, so it reads on every section including the dark ones.
   Liquid port: sections/header.liquid. */
.site-header {
  position: fixed;
  top: max(.9rem, env(safe-area-inset-top));
  left: 50%;
  /* the -50% centring lives in TRANSFORM (not the translate: property -
     house rule 6) so the offstage state can compose with it */
  transform: translateX(-50%);
  z-index: 50;
  display: flex;
  align-items: center;
  padding: .45rem .5rem .45rem 1.05rem;
  border-radius: 999px;
  color: var(--charcoal);
  background: rgba(250, 247, 240, .82);
  -webkit-backdrop-filter: blur(14px) saturate(1.15);
  backdrop-filter: blur(14px) saturate(1.15);
  border: 1px solid rgba(34, 34, 31, .1);
  /* soft ambient drop, NOT the house one-sun object shadow: the pill is
     chrome floating above the paper, not an object lying on it */
  box-shadow: 0 10px 30px rgba(34, 34, 31, .12);
  transition: transform .65s var(--ease-settle), opacity .5s var(--ease-settle);
}
/* story page only, while the hero film plays (added by nav.js once the
   film is CONFIRMED playing - no JS means the pill simply sits there) */
.site-header.offstage {
  transform: translateX(-50%) translateY(-190%);
  opacity: 0;
  pointer-events: none;
}
/* the desktop links live INSIDE the pill and fold away on scroll down.
   max-width is a layout animation, deliberately: it is event-driven (one
   direction change, never scroll-scrubbed) on a element a few hundred px
   wide, and the pill's width change IS the design. */
.pill-links {
  display: flex;
  align-items: center;
  gap: 1.35rem;
  max-width: 30rem;
  padding: 0 .8rem 0 1.3rem;
  overflow: hidden;
  white-space: nowrap;
  opacity: 1;
  transition: max-width .55s var(--ease-settle), opacity .35s var(--ease-settle), padding .55s var(--ease-settle);
}
.site-header.condensed .pill-links {
  max-width: 0;
  padding: 0;
  opacity: 0;
}
.pill-links a {
  font-family: var(--font-mono);
  font-size: .78rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: inherit;
  text-decoration: none;
  transition: color .3s var(--ease-settle);
}
.pill-links a:hover { color: var(--bronze-deep); }
@media (max-width: 860px) {
  /* the hamburger's fullscreen menu is the whole nav on small screens */
  .pill-links { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .site-header, .pill-links { transition: none; }
  .site-header.offstage { transform: translateX(-50%); opacity: 1; pointer-events: auto; }
}
/* the real S778 monogram (Leon's vector via the same alpha mask the
   loading gate carves with), not the typed wordmark. Painted as a
   currentColor-filled MASK, not an <img>, so it takes the pill's charcoal
   ink for free. aspect-ratio matches the mask file (1200x548). */
.wordmark { display: block; color: inherit; text-decoration: none; }
.wordmark-mark {
  display: block;
  width: clamp(58px, 4.6vw, 70px);
  aspect-ratio: 1200 / 548;
  background: currentColor;
  -webkit-mask: url("/assets/monogram-mask-1200.png") center / contain no-repeat;
  mask: url("/assets/monogram-mask-1200.png") center / contain no-repeat;
}
.hamburger { background: none; border: 0; padding: .5rem; cursor: pointer; color: inherit; }
/* the MORE element, Gin Mare mechanism: the strip is 3.5x wider than
   the window; hover glides it left through the clip (1s ease out),
   unhover glides it back. One smooth current, not a wiggle. */
.hamburger .waves {
  display: block;
  width: 30px;
  height: 22px;
  overflow: hidden;
}
.hamburger .waves svg {
  display: block;
  transform: translateX(0);
  transition: transform 1s;
}
.hamburger:hover .waves svg { transform: translateX(-42%); }
@media (prefers-reduced-motion: reduce) {
  .hamburger .waves svg { transition: none; }
}
.menu {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: var(--charcoal);
  color: var(--paper);
  display: grid;
  place-items: center;
  opacity: 0;
  pointer-events: none;
  transition: opacity .6s var(--ease-settle);
}
.menu.open { opacity: 1; pointer-events: auto; }
.menu nav { display: grid; gap: 1.4rem; text-align: center; }
.menu a {
  font-family: var(--font-display);
  font-size: clamp(1.4rem, 4vw, 2.2rem);
  color: var(--paper);
  text-decoration: none;
  letter-spacing: .08em;
}
.menu a:hover { color: var(--bronze); }
.menu .menu-close {
  position: absolute;
  top: 1.2rem;
  right: 1.6rem;
  background: none;
  border: 0;
  color: var(--paper);
  font-size: 1.4rem;
  cursor: pointer;
  /* padding, not font-size: keeps the glyph quiet but clears 44px touch */
  padding: .6rem .8rem;
}


/* ---------- menu: hover image reveal (Marko 2026-08-30) ----------
   "images being shown on hover ... an interesting play that sits with the
   current design approach". The play is the site's own torn-paper collage:
   a paper SCRAP (torn clip-path, seed 13463) pinned to the right of the
   fullscreen menu; hovering a link crossfades that link's analog photo
   into the scrap with a counter-scale settle (image opens at 1.12 and
   eases to 1 while it fades in - the camera-shutter feel; a straight
   opacity swap reads like a slideshow). The outgoing photo lingers under
   the incoming one. Desktop pointer only. */
.menu-reveal {
  position: absolute;
  top: 50%;
  right: clamp(3vw, 6vw, 7rem);
  width: min(30vw, 420px);
  aspect-ratio: 4 / 3;
  background: var(--paper-warm);
  clip-path: polygon(0.0% 2.6%, 6.2% 3.7%, 12.5% 5.0%, 18.8% 4.7%, 25.0% 7.2%, 31.2% 6.7%, 37.5% 5.5%, 43.8% 3.6%, 50.0% 4.5%, 56.2% 4.6%, 62.5% 1.7%, 68.8% 4.0%, 75.0% 3.6%, 81.2% 4.1%, 87.5% 5.8%, 93.8% 4.8%, 95.5% 0.0%, 95.1% 14.3%, 96.6% 28.6%, 96.0% 42.9%, 97.7% 57.1%, 97.2% 71.4%, 97.2% 85.7%, 100.0% 94.3%, 93.8% 96.1%, 87.5% 95.9%, 81.2% 96.1%, 75.0% 97.1%, 68.8% 97.5%, 62.5% 98.3%, 56.2% 97.0%, 50.0% 97.6%, 43.8% 93.5%, 37.5% 95.4%, 31.2% 95.5%, 25.0% 93.7%, 18.8% 95.6%, 12.5% 94.4%, 6.2% 94.9%, 3.8% 100.0%, 5.8% 85.7%, 3.8% 71.4%, 2.7% 57.1%, 1.5% 42.9%, 2.6% 28.6%, 2.2% 14.3%);
  /* condense/expand mechanic (Marko 2026-08-30 v2 of the brief): the
     scrap rests COMPRESSED to a narrow slit and expands open on every
     link hover; leaving the link squeezes it shut again, so each link
     pumps the window open itself. Compress is snappier than the
     expansion, like paper springing back. */
  transform: translateY(-50%) rotate(var(--tilt, -2deg)) scaleX(.1) scaleY(.92);
  opacity: 0;
  transition: opacity .38s var(--ease-settle), transform .45s var(--ease-settle);
  pointer-events: none;
  display: none;
}
.menu-reveal.on {
  opacity: 1;
  transform: translateY(-50%) rotate(var(--tilt, -2deg)) scale(1);
  transition: opacity .5s var(--ease-settle), transform .65s var(--ease-settle);
}
@media (min-width: 861px) and (hover: hover) {
  .menu-reveal { display: block; }
  /* make room: the scrap owns the right, the links move off centre */
  .menu nav { justify-self: start; margin-left: clamp(3rem, 14vw, 16rem); text-align: left; }
}
.menu-reveal img {
  position: absolute;
  /* full bleed (Marko 2026-08-30: "zoom the images so that the tears
     dont have empty spaces"): the photo fills the whole scrap and the
     torn clip-path does the cropping - no paper mat any more. The 1.14
     settle still leaves >=1.0 coverage, so no gaps mid-animation. */
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transform: scale(1.14);
  transition: opacity .65s var(--ease-settle), transform .9s var(--ease-settle);
}
.menu-reveal img.active { opacity: 1; transform: scale(1.02); }
/* transparent cutout renders (bottle, basket) sit ON the paper, never
   crop-filled - contain + a breath of inset */
.menu-reveal img.cutout { object-fit: contain; inset: 8%; width: 84%; height: 84%; }

/* ---------- hamburger hover fan (same brief) ----------
   Three small prints dealt out from under the pill's right end while the
   wave strip glides - the menu's photo language, teased at the button.
   Starts just under the pill edge at opacity 0 (never behind the frosted
   glass - negative-z children would paint OVER the pill background) and
   deals downward with a stagger. Pure CSS, desktop pointer only. */
.burger-fan { display: none; }
@media (min-width: 861px) and (hover: hover) {
  .burger-fan {
    display: block;
    position: absolute;
    top: 100%;
    right: 1.1rem;
    width: 0;
    height: 0;
    pointer-events: none;
  }
  .burger-fan img {
    position: absolute;
    top: 4px;
    right: 0;
    width: 78px;
    /* the anchor is a 0x0 point; the global img { max-width: 100% } would
       collapse the prints to their borders without this */
    max-width: none;
    aspect-ratio: 4 / 5;
    object-fit: cover;
    border: 4px solid var(--paper);
    box-shadow: var(--sun-shadow-soft);
    opacity: 0;
    transform: translate(0, -14px) rotate(0deg);
    transition: opacity .45s var(--ease-settle), transform .55s var(--ease-settle);
  }
  .hamburger:hover + .burger-fan img:nth-child(1),
  .burger-fan:hover img:nth-child(1) { opacity: 1; transform: translate(-118px, 16px) rotate(-8deg); }
  .hamburger:hover + .burger-fan img:nth-child(2),
  .burger-fan:hover img:nth-child(2) { opacity: 1; transform: translate(-56px, 22px) rotate(3deg); transition-delay: .06s; }
  .hamburger:hover + .burger-fan img:nth-child(3),
  .burger-fan:hover img:nth-child(3) { opacity: 1; transform: translate(4px, 18px) rotate(9deg); transition-delay: .12s; }
}
@media (prefers-reduced-motion: reduce) {
  .menu-reveal, .menu-reveal img, .burger-fan img { transition: none; }
}

/* ---------- altimeter HUD ---------- */
.altimeter {
  position: fixed;
  right: clamp(.6rem, 2vw, 1.6rem);
  bottom: max(2rem, env(safe-area-inset-bottom));
  z-index: 40;
  font-family: var(--font-mono);
  font-size: .72rem;
  letter-spacing: .12em;
  color: var(--bronze);
  mix-blend-mode: difference;
  text-align: right;
  user-select: none;
}

/* ---------- section: hero film (01, light end-frame stage) ----------
   Stand-in for the Seedance film's final frame: bottle front and centre
   on warm off-white, copy + CTAs planted on top (they overlay the video
   once it exists). The first scroll gesture is spent on the handoff. */
.hero-film {
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1.6rem;
  /* the hero must OWN the first viewport (Marko 2026-08-30: "Hero section
     has a tiny sliver of the second section"): .band's 88vh floor let a
     strip of 02 show under the film at rest. svh double-declare, same
     reasoning as .horizon-stage - the always-visible height on iOS. */
  min-height: 100vh;
  min-height: 100svh;
}
.hero-overlay { text-align: center; z-index: 2; }
/* the copy lands with the flight instead of sitting on it - full reasoning and
   the measured cue are in scripts/main.js at COPY_IN. Default is VISIBLE;
   .film-pending is only ever added by JS once the film is confirmed playing. */
.hero-overlay { transition: opacity 1.1s var(--ease-settle); }
/* the hide must SNAP (transition: none): it lands while the gate is still
   opaque, and any fade-out here would still be mid-flight as the gate
   thins, flashing the copy over the film's opening. The 1.1s settle above
   belongs to the arrival at COPY_IN only. */
.hero-overlay.film-pending { opacity: 0; transition: none; }
@media (prefers-reduced-motion: reduce) {
  .hero-overlay { transition: none; }
  .hero-overlay.film-pending { opacity: 1; }
}
/* --bronze-deep is a PAPER colour. Over the film's mid-tone limestone the
   eyebrow measures 1.25:1 where 13.6px small caps need 4.5:1, and it failed at
   100% of the sampled frames of the entire film, the resting frame included -
   which is exactly why "prvi bracki gin" could barely be seen. On the film it
   takes the H1's ink (9.81:1 at rest). Bronze stays the eyebrow colour on
   every paper section; this override is scoped to the film only. */
.hero-film .eyebrow { color: var(--charcoal); }
.hero-overlay h1 {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(2rem, 1.4rem + 2.6vw, 3.4rem);
  letter-spacing: .04em;
  margin: .5rem 0 1.1rem;
}
/* narrow phones: the clamp floor of 2rem wraps SUNCE.MORE.STINA onto two
   lines; the three pillars are one breath and must stay one line. 7.4vw
   holds a single line down to 320px. */
@media (max-width: 480px) {
  .hero-overlay h1 { font-size: 7.4vw; }
}
.hero-overlay .cta-row { display: flex; gap: .8rem; justify-content: center; }
/* the three pillars separated by thick vertically-centred dots (Marko
   2026-08-29: "SUNCE (thick dot in the middle) MORE (thick dot in the
   middle) STINA"), the label's SUN | SEA | STONE spoken in the brand's
   own type. currentColor so the dots ride the h1's colour on any frame. */
.h1-dot {
  display: inline-block;
  width: .16em;
  height: .16em;
  border-radius: 50%;
  background: currentColor;
  vertical-align: middle;
  margin: 0 .32em;
  transform: translateY(-.06em);
}
.film-stage {
  /* the 92vw ceiling is the phone fix: 52vh of an 844px-tall phone is
     438px, wider than the 390px screen, and this slot was one of the
     document-wideners the overflow clip above now guards against */
  position: relative;
  width: min(52vh, 480px, 92vw);
}
.film-stage .poster { filter: drop-shadow(var(--sun-shadow)); }
/* the ONE bottle image: JS re-parents it to <body> and drives left/top/
   width from scroll progress between #hero-slot and #land-slot. It grows
   as it descends. Herbs sit under it (z 1), so they pop from behind. */
#the-bottle.scrub {
  position: fixed;
  left: 0;
  top: 0;
  /* z 1, NOT 60. The bottle must never cover copy (Marko 2026-08-28: "i dont
     want it messing up the main content and be in front"). At z1 it sits
     ABOVE section backgrounds but BELOW .landing-copy (z5) and below every
     fixed overlay (header 50, menu 60, gate 100). #hero is raised to z2 so
     the bottle is genuinely hidden UNDERNEATH section 01 and is uncovered
     only as the hero's bottom edge lifts past it. Herbs stay at z1 inside
     #landing and the bottle is a later sibling on <body>, so it still wins
     against them and they keep popping out from behind it. */
  z-index: 1;
  height: auto;
  /* the global `img { max-width: 100% }` resolves against the CONTAINING
     BLOCK, which for this fixed/absolute element is the viewport. On a 390px
     phone that silently capped a 557px box at 390px, so the browser drew the
     bottle 83px left of where every calculation in main.js had placed it and
     it hung off the left edge for the whole flight. The flight maths owns
     this width; nothing else may clamp it. */
  max-width: none;
  pointer-events: none;
  /* sun upper-left, deeper drop than the default sun-shadow */
  filter: drop-shadow(30px 42px 38px rgba(34, 34, 31, .38));
  /* centre origin: the flight now rotates, and rotating about 0 0 swings the
     bottle through a huge arc instead of turning on the spot */
  transform-origin: 50% 50%;
  will-change: transform;
}
/* section 01 is an opaque lid over the bottle and the rock field.
   The box-shadow IS the 01 -> 02 divider (restructure 2026-08-29): the
   hero ends on a crisp FLAT edge that visibly overhangs 02's paper - the
   same outer-over-inner language as the curved seams below, kept flat here
   on the direct brief ("keep it flat not curved like the second one") so
   the bottle reads as dropping out from under a real shelf. */
#hero { z-index: 2; box-shadow: 0 16px 34px rgba(34, 34, 31, .22); }
#hero-slot { aspect-ratio: 1 / 1; } /* holds the hero space once the image is re-parented */
/* ---------- hero film layer (01) ----------
   Full bleed behind the copy. There is deliberately no autoplay attribute
   in the markup: scripts/main.js calls play() on the exact frame the
   loading gate's crossfade starts, so the visitor arrives mid-flight. */
.hero-film { overflow: hidden; }
.hero-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* --film-x is the crop bias, single source of truth for the film, the 4K
     rest twin AND scripts/main.js (filmBottleX reads the video's computed
     object-position, so the flight origin follows this value wherever it is
     set). 50% = the centred cover crop every desktop measurement assumed. */
  object-position: var(--film-x, 50%) 50%;
  z-index: 0;
  pointer-events: none;
  background: var(--paper);
}
/* portrait crop bias (responsive pass 2026-08-29): the take-3 film ends with
   the bottle at x 0.749 of the 16:9 frame (main.js FILM_BOTTLE_FX). A centred
   cover crop on a 390x844 phone shows only frame x 0.37-0.63, so the resting
   hero had NO bottle on screen at all. 78% slides the crop window right: the
   bottle rests at ~0.65 of the screen width on a phone, ~0.71 on a portrait
   tablet. Aspect-gated, not width-gated: the defect is the crop shape, not
   the device. THIS NUMBER IS FILM-SPECIFIC like FILM_BOTTLE_FX - re-derive
   both together after any film swap.
   This is the RESTING crop, not the playback crop (Marko 2026-08-29: "keep
   it centered all the way until the end and then drift it to the right
   towards the bottle"): during playback main.js pins the video's inline
   object-position at 50% and pans it to this value over the film's final
   approach, then clears the inline style so the stylesheet owns the rest
   state again. Poster, no-JS, reduced-motion and .hero-rest all sit at this
   resting value, which is why it stays in CSS. */
@media (max-aspect-ratio: 1/1) {
  .hero-film { --film-x: 78%; }
  /* portrait also lifts the copy block into the film's sky band: centred,
     the CTAs landed exactly on the bottle's cork in the biased crop (the
     end frame's bottle fills the middle of a portrait screen). flex-start
     plus the margin parks eyebrow/H1/CTAs over open sky at rest AND at the
     12.8s copy arrival - the framing is the same summit hold from ~13s on. */
  .hero-film { justify-content: flex-start; }
  .hero-overlay { margin-top: clamp(1rem, 4svh, 5rem); }
  /* a hair narrower than the 7.4vw one-line floor so the H1's ends clear
     the cork, which crowns the exact centre-right of the portrait crop */
  .hero-overlay h1 { font-size: 7vw; }
}
/* the true-4K resting frame - an exact twin of the video's box so its cover
   crop maps pixel-for-pixel onto the film's last frame; z1 sits over the
   film, under the copy (z2). Revealed only by JS on 'ended'. */
.hero-rest {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* MUST match .hero-video: the twin's crop mapping is what makes the 4K
     swap invisible, so it rides the same --film-x bias on portrait */
  object-position: var(--film-x, 50%) 50%;
  z-index: 1;
  pointer-events: none;
  opacity: 0;
  transition: opacity .8s ease;
}
.hero-rest.on { opacity: 1; }
.hero-film .film-stage { z-index: 2; }
/* the ghost CTA is the one piece of copy with no fill of its own, so over
   the dark forest stretch (3.5s to 5s) it fell to 3.2:1. A paper wash puts
   it back over 4.5:1 without veiling more of the film. Measured, not guessed. */
.hero-film .btn.ghost {
  background: rgba(250, 247, 240, .58);
  border-color: rgba(34, 34, 31, .2);
}
.hero-film .btn.ghost:hover { background: rgba(199, 163, 135, .5); }
/* The film's own last frame is a bottle, so a second cut-out bottle must not
   be visible while the hero is up. That used to be handled by fading the
   render in on first scroll (.film-hidden). It is now handled structurally:
   #hero is z2 and opaque, the bottle is z1, so it is simply underneath.
   The opacity rule is kept because the rock field reuses it. */
#the-bottle { transition: opacity .7s var(--ease-settle); }
#the-bottle.film-hidden { opacity: 0; }

/* ---------- 02 -> 03 seam: the IRONHILL transition ------------------
   Marko 2026-08-28, second pass, with ironhill.au open: "this is the section
   gap where i want to implement the rocks. hard connected to the edges and
   we'll do ironhill transition."
   The first build had the rocks scattered across 02 on a FIXED viewport
   layer, present from the first scroll ("right now they're scattered on the
   second section... the rocks also appear instantly, which is not the goal").
   Everything here is the answer to that: the field moved down onto the 02/03
   seam, went from fixed to document-anchored so it sits IN the gap instead of
   riding the screen, got bolted to the frame edges, and got a drift-in.

   THE MELT. 02's warm paper hangs down over 03's lighter paper as one organic
   mass, deepest where it meets the left and right edges and thinning through
   the middle so the centre column stays open for copy. z2, but BELOW the
   rocks (z3) since 2026-08-28 evening (Marko: "make the stones be in front
   of the second section not behind it"): the chain composition stays, but
   the stones paint OVER the cream mass, sitting on 02's hanging edge in
   full instead of half-buried behind it. 03's copy column keeps clear of
   the clusters, so the rocks outranking .spin-center (z2) is safe. */
.melt {
  position: absolute;
  /* ownership flip 2026-08-30 (see .seam): the melt is 04's cream paper, so
     it lives at the BOTTOM of #spin and hangs down over #stina - same page
     pixels as the old top:0-in-#stina, correct owner. The rocks stay in
     #stina (z3 > z2, so they still paint over the mass). */
  top: 100%;
  left: 0;
  right: 0;
  z-index: 2;
  height: clamp(140px, 24vh, 330px);
  overflow: hidden;
  pointer-events: none;
}
.melt svg { display: block; width: 100%; height: 100%; }
.melt #melt-mass { fill: var(--paper-warm); }
/* phones: 1440 units of pattern squeezed into 390px turns the whole mass into
   a comb. Blow the SVG up past the band and centre it, so one shoulder and a
   couple of lobes show at close to their designed proportions. */
/* phones: the mass is ANCHORED at the two frame edges, so it must never be
   blown up past the band - that pushes both shoulders off screen and leaves
   only the shallow middle, which is the one part that carries no meaning.
   Full width, and accept that preserveAspectRatio="none" squeezes the middle
   lobes into ripples; the shoulders are what has to survive. */
@media (max-width: 760px) {
  .melt { height: clamp(110px, 17vh, 190px); }
}

/* ---------- the SEAM system (restructure 2026-08-29) ------------------
   Every section boundary now speaks the same inner/outer paper language
   the 04/05 melt established: the upper section's paper hangs over the
   lower one as one organic drawn edge with a single group drop shadow.
   Each seam is its OWN lobe table in the markup (drawn variant, never a
   copy-paste - even wavelength is what makes a silhouette look
   generated); this class only sizes and layers the band. Shallower than
   the melt: these are page punctuation, the melt + rocks stay THE
   set-piece. The hero's variant is the flat box-shadow edge (#hero). */
.seam {
  position: absolute;
  /* OWNERSHIP FLIP (Marko 2026-08-30, round 3): every tear lives INSIDE the
     section whose paper it is and hangs PAST that section's edge onto its
     neighbour - "the section above should be eating into the one below".
     When the tear belonged to the lower section it painted over anything
     hanging out of the sheet above (the canopy crowns were cut off on a
     ruled line exactly at the boundary). top:100% = same page pixels as the
     old top:0-in-the-next-section, correct owner. */
  top: 100%;
  left: 0;
  right: 0;
  z-index: 2;
  height: clamp(90px, 15vh, 200px);
  overflow: hidden;
  pointer-events: none;
}
.seam svg { display: block; width: 100%; height: 100%; }
/* landing -> berba seam variant (2026-09-02): cream from CSS, not from a
   filtered SVG surface - see the markup comment. Order: blurred shadow
   svg (z0) under, masked cream ::after (z1), fiber rim svg (z2) over. */
.seam-lb svg { position: absolute; inset: 0; }
.seam-lb .lb-shadow { z-index: 0; }
.seam-lb .lb-rim { z-index: 2; }
.seam-lb::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background: var(--paper-warm);
  -webkit-mask: url("data:image/svg+xml,<svg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%201440%20200'%20preserveAspectRatio='none'><path%20d='M-4.0%2C-4%20L-4.0%2C109.2%20L3.5%2C123.7%20L10.4%2C130.6%20L19.5%2C125.2%20L28.8%2C129.7%20L36.8%2C130.5%20L47.4%2C116.4%20L57.6%2C123.0%20L65.5%2C112.5%20L72.0%2C112.3%20L81.9%2C125.9%20L88.9%2C122.6%20L97.4%2C125.5%20L103.7%2C138.3%20L114.2%2C138.9%20L123.5%2C142.6%20L131.3%2C150.6%20L140.5%2C140.9%20L150.1%2C138.9%20L160.2%2C129.6%20L168.1%2C117.9%20L178.4%2C115.0%20L188.8%2C107.9%20L198.4%2C105.5%20L208.1%2C111.3%20L216.9%2C107.1%20L224.4%2C115.2%20L233.1%2C120.7%20L239.3%2C112.7%20L248.5%2C110.7%20L255.5%2C110.1%20L262.0%2C101.2%20L271.2%2C89.2%20L280.8%2C88.2%20L291.6%2C68.2%20L300.1%2C69.5%20L309.7%2C64.5%20L317.9%2C58.7%20L327.9%2C73.8%20L338.0%2C70.8%20L347.0%2C78.8%20L355.8%2C82.2%20L363.3%2C76.1%20L371.4%2C78.3%20L379.7%2C75.6%20L388.8%2C61.0%20L398.6%2C61.7%20L406.2%2C62.4%20L414.3%2C53.2%20L424.8%2C61.7%20L434.3%2C61.4%20L443.1%2C68.5%20L451.9%2C83.0%20L460.3%2C83.3%20L467.6%2C89.7%20L473.7%2C100.6%20L482.9%2C100.4%20L489.2%2C93.6%20L496.7%2C102.5%20L506.0%2C101.3%20L512.4%2C91.2%20L522.8%2C98.5%20L532.1%2C95.3%20L542.0%2C96.4%20L551.3%2C110.4%20L559.3%2C109.5%20L567.9%2C119.4%20L575.5%2C135.2%20L586.0%2C131.0%20L593.7%2C138.5%20L604.5%2C140.7%20L610.7%2C134.6%20L616.9%2C133.7%20L626.6%2C132.5%20L634.7%2C123.6%20L644.3%2C121.3%20L653.7%2C122.0%20L660.0%2C119.7%20L668.1%2C125.9%20L675.8%2C133.3%20L684.5%2C129.7%20L692.6%2C136.9%20L702.7%2C140.5%20L710.8%2C135.4%20L719.3%2C136.5%20L729.0%2C127.1%20L737.9%2C113.2%20L746.5%2C113.1%20L757.2%2C93.9%20L766.8%2C91.8%20L773.6%2C97.8%20L784.2%2C87.8%20L794.1%2C96.4%20L803.1%2C94.8%20L810.1%2C91.6%20L820.6%2C100.8%20L831.3%2C88.5%20L837.3%2C88.2%20L844.4%2C86.6%20L851.5%2C79.8%20L860.1%2C63.6%20L870.0%2C63.2%20L877.2%2C59.4%20L887.4%2C52.7%20L894.2%2C62.2%20L900.8%2C60.7%20L907.4%2C55.6%20L915.7%2C70.5%20L923.5%2C77.7%20L931.0%2C73.9%20L937.1%2C76.6%20L945.0%2C87.2%20L951.3%2C80.2%20L960.9%2C74.0%20L970.3%2C77.2%20L979.4%2C64.8%20L989.9%2C66.8%20L996.4%2C74.2%20L1002.6%2C65.0%20L1009.1%2C67.4%20L1019.4%2C86.2%20L1028.3%2C86.9%20L1035.2%2C95.5%20L1042.0%2C108.1%20L1051.1%2C113.3%20L1059.3%2C115.3%20L1067.9%2C124.1%20L1074.6%2C115.9%20L1082.1%2C111.3%20L1088.8%2C117.2%20L1098.8%2C110.7%20L1106.8%2C103.6%20L1116.5%2C116.6%20L1126.3%2C112.3%20L1133.9%2C120.7%20L1141.3%2C133.7%20L1151.9%2C134.3%20L1161.3%2C141.9%20L1167.8%2C150.8%20L1176.6%2C143.9%20L1186.5%2C145.7%20L1195.7%2C141.9%20L1202.5%2C129.0%20L1211.0%2C127.1%20L1219.3%2C123.3%20L1230.0%2C113.3%20L1239.4%2C117.8%20L1248.0%2C117.9%20L1256.7%2C116.4%20L1264.0%2C128.8%20L1273.0%2C124.2%20L1283.8%2C125.3%20L1292.6%2C126.9%20L1300.5%2C115.6%20L1309.6%2C109.9%20L1315.9%2C106.5%20L1325.3%2C87.2%20L1334.1%2C82.6%20L1344.4%2C78.1%20L1351.3%2C68.2%20L1358.1%2C73.8%20L1365.5%2C78.8%20L1375.1%2C71.8%20L1385.8%2C83.4%20L1392.3%2C86.3%20L1399.9%2C80.7%20L1407.2%2C81.3%20L1414.0%2C86.1%20L1423.5%2C71.4%20L1429.8%2C68.9%20L1436.5%2C70.1%20L1436.5%2C-4%20Z'%20fill='%23fff'/></svg>") 0 0 / 100% 100% no-repeat;
  mask: url("data:image/svg+xml,<svg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%201440%20200'%20preserveAspectRatio='none'><path%20d='M-4.0%2C-4%20L-4.0%2C109.2%20L3.5%2C123.7%20L10.4%2C130.6%20L19.5%2C125.2%20L28.8%2C129.7%20L36.8%2C130.5%20L47.4%2C116.4%20L57.6%2C123.0%20L65.5%2C112.5%20L72.0%2C112.3%20L81.9%2C125.9%20L88.9%2C122.6%20L97.4%2C125.5%20L103.7%2C138.3%20L114.2%2C138.9%20L123.5%2C142.6%20L131.3%2C150.6%20L140.5%2C140.9%20L150.1%2C138.9%20L160.2%2C129.6%20L168.1%2C117.9%20L178.4%2C115.0%20L188.8%2C107.9%20L198.4%2C105.5%20L208.1%2C111.3%20L216.9%2C107.1%20L224.4%2C115.2%20L233.1%2C120.7%20L239.3%2C112.7%20L248.5%2C110.7%20L255.5%2C110.1%20L262.0%2C101.2%20L271.2%2C89.2%20L280.8%2C88.2%20L291.6%2C68.2%20L300.1%2C69.5%20L309.7%2C64.5%20L317.9%2C58.7%20L327.9%2C73.8%20L338.0%2C70.8%20L347.0%2C78.8%20L355.8%2C82.2%20L363.3%2C76.1%20L371.4%2C78.3%20L379.7%2C75.6%20L388.8%2C61.0%20L398.6%2C61.7%20L406.2%2C62.4%20L414.3%2C53.2%20L424.8%2C61.7%20L434.3%2C61.4%20L443.1%2C68.5%20L451.9%2C83.0%20L460.3%2C83.3%20L467.6%2C89.7%20L473.7%2C100.6%20L482.9%2C100.4%20L489.2%2C93.6%20L496.7%2C102.5%20L506.0%2C101.3%20L512.4%2C91.2%20L522.8%2C98.5%20L532.1%2C95.3%20L542.0%2C96.4%20L551.3%2C110.4%20L559.3%2C109.5%20L567.9%2C119.4%20L575.5%2C135.2%20L586.0%2C131.0%20L593.7%2C138.5%20L604.5%2C140.7%20L610.7%2C134.6%20L616.9%2C133.7%20L626.6%2C132.5%20L634.7%2C123.6%20L644.3%2C121.3%20L653.7%2C122.0%20L660.0%2C119.7%20L668.1%2C125.9%20L675.8%2C133.3%20L684.5%2C129.7%20L692.6%2C136.9%20L702.7%2C140.5%20L710.8%2C135.4%20L719.3%2C136.5%20L729.0%2C127.1%20L737.9%2C113.2%20L746.5%2C113.1%20L757.2%2C93.9%20L766.8%2C91.8%20L773.6%2C97.8%20L784.2%2C87.8%20L794.1%2C96.4%20L803.1%2C94.8%20L810.1%2C91.6%20L820.6%2C100.8%20L831.3%2C88.5%20L837.3%2C88.2%20L844.4%2C86.6%20L851.5%2C79.8%20L860.1%2C63.6%20L870.0%2C63.2%20L877.2%2C59.4%20L887.4%2C52.7%20L894.2%2C62.2%20L900.8%2C60.7%20L907.4%2C55.6%20L915.7%2C70.5%20L923.5%2C77.7%20L931.0%2C73.9%20L937.1%2C76.6%20L945.0%2C87.2%20L951.3%2C80.2%20L960.9%2C74.0%20L970.3%2C77.2%20L979.4%2C64.8%20L989.9%2C66.8%20L996.4%2C74.2%20L1002.6%2C65.0%20L1009.1%2C67.4%20L1019.4%2C86.2%20L1028.3%2C86.9%20L1035.2%2C95.5%20L1042.0%2C108.1%20L1051.1%2C113.3%20L1059.3%2C115.3%20L1067.9%2C124.1%20L1074.6%2C115.9%20L1082.1%2C111.3%20L1088.8%2C117.2%20L1098.8%2C110.7%20L1106.8%2C103.6%20L1116.5%2C116.6%20L1126.3%2C112.3%20L1133.9%2C120.7%20L1141.3%2C133.7%20L1151.9%2C134.3%20L1161.3%2C141.9%20L1167.8%2C150.8%20L1176.6%2C143.9%20L1186.5%2C145.7%20L1195.7%2C141.9%20L1202.5%2C129.0%20L1211.0%2C127.1%20L1219.3%2C123.3%20L1230.0%2C113.3%20L1239.4%2C117.8%20L1248.0%2C117.9%20L1256.7%2C116.4%20L1264.0%2C128.8%20L1273.0%2C124.2%20L1283.8%2C125.3%20L1292.6%2C126.9%20L1300.5%2C115.6%20L1309.6%2C109.9%20L1315.9%2C106.5%20L1325.3%2C87.2%20L1334.1%2C82.6%20L1344.4%2C78.1%20L1351.3%2C68.2%20L1358.1%2C73.8%20L1365.5%2C78.8%20L1375.1%2C71.8%20L1385.8%2C83.4%20L1392.3%2C86.3%20L1399.9%2C80.7%20L1407.2%2C81.3%20L1414.0%2C86.1%20L1423.5%2C71.4%20L1429.8%2C68.9%20L1436.5%2C70.1%20L1436.5%2C-4%20Z'%20fill='%23fff'/></svg>") 0 0 / 100% 100% no-repeat;
}
/* the UP tear (Marko 2026-08-30): the layering goal is 1-2-1-2 - the white
   sections sit INSIDE, the cream sheet is ALWAYS on top. So where a cream
   section FOLLOWS a white one, the cream rises up over the white's bottom
   edge instead of the white hanging down. Ownership flip applies here too:
   the variant lives at the TOP of the on-top (lower) section and hangs
   above its edge, filled with that section's own paper, torn edge pointing
   up, shadow thrown up onto the sheet underneath. */
.seam.up { top: auto; bottom: 100%; }
@media (max-width: 760px) {
  .seam { height: clamp(70px, 11vh, 130px); }
}

/* THE ROCKS. This layer is 0 tall and sits ON the seam, so --y is a vh offset
   from it: positive = down onto 03. Pass 2 (the "protrude" pass): two CHAINS
   hang off the melt mass, limb/rosette grammar per the Ironhill closing
   frame - the mass (z2) buries each root's upper half, each stone overlaps
   the next by 20-30% (the overlap is the limb), sizes taper to the tip,
   depths stagger (right hero chain ~40vh, left ~30vh), and the whole middle
   stays one bare melt edge. ZERO stones with cream on all sides at rest -
   that was the exact complaint ("random smaller rocks floating on their
   own"). Nothing comes closer to the centre than a fifth of the viewport.
   ALWAYS VISIBLE, no hidden state at all since the fifth composition
   (Marko: "i dont want them to fade in - they have to be there"): the only
   JS enhancement left is the parallax in scripts/main.js. */
.rock-field {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 0;
  z-index: 3;
  pointer-events: none;
}
.rock-field .rock {
  position: absolute;
  left: var(--x, auto);
  right: var(--rx, auto);
  top: var(--y);
  width: var(--w);
  transform: rotate(var(--r0, 0deg));
}
.rock-field .rock img {
  display: block;
  width: 100%;
  max-width: none;          /* the global img rule would cap these at 100% */
  height: auto;
  /* one shadow length per stone (--shd, inline on each .rock): a flat
     shared shadow made every stone hover at the same height, which is a
     compositing tell. Bigger stone = longer softer throw, fragments cast
     short and dense - same sun, different distances from the paper. */
  filter: drop-shadow(var(--shd, 10px 14px 15px rgba(34, 34, 31, .22)));
}
/* flipped stones: a free second silhouette from the same cutout. No drift-in
   exists any more (fifth composition: "they have to be there"), so the flip
   is one static rule. */
.rock-field .rock.flip img { transform: scaleX(-1); }
/* phones. Desktop vw sizing turns these into pebbles, but a blanket
   multiplier is worse: at 2.2x every rock crossed the centre column on a
   390px screen. So each stone carries its own phone geometry inline
   (--mw/--mx/--mrx/--my), sized bigger AND pushed further off the edge so
   more of it is cut away. The chain TIPS (.drop-sm) used to display:none
   here, which quietly broke the study's grammar on phones: a chain that
   ends at its mid stone has no taper, so both compositions read as clumps
   pressed against the frame edges (and the harness rightly counted the
   hidden tips as orphans). The tips now carry their own phone geometry
   inline, sized up to ~12vw so they read as small stones, not specks, each
   still overlapping its mid stone - the overlap is the limb. */
@media (max-width: 760px) {
  .rock-field .rock {
    width: var(--mw, var(--w));
    left: var(--mx, var(--x, auto));
    right: var(--mrx, var(--rx, auto));
    top: var(--my, var(--y));
  }
}

/* the note described the film that was still missing; it exists now, so
   it is hidden rather than removed until the Liquid port catches up */
.hero-film .film-note { display: none; }
/* sound: the film carries a score and the gate opens with no click, so
   playback has to start muted. This is the one affordance that unmutes. */
.film-tools {
  position: absolute;
  /* bottom LEFT on purpose: the altimeter HUD is fixed bottom-right and on
     a short viewport the two collided */
  left: clamp(1.2rem, 5vw, 4rem);
  /* max() with the safe-area inset: on notched phones (viewport-fit=cover)
     the toggles must clear the home indicator */
  bottom: max(clamp(1.2rem, 4vh, 2.4rem), env(safe-area-inset-bottom));
  z-index: 3;
  display: flex;
  gap: .6rem;
}
@media (max-width: 760px) {
  /* phone Safari owns the bottom strip, and the hero can grow TALLER than
     the viewport on phones - so anchoring to the hero's bottom edge sank
     the pills under the toolbar even after the first 09-04 lift (Marko's
     screenshot). Anchor to the FIRST VISIBLE SCREEN instead: 100svh is the
     always-visible height, the pills end ~55px above the toolbar line and
     still scroll away with the hero. vh line = fallback for old browsers. */
  .film-tools {
    bottom: auto;
    top: calc(100vh - 5.8rem);
    top: calc(100svh - 5.8rem);
  }
}
/* carved like every other control (the rounded pill was the one outlier
   in the button system); shared skin for the sound and skip toggles */
.film-sound {
  background: rgba(250, 247, 240, .72);
  border: 1px solid rgba(34, 34, 31, .18);
  padding: .55rem 1.1rem;
  font-family: var(--font-mono);
  font-size: .72rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--charcoal);
  cursor: pointer;
  transition: background .25s var(--ease-settle), opacity .25s var(--ease-settle);
}
.film-sound:focus-visible { outline: 2px solid var(--bronze); outline-offset: 2px; }
/* the brand claims the film from frame one (Marko 2026-09-04): monogram
   hard left, in the markup so it stands without JS too */
.hero-brand {
  position: absolute;
  top: max(1.3rem, env(safe-area-inset-top));
  left: clamp(1.2rem, 5vw, 4rem);
  height: clamp(40px, 6vh, 58px);
  width: auto;
  z-index: 3;
  filter: drop-shadow(0 2px 10px rgba(34, 34, 31, .38));
  transition: opacity .5s var(--ease-settle);
}
/* the monogram's job is claiming the film WHILE the nav pill is offstage;
   the moment the pill returns (ended / first scroll / no-JS / reduced
   motion) it would double the 7S8 wordmark right beside it (Marko's phone
   screenshot, 2026-09-04), so it yields. Browsers without :has keep both
   marks - cosmetic only. */
body:has(.site-header:not(.offstage)) .hero-brand { opacity: 0; pointer-events: none; }
.film-sound:hover { background: var(--paper); }
.film-sound.on { background: var(--bronze); border-color: transparent; }

.film-note {
  margin-top: 1.4rem;
  text-align: center;
  font-family: var(--font-mono);
  font-size: .72rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  opacity: .45;
}

/* ---------- section: landing (02) + spin (03) shared centre layout ---- */
.landing, .spin { min-height: 100vh; }
/* extra bottom floor (2026-09-02, THE faint line, root cause found by
   Marko's bisect: the bottom herbs' drop-shadows crossed the section
   boundary and the seam's cream cut them dead straight - shadow tail is
   ~70px (dy 18 + blur 20) plus parallax drift. The boundary now sits
   far below any shadow's reach at every viewport, so nothing is ever
   guillotined. Distance, not stacking surgery: every z reshuffle here
   breaks either the flying-bottle law or the rock-field hover lift. */
.landing { position: relative; padding-bottom: clamp(9rem, 16vh, 14rem); }
/* the rock field hangs off both edges of 03; clip only the x axis so the
   rocks that rise out of the top onto 02 are left alone. */
.spin { overflow-x: clip; }
/* blind-embossed suhozid backdrop (2026-09-01): the brand's suhozid
   line art pressed into the cream, shadows+highlights only. Positioned
   with z auto so it paints under the z1 rocks and z2 .spin-center; the
   mask fades it out before the top seam's cream fill and the bottom
   melt/rocks so the paper reads continuous. */
.spin-wall {
  position: absolute;
  inset: 0;
  background: url("/assets/spin-suhozid-emboss.webp") center bottom / cover no-repeat;
  opacity: .5;
  pointer-events: none;
  -webkit-mask-image: linear-gradient(180deg, transparent 0, #000 16%, #000 78%, transparent 96%);
  mask-image: linear-gradient(180deg, transparent 0, #000 16%, #000 78%, transparent 96%);
}
@media (max-width: 760px) {
  /* phone (Marko 2026-09-01: "it eats into the text"): confine the wall
     to the upper band behind the bottle - it fades out before DETALJI
     BOCE so no text ever sits on the emboss. Position pins the left
     wedge's crown into that band. */
  .spin-wall {
    background-position: 12% bottom;
    opacity: .42;
    -webkit-mask-image: linear-gradient(180deg, transparent 0, #000 12%, #000 46%, transparent 60%);
    mask-image: linear-gradient(180deg, transparent 0, #000 12%, #000 46%, transparent 60%);
  }
  /* the stina seam's rock chains overhang the section bottom; on phones
     they swallowed the feature buttons + drag hint (pre-existing, made
     obvious by the wall). Extra floor keeps 03's controls clear. */
  .spin { padding-bottom: 30vh; }
}
/* 03's own content sits above the melt (z0) and the rocks (z1). Scoped to
   .spin on purpose: raising .spin-center globally would trap #landing's
   herbs in a stacking context and put them in FRONT of the flying bottle. */
.spin > .spin-center { position: relative; z-index: 2; }
/* the whole composition rides a touch high of true centre so the bottom
   herb + its callout never fall off short laptop viewports.
   top, NOT translate, on purpose (2026-08-28): the individual `translate`
   property creates a STACKING CONTEXT exactly like `transform` does, but
   getComputedStyle(...).transform still reads "none", so the trap is
   invisible to the usual audit. That context capped every herb annotation
   under the seam rocks (rock-field z3) no matter what z-index the herbs
   carried - a whole debugging session went into finding this. position:
   relative + top has the identical visual effect and no stacking context,
   so the .herb:hover z-lift (see below) actually reaches the root. */
.landing .spin-center { position: relative; top: -4vh; }
/* ---------- the CANOPY FIELD (02, restructure 2026-08-29) ----------
   Half tree canopies grown out of 02's frame edges - maslina left,
   borovica right - the forest beat of the descent. They replaced the rock
   wedges that used to rise up here from the old 02/03 seam (the rocks
   moved down to the shoreline seam above #stina). Ironhill rules carried
   over verbatim from the rocks: bolted to the frame edges (negative --x/
   --rx cut them off mid-crown), ALWAYS present - no fade-in, no hidden
   state - and the only JS on them is the per-piece parallax (data-p).
   z3 (was z0; ownership flip 2026-08-30): the trees are printed on 02's
   cream sheet, so they must paint OVER its torn edge (seam z2) and spill
   past the tear line instead of being cut off at the boundary - the same
   stratum the rocks (z3) hold over the melt (z2). Still under the herb
   callouts (z4) and .landing-copy (z5). The flying bottle (body child,
   z1) now passes UNDER the corner foliage; the flight path is the centre
   column, the canopies hold the frame edges, so they never actually
   cross - and a corner brush would read as depth, not occlusion. */
.canopy-field {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
}
.canopy {
  position: absolute;
  left: var(--x, auto);
  right: var(--rx, auto);
  /* --y OR --b, not both: bottom-anchored pieces (--b, negative = past the
     section edge) tuck their frame-cut bottom under the NEXT section's
     opaque background at ANY viewport height - vw tops could not guarantee
     that (Marko 2026-08-30: visible straight edges "right at the section
     division"). Values are free-form lengths, so max(6vh, 78px) style
     clamps ride in the inline vars to clear the fixed header at every
     viewport. */
  top: var(--y, auto);
  bottom: var(--b, auto);
  width: var(--w);
}
.canopy img {
  display: block;
  width: 100%;
  max-width: none;          /* the global img rule would cap these at 100% */
  height: auto;
  /* softer, longer throw than the rocks: foliage sits higher off the paper
     than stone does, one sun upper-left as everywhere */
  filter: drop-shadow(26px 38px 30px rgba(34, 34, 31, .26));
}
.canopy.flip img { transform: scaleX(-1); }
@media (max-width: 760px) {
  .canopy {
    width: var(--mw, var(--w));
    left: var(--mx, var(--x, auto));
    right: var(--mrx, var(--rx, auto));
    top: var(--my, var(--y, auto));
    bottom: var(--mb, var(--b, auto));
  }
}

.landing-copy {
  position: absolute;
  left: max(3vw, 2rem);
  top: 50%;
  translate: 0 -50%;
  max-width: 19rem;
  text-align: left;
  z-index: 5;
}
.landing-copy h2 { font-size: clamp(1.25rem, .95rem + 1.2vw, 1.7rem); }
/* copy-yield on herb hover REMOVED 2026-08-29: only komorac's callout
   ever reached the copy column, and it now runs toward the BOTTLE instead
   (not .cl; see the komorac markup comment). The remaining .cl callouts
   (ruzmarin, lovor, mirta) stop short of the copy - Marko: "the rest dont
   come even close to the text". */
/* small screens: the copy owns the TOP of the section, above everything
   (Marko 2026-08-29: "you clearly have room for the text to be above
   everything"). The section stops centring: copy first under the header,
   then the ring, with a hard margin sized so the tallest-reaching herb
   (borovica, dy -250 scaled) never climbs into the text the way it did
   when the centred stage rode up under the static copy. top: 0 kills the
   desktop -4vh stage lift inside this range (that lift exists for short
   laptop viewports, not phones). */
@media (max-width: 900px) {
  .landing { justify-content: flex-start; padding-top: clamp(4.5rem, 9vh, 7rem); }
  .landing-copy { position: static; translate: none; max-width: 42rem; text-align: center; margin: 0 auto; }
  .landing .spin-center { top: 0; margin-top: clamp(4rem, 10vh, 7rem); }
}
.spin-center {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.2rem;
  width: 100%;
}
.spin-copy { text-align: center; max-width: 42rem; }
.spin-copy h2 { font-size: clamp(1.4rem, 1rem + 1.6vw, 2.1rem); }
.spin-stage {
  position: relative;
  width: min(980px, 94vw);
  display: grid;
  justify-items: center;
}
/* where the descending bottle lands: bigger than the hero slot, so the
   image grows as it comes down and takes up more of the screen */
.land-slot {
  /* 78vw ceiling: 66vh of a portrait phone is wider than the screen, and
     the landed bottle must leave the herb ring room at the edges */
  width: min(66vh, 620px, 78vw);
  aspect-ratio: 1 / 1;
}
/* the landing reads too light for the clear-glass bottle to pop (Marko
   2026-08-29): a soft stone-tone radial pool behind the stage gives the
   glass edges and the white label something to stand against, like a
   graded product-photo backdrop. Painted on ::before so the section's
   EDGE tones stay exactly --paper-warm - the hero-fade above and the
   melt/veil tones are colour-matched to that and must not shift. Sits
   under the herbs and the bottle (they are positioned; this is z0). */
.landing .spin-stage { position: relative; }
.landing .spin-stage::before {
  content: "";
  position: absolute;
  /* BAKED image, not a CSS gradient (Marko 2026-09-02, the "faint line
     like an image border" under the ring). Two rounds of diagnosis:
     (1) the old -14% box ended exactly at the ellipse radius, and the
     box edge guillotined still-painted rows - razor-straight line;
     (2) enlarging the box moved the cutoff to the ELLIPSE boundary,
     because Chromium keeps ~4-5% alpha right up to r=1.0 no matter
     where the transparent stop sits - a faint curved line remained on
     wide-gamut displays. CSS radial-gradient cannot render this pool
     cleanly, so it is baked: assets/landing-pool.webp, same geometry
     and colours, alpha rolled to TRUE zero by r=.88 with a smootherstep
     tail + dither, border pixels hard-zeroed. Regenerate: the numpy
     block in STATE.md 2026-09-02 (10). */
  inset: -34% -34%;
  background: url("/assets/landing-pool.webp") center / 100% 100% no-repeat;
  pointer-events: none;
}

/* botanicals hide AT the centre, behind the bottle; herbs-in sends each to
   its own offset. Reversible: removing the class tucks them back behind
   the bottle. z-index is AUTO on purpose (2026-08-28): the bottle is a
   later <body> sibling at z1, so it still paints over the herbs by tree
   order, but z1 here made .herbs a stacking context that TRAPPED the
   callout notes under the seam rocks (rock-field z3 in the root context) -
   Marko: "the text from list masline right now goes behind the rocks".
   With auto, .note carries its own root-context z (see .note). */
.herbs { position: absolute; inset: 0; pointer-events: none; }
.herb {
  position: absolute;
  left: 50%;
  top: 42%;
  /* the ring scale system (responsive pass 2026-08-29). Every herb carries
     desktop-tuned px offsets (--dx/--dy) and a width (--hw) inline; on small
     screens the WHOLE composition scales as one drawing instead of each herb
     being re-tuned: --ringx/--ringy squeeze the offsets (x harder than y on
     phones, so the circle becomes the vertical ellipse a portrait screen
     actually has room for) and --rings scales the cutout sizes. The mirrored
     pairs, the arc, and the >=43px shadow clearances from the 2026-08-29
     recomposition all survive because everything scales together. Defaults
     of 1 = the desktop layout, untouched. */
  width: calc(var(--hw) * var(--rings, 1));
  margin-left: calc(var(--hw) * var(--rings, 1) / -2);
  opacity: 0;
  transform: translate(0, 0) scale(.45) rotate(var(--hr, 0deg));
  transition: opacity .4s linear, transform .65s var(--ease-settle);
  transition-delay: var(--hd, 0s);
}
.landing.herbs-in .herb {
  opacity: 1;
  transform: translate(calc(var(--dx, 0px) * var(--ringx, 1)), calc(var(--dy, 0px) * var(--ringy, 1))) scale(1) rotate(var(--hr, 0deg));
}
/* ring tiers, content-driven: 1024 is where the outer tags left the frame,
   760 where the herbs themselves did, 480 where only a vertical ellipse
   still clears the bottle. The custom properties inherit from .landing.
   2026-08-31 audit: the outer tags actually leave the frame from ~1230px
   down (lovor -36px / oxycedrus +64px measured at 1120x700), so a range
   tier bridges the gap to the 1024 one; the callout tiers further down
   (--cline) shorten the runs themselves. */
@media (min-width: 1025px) and (max-width: 1230px) {
  .landing { --ringx: .84; --ringy: .95; --rings: .9; }
}
@media (max-width: 1024px) {
  /* --ringx .8 -> .75 (2026-09-01): at .8 lovor's chip still crossed the
     left margin, the edge-clamp then wrapped it to 3 lines and the
     taller chip grew up into the copy block. .75 buys the ~17px that
     keeps it a 2-line chip. */
  .landing { --ringx: .75; --ringy: .85; --rings: .8; }
}
@media (max-width: 760px) {
  .landing { --ringx: .6; --ringy: .8; --rings: .66; }
}
@media (max-width: 480px) {
  .landing { --ringx: .45; --ringy: .75; --rings: .55; }
}
.herb .iw { display: block; }
.herb img {
  display: block;
  width: 100%;
  height: auto;
  /* popped out of the screen: soft one-sun shadow well below the cutout */
  filter: drop-shadow(24px 36px 26px rgba(34, 34, 31, .44));
  /* `backwards` is load-bearing (2026-08-29): the float's from-pose is
     translate(0,-7px) rotate(-1.6deg), and without a fill the img sits at
     transform:none through its staggered start delay (--hd*4, up to
     ~2.4s), then SNAPS into the from-pose when the delay expires - every
     herb visibly jolted ~7px one by one right after the pop-out settled
     ("they all come out, stop slightly, and then they all like jolt a few
     px away from their positions one by one"). With backwards each herb
     holds the float's starting pose from first paint. */
  animation: herb-float var(--hf, 7s) ease-in-out calc(var(--hd, 0s) * 4) infinite alternate backwards;
}
@keyframes herb-float {
  from { transform: translate(0, -7px) rotate(-1.6deg); }
  to { transform: translate(0, 9px) rotate(1.8deg); }
}
/* hover: jelly wobble (opus CTA feel) + technical-annotation callout.
   The wobble lives on the .iw wrapper (float keeps running on the img
   inside it) and is driven by a JS class removed on animationend, so
   leaving the herb mid-wobble never cancels it: it always settles. */
.landing.herbs-in .herb { pointer-events: auto; cursor: default; }
/* the hovered herb rises above the seam rocks (rock-field z3) so its
   annotation is never buried - the .herb's transform makes it a stacking
   context, which TRAPS .note's own z-index, so the lift must happen on the
   herb itself. Hover-only on purpose: at rest and during the pop-out the
   herbs stay z auto, i.e. still behind the flying bottle (z1, later <body>
   sibling), so the emerge-from-behind illusion is untouched. */
.landing.herbs-in:not(.alphahit) .herb:hover,
.landing.herbs-in .herb.hot { z-index: 4; }
.herb .iw.wob { animation: herb-wobble .7s var(--ease-settle); }
@keyframes herb-wobble {
  0% { transform: scale(1, 1); }
  22% { transform: scale(1.13, .87) rotate(-2.5deg); }
  44% { transform: scale(.9, 1.1) rotate(2.5deg); }
  64% { transform: scale(1.07, .95) rotate(-1.2deg); }
  82% { transform: scale(.97, 1.03); }
  100% { transform: scale(1, 1); }
}
/* callout: dot -> 45deg line -> horizontal line -> label. Extends AWAY
   from the bottle: default variant to the right, .cl mirrors left. */
.note {
  /* z4 in the ROOT stacking context (.herbs and .herb both z auto): the
     annotations must read over the seam rocks (rock-field z3). Over the
     bottle (z1) this is deliberate for komorac, whose callout points AT
     the bottle; the paper halo on .tag text keeps it legible on glass. */
  z-index: 4;
  position: absolute;
  top: 60%;
  left: calc(100% + 4px);
  pointer-events: none;
  /* the herb span is tilted by --hr; undo it so the annotation stays
     technical: true 45deg diagonal, true horizontal, level text */
  transform: rotate(calc(-1 * var(--hr, 0deg)));
  transform-origin: 0 0;
}
.herb.cl .note { left: auto; right: calc(100% + 4px); transform-origin: 100% 0; }
.note i { position: absolute; display: block; background: var(--bronze); }
.note .dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  translate: -50% -50%;
  transform: scale(0);
  transition: transform .18s var(--ease-settle);
}
.note .l45 {
  width: 46px;
  height: 1.5px;
  transform-origin: 0 50%;
  transform: rotate(45deg) scaleX(0);
  transition: transform .22s linear;
}
.herb.cl .note .l45 {
  right: 0;
  transform-origin: 100% 50%;
  transform: rotate(-45deg) scaleX(0);
}
.note .lh {
  left: 32px;
  top: 32.5px;
  /* --cline (2026-08-31 audit tiers below): shortens every horizontal
     connector on narrow desktops so the outer tags stay in the frame */
  width: calc(var(--lhw, 96px) * var(--cline, 1));
  height: 1.5px;
  transform-origin: 0 50%;
  transform: scaleX(0);
  transition: transform .24s linear;
}
.herb.cl .note .lh { left: auto; right: 32px; transform-origin: 100% 50%; }
/* .up: vertical mirror - the callout CLIMBS from the anchor instead of
   dropping (komorac, oxycedrus). --rise sets how high the diagonal climbs
   (default matches the stock 32.5px drop); komorac uses a tall rise so its
   tag lands beside the bottle NECK, where the glass is narrow enough to
   leave room for the text (Marko 2026-08-29). */
.herb.up .note .l45 {
  width: calc(var(--rise, 32.5px) * 1.414);
  transform: rotate(-45deg) scaleX(0);
}
.herb.up .note .lh {
  left: calc(var(--rise, 32.5px) - .5px);
  top: calc(-1 * var(--rise, 32.5px));
}
.herb.up .note .tag {
  left: calc(var(--rise, 32.5px) + 7.5px + var(--lhw, 96px) * var(--cline, 1));
  top: calc(-1 * var(--rise, 32.5px));
}
.note .tag {
  position: absolute;
  left: calc(40px + var(--lhw, 96px) * var(--cline, 1));
  top: 32.5px;
  translate: 0 -50%;
  max-width: 180px;
  opacity: 0;
  transform: translateY(5px);
  transition: opacity .25s linear, transform .25s var(--ease-settle);
  /* paper wash (Marko 2026-08-31: tags collide with the herb art and are
     hard to read - the halo alone does not carry small text over leaves).
     Same idiom as the hero ghost CTA, stronger because this is .74rem
     text on busy cutouts. On the bare cream ground the chip is invisible
     (paper on paper); it only materialises where art sits behind it. */
  background: rgba(250, 247, 240, .85);
  padding: .3rem .55rem;
  /* width: max-content is load-bearing at every width: the tag anchors
     to the 0-width .note, so shrink-to-fit collapses the box to its
     longest WORD (ruzmarin's one-word-per-line column). max-content
     sized + capped, names longer than the cap (both borovicas) wrap
     INSIDE the chip instead of overhanging it (Marko 2026-08-31). */
  width: max-content;
}
.herb.cl .note .tag { left: auto; right: calc(40px + var(--lhw, 96px) * var(--cline, 1)); text-align: right; }
/* paper halo: the maslina/oxycedrus tags can land on the seam rocks, and
   charcoal glyphs vanish against the dark grey stone faces. A stacked
   paper-tone text-shadow reads as nothing on the cream ground but keeps
   the letters legible over any boulder. */
.note .tag b, .note .tag small {
  text-shadow:
    0 0 2px var(--paper), 0 0 4px var(--paper), 0 0 8px var(--paper),
    0 0 14px var(--paper), 0 0 22px var(--paper);
}
.note .tag b {
  display: block;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: .92rem;
  letter-spacing: .08em;
}
.note .tag small {
  display: block;
  font-size: .74rem;
  line-height: 1.35;
  opacity: .75;
}
/* staggered draw on hover; collapses with no delay on leave.
   Every rule below pairs the raw :hover with .herb.hot: when JS arms
   alpha hit-testing (.landing.alphahit, fine pointers only, 2026-09-02)
   the raw box-hover is DISABLED and .hot - granted only over real
   foliage pixels - drives the callout. The transparent camera air around
   each cutout was triggering callouts from nowhere (Marko's "rogue
   line": a staged connector, mid-delay, pointing at nothing). The
   uniform :not() wrapper keeps every rule's specificity bump identical,
   so the .up/.cl source-order re-asserts below still resolve the same. */
.landing:not(.alphahit) .herb:hover .note .dot,
.herb.hot .note .dot { transform: scale(1); }
.landing:not(.alphahit) .herb:hover .note .l45,
.herb.hot .note .l45 { transform: rotate(45deg) scaleX(1); transition-delay: .12s; }
.landing:not(.alphahit) .herb.cl:hover .note .l45,
.herb.cl.hot .note .l45 { transform: rotate(-45deg) scaleX(1); }
/* .up's hover MUST re-assert rotate(-45deg) here, AFTER the generic rule:
   equal specificity means source order decides, and without this the
   diagonal flips downward on hover while .lh/.tag stay up - the chain
   visibly disconnects (shipped broken 2026-08-29, "line cant be seen"). */
.landing:not(.alphahit) .herb.up:hover .note .l45,
.herb.up.hot .note .l45 { transform: rotate(-45deg) scaleX(1); }
.landing:not(.alphahit) .herb:hover .note .lh,
.herb.hot .note .lh { transform: scaleX(1); transition-delay: .3s; }
.landing:not(.alphahit) .herb:hover .note .tag,
.herb.hot .note .tag { opacity: 1; transform: none; transition-delay: .5s; }
/* callout-length tiers (2026-08-31 audit, pairs with the 1025-1230 ring
   range tier): --cline shortens every horizontal connector, long names
   may wrap instead of forcing the run wider, and the tag cap drops to
   150px. Kept ABOVE the (hover: none) block so its 62vw cap and centred
   layout still win on touch. */
@media (max-width: 1230px) {
  .landing { --cline: .6; }
  .note .tag { max-width: 150px; }
}
@media (max-width: 1024px) {
  .landing { --cline: .35; }
}
/* touch devices (responsive pass 2026-08-29): the technical callout runs
   46-152px SIDEWAYS from each herb, which on a phone ring is straight off
   the screen, and there is no hover to draw it anyway - a tap sets sticky
   :hover, so the reveal still fires, but the SHAPE changes: dot on the
   herb, label directly beneath it, no lines. Gated on (hover: none), the
   input, not the width - a mouse tablet keeps the drawn callout, a touch
   tablet gets the compact one. The !importants beat the per-herb inline
   anchors (komorac/oxycedrus/maslina carry style="left..top.." tuned for
   the desktop line geometry). */
@media (hover: none) {
  .note { left: 50% !important; right: auto !important; top: 96% !important; }
  .note .l45, .note .lh { display: none; }
  .note .tag {
    left: 0 !important;
    right: auto !important;
    top: 14px;
    /* --tnx: viewport clamp nudge set from main.js - edge herbs on a
       phone ring pushed centred tags off the frame (up to +89px at
       360px wide, 2026-08-31 audit). Lines are hidden here, so a
       nudged tag disconnects nothing. */
    translate: calc(-50% + var(--tnx, 0px)) 0;
    text-align: center !important;
    max-width: 62vw;
  }
  .herb:hover .note .tag { transition-delay: .12s; }
}
.spin-bottle {
  position: relative;
  width: min(38vw, 48vh);
  aspect-ratio: 1200 / 1725; /* turntable frame aspect, do not change */
  justify-self: center;
  cursor: grab;
  touch-action: pan-y; /* vertical scroll stays free; horizontal drag spins */
  user-select: none;
}
.spin-bottle.dragging { cursor: grabbing; }
.spin-bottle.feature-active { cursor: default; }
.spin-bottle .poster { position: absolute; inset: 0; }
.spin-bottle canvas { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; }
/* the house one-sun shadow on the 3D renders themselves (Marko 2026-08-30:
   "add shadows on the 3d models to actually create depth"). filter follows
   the DRAWN alpha, so the shadow tracks the bottle at every angle and the
   feature takes cast their own as the cap/relief fly off - that separation
   IS the depth. Scoped by id: the loupe canvas must never cast one. */
#spin-canvas, #feature-canvas, .spin-bottle .poster {
  filter: drop-shadow(var(--sun-shadow));
}
.spin-bottle.ready canvas { opacity: 1; }
.spin-bottle.ready #feature-canvas {
  opacity: 0;
  pointer-events: none;
}
.spin-bottle.ready.feature-active #spin-canvas { opacity: 0; }
.spin-bottle.ready.feature-active #feature-canvas { opacity: 1; }
.spin-bottle.ready .poster { opacity: 0; }
.drag-hint {
  position: absolute;
  bottom: -2.2rem;
  left: 50%;
  translate: -50% 0;
  font-family: var(--font-mono);
  font-size: .72rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  opacity: .5;
  white-space: nowrap;
}
/* the loupe is hover-only (hidden on touch, see .lens), so the hint must
   not promise it there; touch gets its own line */
.drag-hint .hint-coarse { display: none; }
@media (hover: none) {
  .drag-hint .hint-fine { display: none; }
  .drag-hint .hint-coarse { display: inline; }
}
/* ---- inspection layout + feature controls ---- */
.spin-inspection {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(230px, 310px);
  align-items: center;
  gap: clamp(2rem, 5vw, 5.5rem);
  width: min(980px, 94vw);
}
.spin-viewer { justify-self: end; display: grid; justify-items: center; }
.bottle-features {
  display: grid;
  align-content: center;
  width: 100%;
  max-width: 310px;
}
.feature-kicker {
  margin-bottom: .75rem;
  font-family: var(--font-mono);
  font-size: .72rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--bronze-deep);
}
.bottle-feature {
  position: relative;
  appearance: none;
  width: 100%;
  min-height: 58px;
  padding: 1rem 1.8rem 1rem 0;
  border: 0;
  border-top: 1px solid rgba(143, 110, 92, .28);
  background: transparent;
  color: var(--charcoal);
  font: inherit;
  line-height: 1.35;
  text-align: left;
  cursor: pointer;
}
.bottle-feature:last-child { border-bottom: 1px solid rgba(143, 110, 92, .28); }
.bottle-feature::before {
  content: "";
  position: absolute;
  left: 0;
  right: 100%;
  top: -1px;
  height: 1px;
  background: var(--bronze);
  transition: right .38s var(--ease-settle);
}
.bottle-feature::after {
  content: "+";
  position: absolute;
  right: .1rem;
  top: 50%;
  translate: 0 -50%;
  color: var(--bronze-deep);
  transition: transform .3s var(--ease-settle), color .3s linear;
}
.bottle-feature:hover::before,
.bottle-feature:focus-visible::before,
.bottle-feature[aria-pressed="true"]::before { right: 0; }
.bottle-feature:hover,
.bottle-feature:focus-visible,
.bottle-feature[aria-pressed="true"] { color: var(--bronze-deep); }
.bottle-feature[aria-pressed="true"]::after {
  transform: rotate(45deg);
  color: var(--bronze);
}
.bottle-feature:focus-visible {
  outline: 1px solid var(--bronze);
  outline-offset: 4px;
}
@media (max-width: 900px) {
  .spin-inspection {
    grid-template-columns: 1fr;
    justify-items: center;
    gap: 3.4rem;
  }
  .spin-viewer { justify-self: center; }
  .bottle-features { max-width: min(480px, 86vw); }
}

/* ---- inner-label peek (3rd detail, 2026-09-01) ----
   The unrolled inner-label strip lays itself across the view at label
   height over the front-locked bottle: fade + rise + settle in the
   tilted-print grammar. transform (not the individual props) per
   standing decision 6. Sits before .lens in source so the loupe still
   paints above. */
.inner-peek {
  position: absolute;
  left: -6%;
  top: 44%;
  width: 112%;
  /* the print treatment (same desk as the dnevnik cards): paper mat +
     one-sun shadow, so the dark artwork reads as a physical print laid
     over the view, not a dark slab */
  padding: 9px 9px 11px;
  background: var(--paper);
  box-shadow: var(--sun-shadow-soft);
  opacity: 0;
  transform: translateY(22px) rotate(-3.5deg) scale(.98);
  transition: opacity .55s linear, transform .65s var(--ease-settle);
  pointer-events: none;
}
.inner-peek img { width: 100%; height: auto; display: block; }
.spin-bottle.inner-on .inner-peek {
  opacity: 1;
  transform: translateY(0) rotate(-3.5deg) scale(1);
}
@media (max-width: 760px) {
  /* the phone reveal is the rotated message card (see the <picture> in
     index.html): sized to the viewport, not the small bottle box, and
     centred on the screen with margin so the reveal transforms stay
     untouched */
  .inner-peek {
    width: 80vw;
    left: 50%;
    margin-left: -40vw;
    top: 32%;
  }
}

/* ---- the lens (loupe): magnifies only what it hovers ---- */
.lens {
  position: absolute;
  width: 190px;
  height: 190px;
  border-radius: 50%;
  border: 1px solid rgba(199, 163, 135, .8);
  box-shadow: 0 10px 30px rgba(0, 0, 0, .35), inset 0 0 0 1px rgba(34, 34, 31, .3);
  overflow: hidden;
  pointer-events: none;
  opacity: 0;
  transition: opacity .25s var(--ease-settle);
  translate: -50% -50%;
  z-index: 5;
  background: var(--charcoal-soft);
}
.lens canvas { width: 100%; height: 100%; display: block; }
.spin-bottle:hover .lens.on { opacity: 1; }
@media (hover: none) { .lens { display: none; } }

/* ---- sun dial: rotation control, centred = front ---- */
.sundial {
  position: relative;
  width: min(30vw, 340px);
  height: 2.6rem;
  margin-top: 1.6rem;
  cursor: pointer;
  touch-action: none;
}
/* phones: 30vw of 390 is a 117px slider - unusable with a finger. Wider
   track, and a hair taller so the whole strip clears the 44px touch floor. */
@media (max-width: 760px) {
  .sundial { width: min(74vw, 340px); height: 2.8rem; }
}
.sundial .track {
  position: absolute;
  left: 0; right: 0; top: 50%;
  height: 1px;
  background: rgba(199, 163, 135, .35);
}
.sundial .track::before, .sundial .track::after {
  content: "";
  position: absolute;
  top: -3px;
  width: 1px;
  height: 7px;
  background: rgba(199, 163, 135, .5);
}
.sundial .track::before { left: 0; }
.sundial .track::after { right: 0; }
.sundial .sun {
  position: absolute;
  left: 50%;
  top: 50%;
  translate: -50% -50%;
  color: var(--bronze);
  cursor: grab;
  transition: color .3s var(--ease-settle);
}
.sundial .sun:hover { color: var(--paper); }
.sundial.dragging .sun { cursor: grabbing; }
.sundial .sun svg { display: block; }

/* ---------- section: berba (03, Larisa's harvest) ---------- */
/* photos from the client's professional shoot: the olive-grove walk is
   the wide anchor, the overhead picking shot tucks under it, offset like
   two prints laid on the same table. Same one-sun shadow system. */
.berba-photos { position: relative; padding-bottom: clamp(3rem, 8vh, 6rem); }
.berba-photos img { height: auto; }
.berba-photos .ph-grove {
  width: 100%;
  box-shadow: var(--sun-shadow-soft);
}
.berba-photos .ph-pick {
  position: absolute;
  width: 52%;
  right: -4%;
  bottom: 0;
  box-shadow: var(--sun-shadow);
}
@media (max-width: 760px) {
  .berba-photos .ph-pick { width: 62%; right: 0; }
}

/* ---------- section: stina (05) ---------- */
/* the rock field hangs off both edges; clip x only, same contract as the
   old .spin clip (the crowns rise above the seam onto 04 untouched) */
.stone { overflow-x: clip; }
.stone > .stone-center { position: relative; z-index: 2; }

/* ---------- chapters (shared grid) ---------- */
.ch-grid {
  display: grid;
  grid-template-columns: 1fr 1.2fr;
  gap: clamp(1.5rem, 4vw, 3.5rem);
  align-items: center;
}
.ch-lead { margin-bottom: 2rem; }
@media (max-width: 760px) {
  .ch-grid { grid-template-columns: 1fr; }
  .spin-bottle { width: min(60vw, 44vh); }
  /* the old `--hw: 90px !important` herb floor is gone: it shrank the
     cutouts but left the px offsets at desktop reach, so herbs sat
     off-screen anyway. The ring tiers above own small screens now. */
}

/* macro pair inside #stina (was the 778 summit band; restructure
   2026-08-29 moved the cap + relief content up here and retired the
   numeral - 778 lives at the top of the page now) */
.summit-macros { margin-top: 2.4rem; align-items: stretch; width: min(1100px, 100%); }

/* ---------- section: wipe (06) - label compare slider ---------- */
.wipe { text-align: center; align-items: center; }
.label-compare {
  position: relative;
  width: min(70rem, 100%);
  aspect-ratio: 2800 / 959;
  margin-top: clamp(1.6rem, 4vh, 2.6rem);
  box-shadow: var(--sun-shadow);
  overflow: clip;
  touch-action: pan-y; /* vertical scroll stays free; horizontal drag slides */
  cursor: ew-resize;
  user-select: none;
  -webkit-user-select: none;
}
.label-compare img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  pointer-events: none;
}
.lc-front { position: absolute; }
/* the inner strip pane clips to the LEFT of the divider: divider fully
   left = front only, fully right = inner only (Marko's mapping) */
.lc-back {
  position: absolute;
  inset: 0;
  clip-path: inset(0 calc(100% - var(--lcx, 50%)) 0 0);
}
.lc-divider {
  position: absolute;
  top: 0;
  bottom: 0;
  left: var(--lcx, 50%);
  width: 2px;
  margin-left: -1px;
  background: var(--bronze);
}
.lc-handle {
  position: absolute;
  top: 50%;
  left: 50%;
  /* transform shorthand, never the individual props (decision 6) */
  transform: translate(-50%, -50%);
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--paper);
  color: var(--charcoal);
  box-shadow: 0 6px 18px rgba(0, 0, 0, .4);
}

/* ---------- section: sampion (07, award scroll deck) ---------- */
.sampion { text-align: center; align-items: center; }
/* Base (NO-JS / reduced motion) = the complete resting page: awards as
   stacked alternating rows, everything visible. JS adds .deck-on for the
   full-screen sticky crossfade deck (Marko 2026-09-02). */
.award-deck { width: 100%; }
.a-slide {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(2rem, 6vw, 5rem);
  align-items: center;
  width: min(64rem, 100%);
  margin: clamp(2.2rem, 5vh, 3.4rem) auto 0;
  text-align: left;
}
/* alternation: copy left / image right by default, .flip mirrors */
.a-slide.flip .a-media { order: -1; }
.a-copy .a-year {
  font-family: var(--font-mono);
  font-size: .74rem;
  letter-spacing: .16em;
  color: var(--bronze-deep);
  margin-bottom: .45rem;
}
.a-copy h3 {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(1.3rem, 2.4vw, 1.9rem);
  letter-spacing: .05em;
  margin-bottom: .6rem;
}
.a-copy p { font-size: .95rem; line-height: 1.65; opacity: .78; max-width: 36ch; }
.a-media { margin: 0; display: flex; justify-content: center; }
/* matted print, same physical language as the inner-label peek. The
   frame shrinks to each photo: the award images are the client's own
   announcement visuals in mixed aspects (squares and portraits), and
   cropping them to one ratio loses medal text - so no object-fit cover,
   the photo keeps its shape and the height cap does the sizing. */
.a-frame {
  width: fit-content;
  padding: 10px;
  background: var(--paper);
  box-shadow: var(--sun-shadow-soft);
}
.a-frame .a-photo {
  display: block;
  max-width: 100%;
  width: auto;
  height: auto;
  max-height: min(52vh, 560px);
}
@media (max-width: 760px) {
  .a-slide { grid-template-columns: 1fr; gap: 1.3rem; }
  /* image above its text on phones, both variants */
  .a-media, .a-slide.flip .a-media { order: -1; }
  .a-copy p { max-width: none; }
}

/* the carousel (2026-09-02, replaced the same-day scroll deck - it cost
   ~7 screens of scroll between the label and the shop): ONE screen,
   click/swipe driven. Slides share a grid cell so the track auto-sizes
   to the tallest slide; .active crossfades in with the same side-drift
   language the deck had (image from its own side, copy countering). */
.car-on .car-track { display: grid; width: 100%; }
.car-on .car-track .a-slide {
  grid-area: 1 / 1;
  margin: clamp(2.2rem, 5vh, 3.4rem) auto 0;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity .4s var(--ease-settle), visibility 0s .4s;
}
.car-on .car-track .a-slide.active {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transition: opacity .45s var(--ease-settle) .08s;
}
.car-on .a-slide .a-media { transform: translateX(34px); transition: transform .55s var(--ease-settle); }
.car-on .a-slide.flip .a-media { transform: translateX(-34px); }
.car-on .a-slide .a-copy { transform: translateX(-20px); transition: transform .55s var(--ease-settle); }
.car-on .a-slide.flip .a-copy { transform: translateX(20px); }
.car-on .a-slide.active .a-media, .car-on .a-slide.active .a-copy { transform: none; }
.car-controls[hidden] { display: none; }
.car-controls {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1.4rem;
  margin-top: clamp(1.4rem, 3vh, 2.2rem);
}
.car-prev, .car-next {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  background: transparent;
  border: 1px solid var(--bronze);
  color: var(--charcoal);
  cursor: pointer;
  transition: background .4s var(--ease-settle);
}
.car-prev:hover, .car-next:hover { background: rgba(199, 163, 135, .18); }
.car-count {
  font-family: var(--font-mono);
  font-size: .72rem;
  letter-spacing: .16em;
  color: var(--bronze-deep);
  min-width: 3.6em;
  text-align: center;
}
@media (max-width: 760px) {
  .car-on .a-frame .a-photo { max-height: min(38vh, 82vw); }
}
@media (prefers-reduced-motion: reduce) {
  .car-on .car-track .a-slide,
  .car-on .a-slide .a-media,
  .car-on .a-slide .a-copy { transition: none; }
}

/* ---------- section: shop (08, no hard sell here) ---------- */
/* overflow clip: on phones the side slots of the wheel peek past the
   viewport edge; clip at the section so the page never scrolls sideways.
   Nothing inside 08 overhangs its bounds (seams live in the neighbours). */
.shop { text-align: center; align-items: center; overflow: clip; }
/* cracked-slate bed: the brand's own IG stone, dark stage baked into
   the asset where the content stands. Positioned children paint above. */
.shop-wall {
  position: absolute;
  inset: 0;
  background: url("/assets/shop-stone.webp") center / cover no-repeat;
  pointer-events: none;
  -webkit-mask-image: linear-gradient(180deg, transparent 0, #000 14%, #000 84%, transparent 100%);
  mask-image: linear-gradient(180deg, transparent 0, #000 14%, #000 84%, transparent 100%);
}
.shop > .eyebrow, .shop > h2, .shop > .formats { position: relative; }
/* Ferris-wheel stage (Marko 2026-09-01): three slots on a circle seen
   from the front - front slot at true size, the two behind smaller,
   lifted and dimmed. These custom props ARE the wheel geometry;
   scripts/main.js derives the spin arc from the resolved values of
   .pos-right, so geometry is edited HERE only, never in JS numbers. */
.formats {
  position: relative;
  /* contain the slot z-indexes (50/20 below): they only order the three
     bottles among THEMSELVES. Without this they fought the fixed
     .site-header (also z 50) in the root context and the 0,7 neck
     painted over the pill nav (Marko 2026-09-03 screenshot). */
  isolation: isolate;
  width: 100%;
  /* front card total: image row + name + price + quick-add button */
  height: calc(min(54vh, 470px) + 8rem);
  margin-top: 2.6rem;
  --wheel-x: clamp(150px, 26vw, 380px); /* side-slot offset from centre */
  --wheel-y: -52px;                     /* side-slot lift (behind = higher) */
  --wheel-s: .72;                       /* side-slot scale */
}
.format {
  position: absolute;
  left: 50%;
  bottom: 0;
  /* abs-pos shrink-to-fit collapses to the longest word on a 0-width
     anchor (same trap as the herb callouts) - pin the intrinsic width */
  width: max-content;
  text-align: center;
  cursor: pointer;
  transform-origin: 50% 100%;
}
.format.pos-front { transform: translate(-50%, 0); z-index: 50; }
.format.pos-right {
  transform: translate(calc(-50% + var(--wheel-x)), var(--wheel-y)) scale(var(--wheel-s));
  z-index: 20;
  filter: brightness(.72);
}
.format.pos-left {
  transform: translate(calc(-50% - var(--wheel-x)), var(--wheel-y)) scale(var(--wheel-s));
  z-index: 20;
  filter: brightness(.72);
}
.format.pos-left:hover, .format.pos-right:hover { filter: brightness(.88); }
/* price + quick-add belong to the front slot only; visibility (not
   display) so the card keeps its height and the ground line holds */
.format .price, .format [data-add] {
  transition: opacity .35s var(--ease-settle), visibility 0s .35s;
}
.format.pos-left .price, .format.pos-right .price,
.format.pos-left [data-add], .format.pos-right [data-add] {
  opacity: 0;
  visibility: hidden;
}
.format .f-link {
  display: grid;
  grid-template-rows: min(54vh, 470px) auto auto;
}
.format img {
  grid-row: 1;
  align-self: end;
  justify-self: center;
  margin: 0 auto;
  width: auto;
  max-width: none;
  filter: drop-shadow(var(--sun-shadow-soft));
}
/* Physical scale is calibrated from the shared stone cap: it is the same
   real-world part on all three bottles. The small renders contain a cap
   almost exactly 1.946x wider in pixels than the 0,7 render, so their
   camera canvases cannot be compared at equal CSS height. */
.format.f07 img {
  height: min(57vh, 500px);
  /* The 0.7 L turntable has a taller transparent camera canvas than the
     retired static cutout. Normalize it once, from the bottom edge, so
     frame changes cannot shrink the bottle or move its visible baseline. */
  transform: translateY(4.47%) scale(1.243);
  transform-origin: 50% 100%;
}
.format.f02 img,
.format.f01 img { height: min(36.45vh, 319px); }
.format.f02 img {
  transform: translateY(6.05%);
  transform-origin: 50% 100%;
}
.format.f01 img {
  transform: translateY(18.54%);
  transform-origin: 50% 100%;
}
/* Phones keep the same cap-calibrated ratio in vw units. The transparent
   canvas remains bottom-aligned, so every frame keeps one ground line. */
@media (max-width: 600px) {
  .formats {
    height: calc(min(51vw, 300px) + 7.5rem);
    --wheel-x: 33vw;
    --wheel-y: -32px;
    --wheel-s: .68;
  }
  .format .f-link { grid-template-rows: min(51vw, 300px) auto auto; }
  .format.f07 img { height: min(54vw, 318px); }
  .format.f02 img,
  .format.f01 img { height: min(34.5vw, 203px); }
}
.format .name { font-family: var(--font-display); letter-spacing: .1em; margin-top: 1rem; }

/* carved buttons: sharp corners, bronze as light */
.cta-row { display: flex; gap: 1rem; justify-content: center; flex-wrap: wrap; }
.btn {
  font-family: var(--font-display);
  font-size: .95rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  text-decoration: none;
  padding: 1rem 2.4rem;
  border: 1px solid var(--bronze);
  color: var(--charcoal);
  background: transparent;
  cursor: pointer;
  transition: background .4s var(--ease-settle), color .4s var(--ease-settle);
}
.btn.primary { background: var(--bronze); color: var(--charcoal); }
.btn.primary:hover { background: var(--paper); }
.btn.ghost:hover { background: rgba(199, 163, 135, .18); }
.btn:focus-visible { outline: 2px solid var(--bronze); outline-offset: 3px; }
.dark .btn, .dark-2 .btn { color: var(--paper); }
.dark .btn.primary, .dark-2 .btn.primary { color: var(--charcoal); }

/* ---------- section: horizon close (09, the departure) ---------- */
.horizon { position: relative; min-height: 150vh; overflow: clip; }
/* svh double declaration: on iOS 100vh is the LARGE viewport, so the
   bottom-anchored caption sat behind the toolbar for the whole idle scene.
   svh is the always-visible height; browsers without it keep the vh line.
   svh and not dvh on purpose - dvh resizes WHILE the toolbar collapses,
   which would pump the sticky stage mid-parallax. */
.horizon-stage { position: sticky; top: 0; height: 100vh; height: 100svh; overflow: clip; }
.horizon .sky, .horizon .sea {
  position: absolute;
  inset: 0;
  background-image: var(--plate);
  background-size: cover;
  background-position: center;
}
.horizon .sky { z-index: 0; }
.horizon .sea {
  z-index: 2;
  -webkit-mask-image: var(--plate-mask);
  mask-image: var(--plate-mask);
  -webkit-mask-size: cover;
  mask-size: cover;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
}
.horizon .monogram {
  position: absolute;
  /* No `translate:` property here (decision 6 - stacking-context trap)
     and no transform centering: JS drift owns inline transform
     (translateY only), so left/top do all the placement. Top edge is
     computed directly - the anchor line minus 68% of the glyph height
     (gold monogram aspect ~2.19) - with a hard 150px floor so the
     wordmark can never ride up under the fixed pill nav (it did, on
     every viewport, when this was translate -80%). */
  --mw: min(46vw, 760px);
  width: var(--mw);
  left: calc(50% - var(--mw) / 2);
  top: max(150px, calc(var(--anchor, 43%) - var(--mw) * .31));
  z-index: 1;
  will-change: transform;
}
.horizon .monogram img { width: 100%; opacity: .85; }
/* dusk scrim: the photo's bright sea/beach band made every bottom-stack
   glyph unreadable (Marko screenshot 2026-09-02). Above sea (2), below
   the bottle (4) and footer (5); the glass stays clear of it. */
.horizon-stage::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  background: linear-gradient(180deg, rgba(22, 21, 18, 0) 46%, rgba(22, 21, 18, .38) 72%, rgba(22, 21, 18, .66) 100%);
}
.horizon .h-bottle {
  position: absolute;
  left: var(--bottle-x, 50%); /* off-centre: the spit stays visible */
  bottom: 13vh;
  translate: -50% 0;
  z-index: 4;
}
.horizon .h-bottle {
  height: min(56vh, 520px);
  aspect-ratio: 1200 / 1725;
}
.horizon .h-bottle .poster,
.horizon .h-bottle canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  filter: drop-shadow(var(--sun-shadow));
  /* true transparent glass: 240-frame alpha turntable (2026-08-25) */
}
.horizon .h-bottle canvas { opacity: 0; }
.horizon .h-bottle.ready canvas { opacity: 1; }
.horizon .h-bottle.ready .poster { opacity: 0; }
/* portrait phones: smaller bottle, kept off-centre so the Zlatni rat
   spit stays visible (the section's one rule); monogram widened so it
   still reads on the narrow crop. (The old right-side caption block is
   gone - CTAs live in the footer stack now.) */
@media (max-width: 760px) {
  .horizon .h-bottle { height: min(40vh, 340px); bottom: 11vh; }
  .horizon .monogram { --mw: min(78vw, 880px); }
}

/* ---------- section: newsletter (09, inner label) ----------
   The closing surface IS the inner-label artwork: paper grain, faint
   compass rose, Brac silhouette with the 778 peak. Cover-cropped so
   the island anchors the middle of the band; the form prints on it.
   Swap 2026-08-31: the footer moved onto the horizon close (10), so
   this is a shorter band now, form centred on the artwork. It follows
   the dark shop directly (same charcoal base, no tear - a dark tear on
   dark paper reads as nothing); its own down-tear hangs over the
   horizon photograph below. */
.inner-label {
  /* positioned so its down-tear can hang past its bottom edge, over the
     horizon photograph */
  position: relative;
  min-height: 64vh;
  display: flex;
  flex-direction: column;
  /* flex-start, not center: centring parked the artwork's 778/island
     art under the e-mail input. Form on top, island art below it. */
  justify-content: flex-start;
  background-color: var(--charcoal);
  background-image: url("/assets/label-inner.webp");
  /* the webp is the retouched label-inner-notagline.png: the artwork's
     right-side script text is patched out, so cover can show the full
     height (compass complete) and crop only the sides on any viewport */
  background-size: cover;
  background-position: center;
  color: var(--stone);
}
.inner-label .closing {
  text-align: center;
  padding: 4.5rem 1.5rem;
}
.inner-label .closing h2 { color: var(--paper); }
.closing form {
  display: flex;
  gap: .6rem;
  flex-wrap: wrap;
  justify-content: center;
  margin-top: 1.4rem;
}
.closing input[type="email"] {
  font-family: var(--font-body);
  font-size: 1rem;
  padding: .9rem 1.2rem;
  border: 1px solid var(--stone-shadow);
  background: rgba(34, 34, 31, .45); /* keeps the field legible over the grain */
  color: var(--paper);
  min-width: min(20rem, 70vw);
}
/* the UA's blue focus ring is the one foreign object on the label art;
   focus speaks bronze like every other control */
.closing input[type="email"]:focus,
.closing input[type="email"]:focus-visible {
  outline: 2px solid var(--bronze);
  outline-offset: 2px;
  border-color: var(--bronze);
}
.site-footer {
  background: none; /* the inner-label artwork is the surface */
  color: var(--stone);
  text-align: center;
  padding: 4rem 1.5rem 2.5rem;
}
.site-footer .signature {
  font-family: var(--font-display);
  color: var(--paper);
  letter-spacing: .06em;
  margin-bottom: 1.6rem;
}
.site-footer nav { display: flex; gap: 1.6rem; justify-content: center; flex-wrap: wrap; margin-bottom: 1.6rem; }
.site-footer a { color: var(--stone); text-decoration: none; font-size: .9rem; }
.site-footer a:hover { color: var(--bronze); }
.site-footer .fine { font-size: .78rem; opacity: .7; }

/* ---------- footer ON the horizon close (swap 2026-08-31) ----------
   The departure scene is the page end now; the footer prints over the
   sea at the very bottom of the sticky stage, in the clear water band
   under the idle bottle (which rests at 13vh / 17vh mobile). Base
   .site-footer above still dresses the subpages' inner-label footers -
   override here, never edit the base. */
.horizon .site-footer {
  position: absolute;
  z-index: 5;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 0 1.5rem 1.4rem;
  text-shadow: 0 1px 8px rgba(34, 34, 31, .5);
}
/* the eyebrow + CTAs lead the footer stack (2026-09-02): the final ask
   is centred on the scrim, not floating over open water */
.horizon .site-footer .eyebrow { color: var(--paper); opacity: .9; }
.horizon .site-footer .cta-row { margin-bottom: clamp(1.2rem, 3vh, 2rem); }
.horizon .btn.ghost { color: var(--paper); }
.horizon .site-footer .signature { margin-bottom: .8rem; }
.horizon .site-footer nav { margin-bottom: .8rem; gap: 1.3rem; }
/* the footer owns the bottom band of the scene now: lift the bottle
   clear of it (it sat at 13vh when the scene ended the section without
   a footer) */
.horizon .h-bottle { bottom: 16vh; }
@media (max-width: 760px) {
  /* phone: the footer stack owns the bottom ~45vh and the sea band sits
     exactly behind it - every bottle position either covered the CTAs or
     floated over the mountains (tried 34vh and 48vh, both wrong). The
     idle bottle sits this scene out on phones; the departure is island +
     monogram + the ask. Desktop keeps the full scene. */
  .horizon .h-bottle { display: none; }
  .horizon .site-footer { padding-bottom: max(1rem, env(safe-area-inset-bottom)); }
  .horizon .site-footer nav { gap: .7rem 1.1rem; margin-bottom: .7rem; }
  .horizon .site-footer .signature { font-size: .95rem; }
  .horizon .site-footer .cta-row { gap: .7rem; }
}

@media (prefers-reduced-motion: reduce) {
  .horizon .monogram { transform: none !important; }
  html { scroll-behavior: auto; }
}

/* newsletter capture additions (2026-09-03): consent row + status +
   honeypot. The consent checkbox is REQUIRED and unticked by design. */
.nl-hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  opacity: 0;
}
.nl-consent {
  display: flex;
  gap: .55rem;
  align-items: flex-start;
  width: 100%;
  justify-content: center;
  margin-top: .9rem;
  font-size: .82rem;
  line-height: 1.5;
  color: var(--paper);
  opacity: .85;
  cursor: pointer;
  text-align: left;
}
.nl-consent input {
  margin-top: .2rem;
  accent-color: var(--bronze);
}
.nl-consent a { color: var(--bronze); }
.nl-consent span { max-width: 26rem; }
.nl-status {
  width: 100%;
  text-align: center;
  font-size: .9rem;
  color: var(--bronze);
  margin: .8rem 0 0;
}
.nl-status[hidden] { display: none; }
