/* =================================================================
   SITE — homepage styles, layered on top of intro.css.

   Loaded AFTER intro.css. Owns the header, the scrollable layout,
   the mobile drawer, and the .in-header final state for the logo.

   Visibility model: body has data-stage flipped from "intro" → "site"
   by site.js when intro.js dispatches talkbg:intro-complete. After
   the logo dock animation lands (~1.0s into the timeline), site.js
   also adds html.scroll-ready, which triggers the staggered reveal
   of the header chrome (nav, CTA, log-in / hamburger). The hero
   content rises in at the same moment via GSAP.

   So the sequence the user sees is:
     1. Header glass appears (warm, brighter, no border).
     2. Logo glides from centre → header slot (1.1s).
     3. AS LOGO LANDS: nav + actions + hero content fade up in concert.
   ================================================================= */

.site {
  position: relative;
  inset: auto;
  z-index: 1;
  background: transparent;
  min-height: 100vh;
  opacity: 0;
  transition: opacity 0.9s var(--ease-flow);
}
body[data-stage="site"] .site,
.site.ready { opacity: 1; }

html.scroll-ready,
html.scroll-ready body {
  overflow-x: hidden;
  overflow-y: auto;
  height: auto;
  width: 100%;
}

/* ===================================================================
   HEADER — fixed bar with a PERSISTENT warmer glass backdrop.
   No bottom border. Brighter than v4. Hidden during intro via
   opacity 0 + translateY(-100%); revealed when body[data-stage="site"].
   =================================================================== */
.site-header {
  position: fixed;
  top: 0; left: 0; right: 0;
  height: 100px;
  z-index: 1400;
  display: grid;
  /* Symmetric layout: logo slot left · nav centered · empty slot right.
     The right 300px column intentionally has no child — it mirrors
     the logo column so the nav stays viewport-centered. */
  grid-template-columns: 300px 1fr 300px;
  align-items: center;
  padding: 0 48px;
  gap: 32px;

  /* Translucent white. Constant 50% — page elements bleed through
     consistently at rest and during scroll. */
  background: rgba(255, 255, 255, 0.50);
  -webkit-backdrop-filter: blur(24px) saturate(150%);
          backdrop-filter: blur(24px) saturate(150%);

  opacity: 0;
  transform: translateY(-100%);
  pointer-events: none;
  transition: opacity 0.7s var(--ease-flow),
              transform 0.7s var(--ease-flow);
}
body[data-stage="site"] .site-header {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}

/* Reserves grid space where the logo lands. The real <img> is in
   #logoOverlay (top-level, position: fixed) — this slot just keeps
   the grid balanced. */
.header-logo-slot {
  width: 100%;
  height: 100%;
}

/* ===================================================================
   HEADER CHROME REVEAL — nav and hamburger are hidden until the
   logo dock has landed. site.js adds html.scroll-ready ~1.0s into
   the dock timeline, which kicks off this fade-in.
   =================================================================== */
.primary-nav,
.nav-toggle {
  opacity: 0;
  transform: translateY(-8px);
  pointer-events: none;
  transition: opacity 0.55s var(--ease-flow),
              transform 0.55s var(--ease-flow);
}
html.scroll-ready .primary-nav,
html.scroll-ready .nav-toggle {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}

/* ----- Primary navigation — black text on translucent white ----- */
.primary-nav {
  justify-self: center;
  grid-column: 2;
}
.primary-nav ul {
  display: flex;
  gap: 40px;
  list-style: none;
  padding: 0;
  margin: 0;
}
.primary-nav a {
  font-family: 'Onest', 'Geist', sans-serif;
  font-size: 12.5px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: #0a0a0b;
  text-decoration: none;
  transition: color 0.25s ease;
  position: relative;
  padding: 6px 0;
}
/* Hover keeps the amber dot + underline so the brand language reads
   even on the white bar — text stays black. */
.primary-nav a::before {
  content: '';
  position: absolute;
  left: -14px; top: 50%;
  width: 4px; height: 4px;
  border-radius: 50%;
  background: var(--accent);
  opacity: 0;
  transform: translate(-50%, -50%) scale(0.4);
  transition: opacity 0.3s var(--ease-flow),
              transform 0.3s var(--ease-flow);
}
.primary-nav a::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 1px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform 0.3s var(--ease-flow);
}
.primary-nav a:hover::before {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
}
.primary-nav a:hover::after { transform: scaleX(1); }

.nav-toggle { display: none; }

/* ===================================================================
   LOGO IN HEADER — container vertically centered inside the 100px bar
   on desktop (top calculated as (header - container)/2). On mobile,
   the logo is HORIZONTALLY CENTERED via left:0/right:0 + margin auto
   (no CSS transform — keeps GSAP's tween-end transform from fighting
   the centering).
   =================================================================== */
.logo-overlay.in-header {
  top: 0;
  left: 24px;
  width: 280px;
  height: 100px;
  aspect-ratio: auto;
  transform: none;
  opacity: 1;
  pointer-events: auto;
}

/* ===================================================================
   MOBILE — header collapses below 900px. Layout is [ . | hamburger ]
   with the logo overlay (position: fixed) horizontally centered
   independently via left:0/right:0 + margin auto. Hamburger black
   so it reads against the white header.
   =================================================================== */
@media (max-width: 900px) {
  .site-header {
    grid-template-columns: 1fr auto;
    padding: 0 18px;
    gap: 12px;
    height: 92px;
  }
  .primary-nav { display: none; }

  .nav-toggle {
    display: flex;
    flex-direction: column;
    gap: 5px;
    width: 44px;
    height: 44px;
    background: rgba(10, 10, 11, 0.04);
    border: 1px solid rgba(10, 10, 11, 0.16);
    cursor: pointer;
    padding: 0;
    justify-content: center;
    align-items: center;
    border-radius: 12px;
    transition: background 0.2s ease, border-color 0.2s ease;
    justify-self: end;
  }
  .nav-toggle:hover {
    background: rgba(10, 10, 11, 0.08);
    border-color: rgba(10, 10, 11, 0.28);
  }
  .nav-toggle span {
    display: block;
    width: 22px;
    height: 1.5px;
    background: #0a0a0b;
    border-radius: 2px;
    transition: transform 0.3s ease, opacity 0.3s ease;
  }
  .nav-toggle[aria-expanded="true"] span:nth-child(1) {
    transform: translateY(6.5px) rotate(45deg);
  }
  .nav-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
  .nav-toggle[aria-expanded="true"] span:nth-child(3) {
    transform: translateY(-6.5px) rotate(-45deg);
  }

  /* Logo CENTERED horizontally on mobile. Using left:0/right:0 +
     margin auto keeps centering layout-based, so GSAP's tween-end
     transform doesn't break the centering. */
  .logo-overlay.in-header {
    top: 0;
    left: 0;
    right: 0;
    margin-left: auto;
    margin-right: auto;
    width: 240px;
    height: 92px;
  }
}

/* ===================================================================
   MOBILE DRAWER — full-screen overlay with a visible X close button.
   =================================================================== */
.mobile-drawer {
  position: fixed;
  inset: 0;
  background: radial-gradient(ellipse 100vw 80vh at 70% 20%,
                              rgba(40, 28, 18, 0.94) 0%,
                              rgba(8, 7, 9, 0.98) 70%);
  -webkit-backdrop-filter: blur(28px) saturate(140%);
          backdrop-filter: blur(28px) saturate(140%);
  transform: translateX(100%);
  transition: transform 0.5s cubic-bezier(0.65, 0, 0.05, 1);
  z-index: 1450;
  padding: 110px 28px 32px;
  pointer-events: none;
  display: none;
}
.mobile-drawer.open { transform: translateX(0); pointer-events: auto; }

/* When the drawer is open, hide the persistent logo overlay. It has a
   higher z-index than the drawer (set in intro.css to survive the
   intro fade-out) and would otherwise bleed through the drawer.
   !important + visibility:hidden because intro.js can set inline
   opacity on the overlay (calibration / endIntro paths) and we must
   guarantee the hide wins regardless. */
html.drawer-open .logo-overlay {
  opacity: 0 !important;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.25s var(--ease-flow);
}

@media (max-width: 900px) {
  .mobile-drawer { display: block; }

  .drawer-close {
    position: absolute;
    top: 20px; right: 18px;
    width: 46px; height: 46px;
    background: rgba(212, 163, 115, 0.12);
    border: 1px solid rgba(212, 163, 115, 0.28);
    border-radius: 50%;
    color: var(--accent);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    transition: background 0.25s ease, transform 0.25s ease;
  }
  .drawer-close:hover {
    background: rgba(212, 163, 115, 0.24);
    transform: scale(1.06);
  }
  .drawer-close svg {
    width: 18px;
    height: 18px;
    display: block;
  }

  .mobile-drawer nav {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    height: 100%;
  }
  .mobile-drawer nav ul {
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: 40px;
    padding: 0;
    margin: 0;
    text-align: center;
  }
  .mobile-drawer nav a {
    font-family: 'Onest', 'Geist', sans-serif;
    color: var(--ink);
    text-decoration: none;
    font-size: 32px;
    font-weight: 500;
    letter-spacing: -0.015em;
    transition: color 0.25s ease;
    display: inline-block;
    position: relative;
    padding-bottom: 8px;
  }
  /* Constant amber underline — same line that fades in on desktop
     hover, but permanently visible on mobile per request. */
  .mobile-drawer nav a::after {
    content: '';
    position: absolute;
    left: 0; right: 0; bottom: 0;
    height: 1px;
    background: var(--accent);
  }
  .mobile-drawer nav a:hover { color: var(--accent); }
}

/* ===================================================================
   AMBIENT PAGE LIGHTING — the warm paper texture (multi-layer) the
   first iteration shipped with, restored verbatim. Lives OUTSIDE
   .site (as a body-level sibling) so the GSAP filter:blur applied to
   .site during the reveal doesn't snap it on clearProps.
     1. .page-texture — five composited layers: vignette + linen
        weave + low-freq warm cloud + warm-umber grain + cream
        highlight grain. Subtle 28s breathing via background-position
        on the tiled layers.
     2. .site::before — two original warm radials: amber bulb
        upper-right + deep ember lower-left, screen-blended.
   =================================================================== */
.page-texture {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-image:
    radial-gradient(140vw 100vh at 50% 35%,
                    transparent 0%,
                    transparent 58%,
                    rgba(94, 64, 38, 0.10) 100%),
    url("data:image/svg+xml;utf8,<svg viewBox='0 0 14 14' xmlns='http://www.w3.org/2000/svg'><path d='M0 7 L14 7 M7 0 L7 14' stroke='rgba(85,58,38,0.055)' stroke-width='0.3'/><path d='M0 3 L14 3 M0 11 L14 11' stroke='rgba(255,250,242,0.045)' stroke-width='0.3'/></svg>"),
    url("data:image/svg+xml;utf8,<svg viewBox='0 0 600 600' xmlns='http://www.w3.org/2000/svg'><filter id='f'><feTurbulence type='fractalNoise' baseFrequency='0.014' numOctaves='2' seed='3' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0.33  0 0 0 0 0.23  0 0 0 0 0.15  0 0 0 0.32 0'/></filter><rect width='100%25' height='100%25' filter='url(%23f)'/></svg>"),
    url("data:image/svg+xml;utf8,<svg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'><filter id='g'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' seed='7' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0.27  0 0 0 0 0.18  0 0 0 0 0.12  0 0 0 0.50 0'/></filter><rect width='100%25' height='100%25' filter='url(%23g)'/></svg>"),
    url("data:image/svg+xml;utf8,<svg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'><filter id='h'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' seed='13' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1  0 0 0 0 0.97  0 0 0 0 0.92  0 0 0 0.40 0'/></filter><rect width='100%25' height='100%25' filter='url(%23h)'/></svg>");
  background-size: 100% 100%, 14px 14px, 600px 600px, 220px 220px, 220px 220px;
  background-repeat: no-repeat, repeat, repeat, repeat, repeat;
  background-blend-mode: normal, multiply, soft-light, multiply, screen;
  opacity: 0.01;
  transition: opacity 1.6s var(--ease-flow);
  animation: paper-breathe 28s ease-in-out infinite;
  /* No will-change here (v=96): background-position is a paint property,
     not a composited one, so the hint reserved a large full-screen
     backing store for zero benefit — pure memory pressure on iOS. */
}
html.scroll-ready .page-texture { opacity: 1; }

/* Gentle 28s drift on the tiled grain layers — only background-position
   moves, so no repaint of the SVG noise; cheap on every device. */
@keyframes paper-breathe {
  0%, 100% {
    background-position:
      0% 0%, 0px 0px, 0px 0px, 0px 0px, 0px 0px;
  }
  50% {
    background-position:
      0% 0%, -3px 2px, -14px 10px, 2px -2px, -2px 2px;
  }
}

/* Two warm radials behind the content: amber bulb upper-right + deep
   ember lower-left. Screen-blended, so each ADDS warmth (never
   darkens). Inside .site — briefly inherits the GSAP reveal blur,
   but as soft gradients the blur transition is invisible. */
.site::before {
  content: '';
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background:
    radial-gradient(60vw 50vh at 78% -10%,
                    rgba(212, 163, 115, 0.18) 0%,
                    rgba(212, 163, 115, 0.06) 32%,
                    transparent 70%),
    radial-gradient(90vw 70vh at 12% 110%,
                    rgba(90, 58, 31, 0.22) 0%,
                    rgba(90, 58, 31, 0.06) 40%,
                    transparent 75%);
  mix-blend-mode: screen;
  opacity: 0;
  transition: opacity 1.2s var(--ease-flow);
}
html.scroll-ready .site::before { opacity: 1; }

.site > * { position: relative; z-index: 1; }

/* pulse-dot keyframes — used by .setup-flow__pulse (card 01) and
   .voice-print__dot (card 03). Two-shadow stack: a constant 18px glow
   (the static "active-dot" halo) + a ripple ring that scales outward. */
@keyframes pulse-dot {
  0% {
    box-shadow:
      0 0 18px rgba(201, 148, 92, 0.65),
      0 0 0 0 rgba(201, 148, 92, 0.55);
  }
  70% {
    box-shadow:
      0 0 18px rgba(201, 148, 92, 0.65),
      0 0 0 10px rgba(201, 148, 92, 0);
  }
  100% {
    box-shadow:
      0 0 18px rgba(201, 148, 92, 0.65),
      0 0 0 0 rgba(201, 148, 92, 0);
  }
}

/* ===================================================================
   VISUALLY HIDDEN — for the section h2 that lives off-screen.
   =================================================================== */
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ===================================================================
   HERO — display headline + paragraph + scroll cue at the top of
   .site, above the scroll-stack. Symmetrical, centred, no CTAs.
   Entry is CSS-only via html.scroll-ready (added by site.js 1.0s
   into the post-intro timeline): each title word translates + de-
   blurs on its own delay (driven by inline --i), then the subtitle,
   then the scroll arrow.
   =================================================================== */
.hero {
  position: relative;
  min-height: 100vh;
  /* svh, not dvh — see the card-wrapper rule for the long version.
     Short version: dvh changes as the iOS Safari address bar shows/hides
     on slow scroll. The hero is in document flow, so every dvh change
     shifted every sticky card below it, which read as the whole page
     jittering in lockstep. svh is static = no shift = no jitter. */
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  text-align: center;
  padding: clamp(140px, 18vh, 200px) var(--gutter) clamp(96px, 14vh, 140px);
  z-index: 1;
}

.hero__inner {
  width: 100%;
  max-width: 1120px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(28px, 3.4vw, 44px);
}

/* TITLE — Playfair Display, two centred lines. Font-size is sized
   by viewport (7vw) so the longer line ("Единственият Български",
   ~22 chars) always fits on a single row from 320px up to 4K — the
   clamp min/max set sane bounds. Tight letter-spacing buys back ~5%
   horizontal room. Gradient text gives subtle warm depth. */
.hero__title {
  font-family: 'Playfair Display', 'Onest', Georgia, serif;
  font-weight: 400;
  /* 7vw still controls small/medium viewports (so phones are
     unchanged at ~26px on a 375px screen), but the max ceiling drops
     from 120 → 76 so desktops/wide displays cap at a modern display
     size instead of marketing-poster size. */
  font-size: clamp(22px, 7vw, 76px);
  line-height: 1.04;
  letter-spacing: -0.025em;

  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(2px, 0.5vw, 12px);

  margin: 0;
}

/* Each line is a single flex row that NEVER wraps: flex-wrap:nowrap
   stops the word-spans from breaking to a new line, white-space:
   nowrap stops the text inside a span from breaking either. The
   font-size above is calibrated so the line still fits the viewport
   without overflow. */
.hero__title-line {
  display: flex;
  flex-wrap: nowrap;
  white-space: nowrap;
  justify-content: center;
  gap: 0 0.32em;
}

.hero__title-line > span {
  display: inline-block;
  /* Warm-dark vertical gradient — top deeper, bottom slightly warmer.
     Falls back to solid #25211E if background-clip:text unsupported. */
  background-image: linear-gradient(180deg, #25211E 0%, #4A3F35 100%);
  -webkit-background-clip: text;
          background-clip: text;
  -webkit-text-fill-color: transparent;
  color: #25211E;

  opacity: 0;
  transform: translateY(28px);
  filter: blur(8px);
  transition:
    opacity 0.95s var(--ease-flow),
    transform 0.95s var(--ease-flow),
    filter 0.95s var(--ease-flow);
  transition-delay: calc(0.20s + var(--i, 0) * 0.12s);
  will-change: transform, opacity, filter;
}

html.scroll-ready .hero__title-line > span {
  opacity: 1;
  transform: translateY(0);
  filter: blur(0);
}

/* SUBTITLE — Onest body, warm taupe. Enters once the last title
   word has settled (0.20 + 3·0.12 = 0.56s + duration). */
.hero__subtitle {
  font-family: 'Onest', sans-serif;
  font-weight: 400;
  font-size: clamp(16px, 1.4vw, 20px);
  line-height: 1.6;
  letter-spacing: -0.005em;
  color: #6F6258;
  max-width: 720px;
  margin: 0;

  opacity: 0;
  transform: translateY(20px);
  filter: blur(6px);
  transition:
    opacity 1.0s var(--ease-flow) 0.95s,
    transform 1.0s var(--ease-flow) 0.95s,
    filter 1.0s var(--ease-flow) 0.95s;
  will-change: transform, opacity, filter;
}
html.scroll-ready .hero__subtitle {
  opacity: 1;
  transform: translateY(0);
  filter: blur(0);
}

/* SCROLL CUE — bottom-centred. The outer button handles translateX
   centering + colour transition; the inner SVG runs the bounce so
   the two transforms don't fight. */
.hero__scroll {
  position: absolute;
  left: 50%;
  bottom: clamp(28px, 5vh, 56px);
  transform: translateX(-50%);

  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 12px 14px;
  background: none;
  border: 0;
  cursor: pointer;
  /* 0.85 alpha gives ~8:1 contrast on cream at the wave's high state and
     ~3.1:1 at the keyframe-low (0.60), keeping the resting cue above
     WCAG AA 3:1 even when it's dimmest. */
  color: rgba(43, 38, 34, 0.85);

  opacity: 0;
  /* visibility:hidden also takes the button out of tab order and blocks
     clicks during the intro — flipping to visible at exactly the moment
     the opacity transition begins (delay 1.7s) so the button is never
     interactive while invisible. */
  visibility: hidden;
  transition: opacity 1.2s var(--ease-flow) 1.7s,
              visibility 0s 1.7s,
              color 0.3s ease;
}
.hero__scroll:hover  { color: rgba(185, 137, 88, 0.92); }
.hero__scroll:focus-visible {
  outline: 2px solid rgba(185, 137, 88, 0.6);
  outline-offset: 4px;
  border-radius: 8px;
}
html.scroll-ready .hero__scroll {
  opacity: 1;
  visibility: visible;
}

.hero__scroll svg {
  width: 24px;
  height: 32px;
  display: block;
  /* Whole-icon heartbeat pulse — 2.4s loop, slight scale-up. Combines
     with the per-chevron wave below for a layered, breathing cue. */
  animation: hero-scroll-pulse 2.4s ease-in-out infinite;
}

/* Three stacked chevrons, each fading from dim → bright → dim in
   sequence (0s · 0.25s · 0.50s delays). The 30% peak in the keyframe
   creates a short bright pulse rather than a slow lerp, which reads
   as a "wave descending" — modern scroll cue. */
.hero__scroll-chevron {
  opacity: 0.22;
  animation: hero-chevron-wave 2.4s ease-in-out infinite;
}
.hero__scroll-chevron--1 { animation-delay: 0s; }
.hero__scroll-chevron--2 { animation-delay: 0.25s; }
.hero__scroll-chevron--3 { animation-delay: 0.50s; }

@keyframes hero-chevron-wave {
  /* 0.60 keyframe-low (was 0.22) keeps each chevron above AA contrast
     even at its dimmest — the wave still reads as a wave because the
     30% peak jumps from ~3:1 to ~8:1, which is the *delta* the eye
     follows, not the absolute dimness. */
  0%, 60%, 100% { opacity: 0.60; }
  30%           { opacity: 1; }
}

@keyframes hero-scroll-pulse {
  0%, 100% { transform: scale(1);    }
  50%      { transform: scale(1.08); }
}

@media (max-width: 600px) {
  .hero { padding: 130px 18px 100px; }
  .hero__subtitle { font-size: 15px; line-height: 1.55; }
}

/* ===================================================================
   SCROLL STACK — 4 sticky full-viewport glass cards on the ambient
   body background. Each card sits in its own .card-wrapper which is
   position:sticky, top:0, height:100vh — so as you scroll, wrappers
   pin and the next one slides up to cover the previous. The
   .glass-card inside each wrapper gets a scrub'd fold-back transform
   (rotateX + scale + blur + dim) driven by site.js when the NEXT
   wrapper enters the lower viewport.
   =================================================================== */
.scroll-stack {
  position: relative;
  z-index: 1;
  /* No padding — wrappers own all spacing. The section is a vertical
     stack of 100vh wrappers; total height = N * 100vh. */
}

/* --- STICKY CARD WRAPPER ------------------------------------------ */
.card-wrapper {
  position: sticky;
  top: 0;
  height: 100vh;
  /* svh (small viewport) is static — does not change as the iOS Safari
     address bar shows/hides. dvh (the dynamic value) was causing every
     sticky wrapper to recompute height during slow scroll, which read
     as everything-on-the-page-jittering. svh trades a small strip of
     unused viewport (when the bar is hidden) for a rock-stable height. */
  height: 100svh;
  display: grid;
  place-items: center;
  padding: 110px var(--gutter) 60px;
  /* Each wrapper owns its 3D context so the card's rotateX looks
     consistent regardless of scroll position. */
  perspective: 2200px;
  perspective-origin: 50% 50%;
}

/* --- GLASS CARD (shared base) ------------------------------------- */
/* ----- CARDS ON CREAM — quiet-luxury light surface --------------------
   The page background is warm cream (#f4eae0). The cards sit just
   barely above it in luminance — nearly-white warm cream, defined not
   by color contrast but by a visible warm-tan border and a single soft
   warm shadow. Premium AI-product feel: not heavy tech-blue, not deep
   dark-mode brown, just a calm warm-light surface.
   Text inside the card is warm dark (#25211E title, #6F6258 body),
   accent is warm amber (#B98958).
   --------------------------------------------------------------------- */
.glass-card {
  width: min(1180px, 100%);
  min-height: clamp(440px, 64vh, 620px);
  padding: clamp(28px, 4.2vw, 64px);
  display: grid;
  align-items: center;
  gap: clamp(28px, 5vw, 72px);

  /* Glassmorphic: 0.45 alpha lets the warm paper texture clearly read
     through the card, the 22px backdrop-blur frosts whatever sits
     behind, and saturate(150%) pushes the warmth so the cream + amber
     glow behind doesn't go grey through the glass. Border + shadow
     carry card definition since the fill no longer can. */
  background: rgba(255, 252, 247, 0.45);
  -webkit-backdrop-filter: blur(22px) saturate(150%);
          backdrop-filter: blur(22px) saturate(150%);

  border: 1px solid rgba(214, 196, 174, 0.65);
  border-radius: clamp(22px, 2.4vw, 34px);

  /* Single soft warm shadow — clean, not over-engineered. The cream
     undertone in the shadow (RGB 65/48/35) makes the elevation feel
     like ambient warm light, not a hard drop. */
  box-shadow: 0 32px 90px rgba(65, 48, 35, 0.14);

  transform-origin: 50% 50%;
  will-change: transform, opacity, filter;
  backface-visibility: hidden;
}

/* Per-card asymmetric grid (DESIGN_VARIANCE 8 — each card breaks
   the previous rhythm so the stack reads as four distinct moments). */
.glass-card--01 { grid-template-columns: 1.25fr 1fr; }
.glass-card--02 { grid-template-columns: 1fr 1.25fr; }
.glass-card--03 { grid-template-columns: 1.2fr 1fr; }
.glass-card--04 { grid-template-columns: 1fr 1.25fr; }

/* --- LEAD COLUMN (eyebrow + title + body + meta) ------------------ */
.glass-card__lead {
  display: flex;
  flex-direction: column;
  gap: clamp(16px, 2.4vw, 28px);
  max-width: 38ch;
}

/* Light-card text scale. Title = deep warm near-black, body = warm
   taupe, accent = warm amber. All chromas in the same warm family —
   no cool grays. */
.card-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  font-family: 'JetBrains Mono', 'Geist Mono', ui-monospace, monospace;
  font-size: 11.5px;
  color: rgba(43, 38, 34, 0.45);
  letter-spacing: 0.22em;
  text-transform: uppercase;
}
.card-eyebrow__num   { color: #B98958; }
.card-eyebrow__label { color: rgba(43, 38, 34, 0.72); }
.card-eyebrow__dot {
  width: 4px; height: 4px;
  border-radius: 50%;
  background: #C9945C;
  box-shadow:
    0 0 0 4px rgba(201, 148, 92, 0.14),
    0 0 12px rgba(201, 148, 92, 0.40);
}

.card-title {
  font-family: 'Onest', 'Geist', sans-serif;
  font-weight: 600;
  font-size: clamp(36px, 5.4vw, 68px);
  line-height: 1.02;
  letter-spacing: -0.028em;
  color: #25211E;
  margin: 0;
}

.card-body {
  font-family: 'Onest', 'Geist', sans-serif;
  font-size: clamp(15px, 1.18vw, 18px);
  line-height: 1.6;
  color: #6F6258;
  margin: 0;
  max-width: 42ch;
}

.card-meta {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding-top: clamp(8px, 1.4vw, 16px);
  border-top: 1px solid rgba(214, 196, 174, 0.55);
  font-family: 'JetBrains Mono', 'Geist Mono', ui-monospace, monospace;
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}
.card-meta__k { color: rgba(111, 98, 88, 0.70); }
.card-meta__v {
  color: #B98958;
  letter-spacing: 0.08em;
  font-size: 15.5px;
  text-transform: none;
}

/* --- CARD VISUAL COLUMN — base centering ------------------------- */
.card-visual {
  display: flex;
  align-items: center;
  justify-content: center;
}

/* ===================================================================
   VISUAL 01 — SETUP FLOW
   Vertical step list with an active terminal step that pulses.
   =================================================================== */
/* Setup-flow wrapper has no chrome of its own — the parent card
   already provides the container. The steps own all the visual weight. */
.setup-flow {
  flex-direction: column;
  align-items: stretch;
  gap: 12px;
  width: 100%;
  max-width: 360px;
  padding: 0;
  background: transparent;
  border: none;
}
.setup-flow__step {
  position: relative;
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 16px 18px;
  background: #FFFFFF;
  border: 1px solid #E8DCCF;
  border-radius: 16px;
  box-shadow: 0 10px 28px rgba(65, 48, 35, 0.08);
  color: #2B2622;
  transition: border-color 0.4s var(--ease-flow),
              background 0.4s var(--ease-flow),
              box-shadow 0.4s var(--ease-flow);
}
.setup-flow__num {
  font-family: 'JetBrains Mono', 'Geist Mono', ui-monospace, monospace;
  font-size: 12px;
  color: rgba(43, 38, 34, 0.42);
  letter-spacing: 0.12em;
}
.setup-flow__label {
  font-family: 'Onest', sans-serif;
  font-size: 15px;
  color: #2B2622;
  flex: 1;
}
.setup-flow__line {
  display: block;
  width: 1px;
  height: 18px;
  background: linear-gradient(180deg,
              rgba(214, 196, 174, 0.75), transparent);
  margin: 0 auto;
}
.setup-flow__step--active {
  background: #F3E7D8;
  border: 1px solid #CBA16D;
  box-shadow: 0 12px 34px rgba(185, 137, 88, 0.18);
}
.setup-flow__step--active .setup-flow__num   { color: #B98958; }
.setup-flow__step--active .setup-flow__label { color: #25211E; }
.setup-flow__pulse {
  position: relative;
  width: 8px; height: 8px;
  border-radius: 50%;
  background: #C9945C;
  box-shadow: 0 0 18px rgba(201, 148, 92, 0.65);
  animation: pulse-dot 2s var(--ease-flow) infinite;
}

/* ===================================================================
   VISUAL 01 (current) — ALWAYS ON · LIVE NOW TIMELINE
   Horizontal 24-hour amber strip. A live "now" marker (positioned by
   JS based on the visitor's local time) rides the bar. Every visitor
   sees the pulse in a different position — the visual itself proves
   the 24/7 claim instead of merely stating it.
   =================================================================== */
.always-on {
  flex-direction: column;
  align-items: stretch;
  justify-content: center;
  gap: 14px;
  padding: clamp(20px, 2.4vw, 36px);
  width: 100%;
  max-width: 440px;
}

/* The amber strip itself. Gradient fades on both ends so the marker
   can sit at 00:00 or 24:00 without "falling off" a hard edge. */
.always-on__track {
  position: relative;
  height: 10px;
  border-radius: 999px;
  background: linear-gradient(90deg,
    rgba(201, 148, 92, 0.10) 0%,
    rgba(201, 148, 92, 0.34) 50%,
    rgba(201, 148, 92, 0.10) 100%);
  border: 1px solid rgba(214, 196, 174, 0.55);
  /* Headroom above for the "СЕГА" label rendered atop the marker. */
  margin-top: 32px;
}
.always-on__track-fill {
  position: absolute;
  inset: 1px;
  border-radius: 999px;
  background: linear-gradient(90deg,
    rgba(201, 148, 92, 0.18) 0%,
    rgba(201, 148, 92, 0.50) 50%,
    rgba(201, 148, 92, 0.18) 100%);
}

/* The "now" marker — positioned absolutely along the track. JS sets
   `left: <pct>%` based on hour-of-day. Pulse + label centred on that
   point. left:50% here is just the JS-less fallback. */
.always-on__marker {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  pointer-events: none;
  z-index: 2;
}
.always-on__marker-label {
  position: absolute;
  bottom: 100%;
  margin-bottom: 8px;
  font-family: 'JetBrains Mono', 'Geist Mono', ui-monospace, monospace;
  font-size: 9.5px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: #B98958;
  white-space: nowrap;
}
.always-on__marker-pulse {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: #C9945C;
  box-shadow:
    0 0 0 4px rgba(201, 148, 92, 0.22),
    0 0 0 10px rgba(201, 148, 92, 0.10),
    0 0 18px rgba(201, 148, 92, 0.6);
  animation: pulse-dot 2.2s var(--ease-flow) infinite;
}

/* Hour-tick row beneath the strip. Mono numerals spaced evenly so the
   five labels (00, 06, 12, 18, 24) anchor the eye to the 24-hour axis. */
.always-on__ticks {
  display: flex;
  justify-content: space-between;
  font-family: 'JetBrains Mono', 'Geist Mono', ui-monospace, monospace;
  font-size: 10px;
  letter-spacing: 0.16em;
  color: rgba(43, 38, 34, 0.45);
}

/* Caption beneath: a steady amber dot + the "винаги на линия" line. */
.always-on__caption {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  font-family: 'JetBrains Mono', 'Geist Mono', ui-monospace, monospace;
  font-size: 11px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: rgba(43, 38, 34, 0.72);
  margin-top: 12px;
}
.always-on__caption-dot {
  width: 6px; height: 6px;
  border-radius: 50%;
  background: #C9945C;
  box-shadow: 0 0 10px rgba(201, 148, 92, 0.6);
}

/* Card 01's title is a full declarative sentence (~45 chars), not a
   short poster headline like the other cards. Editorial scale + tight
   max-width force a balanced 2-3 line wrap instead of a giant runaway. */
.glass-card--01 .card-title {
  font-size: clamp(28px, 3.4vw, 46px);
  line-height: 1.12;
  letter-spacing: -0.02em;
  max-width: 17ch;
  text-wrap: balance;
}

/* All four cards use the centred-lead template — eyebrow chip in the
   middle, title wrapping on-axis, body paragraph centred, meta stack
   centred with its hairline spanning the full column width. Reads as
   an editorial spread rather than a left-flush feature list. */
.glass-card--01 .glass-card__lead,
.glass-card--02 .glass-card__lead,
.glass-card--03 .glass-card__lead,
.glass-card--04 .glass-card__lead {
  align-items: center;
  text-align: center;
}
.glass-card--01 .card-meta,
.glass-card--02 .card-meta,
.glass-card--03 .card-meta,
.glass-card--04 .card-meta {
  align-items: center;
  width: 100%;
}

/* Cards 02-04 use the editorial title scale — paragraph-lead, not
   poster headlines. Card 01 has its own rule above. */
.glass-card--02 .card-title,
.glass-card--03 .card-title,
.glass-card--04 .card-title {
  font-size: clamp(28px, 3.4vw, 46px);
  line-height: 1.12;
  letter-spacing: -0.02em;
  max-width: 17ch;
  text-wrap: balance;
}

/* ===================================================================
   VISUAL 02 (current) — ACTION STREAM · ROTATING LIVE FEED
   A glass panel that cycles through actions one at a time with a
   synchronized slide-up: old exits the top, new enters from below in
   lockstep. The panel itself is alive — a slow orbital amber glow
   moves inside it, and a hairline progress bar at the bottom fills
   in rhythm with each rotation. The visual demonstrates the assistant
   working in real time rather than just listing capabilities.
   =================================================================== */
.action-stream {
  position: relative;
  flex-direction: column;
  align-items: stretch;
  gap: 18px;
  padding: clamp(20px, 2.4vw, 28px);
  width: 100%;
  max-width: 380px;
  background: rgba(255, 252, 247, 0.55);
  border: 1px solid rgba(214, 196, 174, 0.5);
  border-radius: 18px;
  overflow: hidden;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.4),
    0 12px 30px rgba(65, 48, 35, 0.08);
}

/* Ambient warm light orbiting slowly inside the panel — the primary
   "alive" element. A blurred amber radial that drifts around in a
   16s loop. The container's overflow:hidden clips it cleanly. */
.action-stream::before {
  content: '';
  position: absolute;
  top: 0; left: 0;
  width: 75%;
  height: 75%;
  border-radius: 50%;
  background: radial-gradient(
    circle at 50% 50%,
    rgba(201, 148, 92, 0.32) 0%,
    rgba(201, 148, 92, 0.12) 30%,
    transparent 65%
  );
  filter: blur(34px);
  pointer-events: none;
  animation: action-stream-glow 16s ease-in-out infinite;
  z-index: 0;
}
@keyframes action-stream-glow {
  0%, 100% { transform: translate(-15%, -20%) scale(1); }
  25%      { transform: translate(60%,   0%) scale(1.15); }
  50%      { transform: translate(75%,  55%) scale(0.95); }
  75%      { transform: translate(-5%,  60%) scale(1.10); }
}

/* All content sits above the ambient glow. */
.action-stream__header,
.action-stream__list,
.action-stream__progress {
  position: relative;
  z-index: 1;
}

.action-stream__header {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  font-family: 'JetBrains Mono', 'Geist Mono', ui-monospace, monospace;
  font-size: 10.5px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: rgba(43, 38, 34, 0.6);
}
.action-stream__pulse {
  width: 7px; height: 7px;
  border-radius: 50%;
  background: #C9945C;
  box-shadow:
    0 0 0 4px rgba(201, 148, 92, 0.18),
    0 0 12px rgba(201, 148, 92, 0.5);
  animation: pulse-dot 2.2s var(--ease-flow) infinite;
}

/* Fixed-height stage with overflow:hidden so the synchronized slide
   has clean edges — items move full-stage-height (yPercent ±100) and
   the off-stage portions are clipped. No visual overlap mid-transition. */
.action-stream__list {
  position: relative;
  height: 180px;
  list-style: none;
  margin: 0;
  padding: 0;
  overflow: hidden;
}

.action-stream__item {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  opacity: 0;
  will-change: transform, opacity;
}

/* Refined icon tile — smaller, lighter chrome, soft radial fill
   instead of the old linear gradient + halo. Cleaner and more
   elegant; the ambient glow behind carries the warmth. */
.action-stream__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  color: #B98958;
  background: radial-gradient(circle at 50% 35%,
    rgba(201, 148, 92, 0.18) 0%,
    rgba(201, 148, 92, 0.05) 100%);
  border-radius: 14px;
  border: 1px solid rgba(201, 148, 92, 0.26);
  box-shadow:
    inset 0 1px 0 rgba(255, 245, 230, 0.5),
    0 6px 16px rgba(201, 148, 92, 0.10);
}
.action-stream__icon svg {
  width: 26px;
  height: 26px;
}

.action-stream__label {
  font-family: 'Onest', 'Geist', sans-serif;
  font-size: 17px;
  font-weight: 500;
  color: #25211E;
  text-align: center;
  max-width: 240px;
  line-height: 1.3;
  letter-spacing: -0.005em;
}

.action-stream__time {
  font-family: 'JetBrains Mono', 'Geist Mono', ui-monospace, monospace;
  font-size: 10.5px;
  letter-spacing: 0.14em;
  color: rgba(111, 98, 88, 0.55);
}

/* Hairline progress bar at the bottom — fills left-to-right over the
   full cycle (slide + dwell), then resets on rotation. JS drives the
   transform so it stays synchronized with the rotation interval. */
.action-stream__progress {
  height: 2px;
  background: rgba(201, 148, 92, 0.16);
  border-radius: 999px;
  overflow: hidden;
}
.action-stream__progress-fill {
  display: block;
  width: 100%;
  height: 100%;
  transform: scaleX(0);
  transform-origin: 0 50%;
  background: linear-gradient(90deg,
    rgba(201, 148, 92, 0.55) 0%,
    #C9945C 60%,
    rgba(201, 148, 92, 0.85) 100%);
  box-shadow: 0 0 8px rgba(201, 148, 92, 0.55);
}

/* JS-free fallback — if GSAP doesn't initialise (cached miss, slow
   network), pin the first item visible so the panel isn't empty. */
.action-stream__list:not(.is-wired) .action-stream__item:first-child {
  opacity: 1;
}

/* ===================================================================
   VISUAL 02 (legacy) — STATUS 24/7
   Radiating rings around a warm core + mono stats below.
   Kept for now (unused) until we're certain we won't reuse it.
   =================================================================== */
.status-24 {
  flex-direction: column;
  gap: 20px;
  width: 100%;
  max-width: 380px;
  padding: clamp(24px, 3vw, 36px);
  /* Subtle warm halo from above — sits on the light card without
     adding its own border. The card already contains it. */
  background:
    radial-gradient(60% 50% at 50% 35%,
                    rgba(201, 148, 92, 0.10) 0%,
                    transparent 70%);
  border: none;
  border-radius: 20px;
  text-align: center;
}
.status-24__core {
  position: relative;
  width: 140px;
  height: 140px;
  margin: 6px auto 0;
}
.status-24__hub {
  position: absolute;
  inset: 50% auto auto 50%;
  width: 22px; height: 22px;
  margin: -11px 0 0 -11px;
  border-radius: 50%;
  background: radial-gradient(circle, #DBB07D 0%, #B98958 80%);
  box-shadow: 0 0 24px rgba(201, 148, 92, 0.55);
}
.status-24__ring {
  position: absolute;
  inset: 50% auto auto 50%;
  width: 30px; height: 30px;
  margin: -15px 0 0 -15px;
  border-radius: 50%;
  border: 1px solid rgba(201, 148, 92, 0.55);
  opacity: 0;
  animation: status-ripple 2.4s cubic-bezier(0, 0.6, 0.3, 1) infinite;
}
.status-24__ring:nth-child(2) { animation-delay: 0.8s; }
.status-24__ring:nth-child(3) { animation-delay: 1.6s; }
@keyframes status-ripple {
  0%   { transform: scale(0.6); opacity: 0.9; border-width: 1.5px; }
  100% { transform: scale(4.2); opacity: 0;   border-width: 0.5px; }
}
.status-24__label {
  font-family: 'JetBrains Mono', 'Geist Mono', ui-monospace, monospace;
  font-size: 11.5px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: #B98958;
}
.status-24__time {
  font-family: 'Onest', sans-serif;
  font-size: clamp(40px, 4.4vw, 56px);
  font-weight: 600;
  letter-spacing: -0.025em;
  color: #25211E;
  line-height: 1;
}
.status-24__stats {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
  padding-top: 18px;
  border-top: 1px solid rgba(214, 196, 174, 0.55);
}
.status-24__stats > div {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.status-24__stats strong {
  font-family: 'JetBrains Mono', 'Geist Mono', ui-monospace, monospace;
  font-size: 20px;
  font-weight: 500;
  color: #25211E;
  letter-spacing: 0.04em;
  font-variant-numeric: tabular-nums;
}
.status-24__stats span {
  font-family: 'Onest', sans-serif;
  font-size: 11.5px;
  color: rgba(111, 98, 88, 0.70);
  line-height: 1.3;
}

/* ===================================================================
   VISUAL 03 (current) — LIVE CONVERSATION THREAD
   A chat thread that types itself out in real time, loops every ~12s.
   Demonstrates both claims at once:
     - MEMORY: AI greets the returning customer BY NAME (Иван) after
       reconnect, without ever being told who's calling.
     - CONTINUITY: AI references the prior context ("масата за петък")
       seamlessly after the connection drop.
   Driven by JS (wireChatThread) — bubbles fade-scale in, typing dots
   show before AI messages, then text types character-by-character.
   The disconnect indicator pulses red. Loops perpetually.
   =================================================================== */
.chat-thread {
  position: relative;
  flex-direction: column;
  align-items: stretch;
  width: 100%;
  max-width: 380px;
  padding: clamp(20px, 2.4vw, 26px);
  gap: 14px;
  background: rgba(255, 252, 247, 0.55);
  border: 1px solid rgba(214, 196, 174, 0.5);
  border-radius: 20px;
  overflow: hidden;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.4),
    0 12px 30px rgba(65, 48, 35, 0.08);
}

/* Ambient amber glow drifting inside — primary "alive" effect. */
.chat-thread::before {
  content: '';
  position: absolute;
  top: 0; left: 0;
  width: 75%;
  height: 70%;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 50%,
    rgba(201, 148, 92, 0.28) 0%,
    rgba(201, 148, 92, 0.10) 40%,
    transparent 70%);
  filter: blur(34px);
  pointer-events: none;
  animation: chat-thread-glow 18s ease-in-out infinite;
  z-index: 0;
}
@keyframes chat-thread-glow {
  0%, 100% { transform: translate(-15%, -15%) scale(1); }
  33%      { transform: translate(60%, 10%)  scale(1.12); }
  66%      { transform: translate(10%, 60%)  scale(0.95); }
}

.chat-thread__header,
.chat-thread__messages,
.chat-thread__progress {
  position: relative;
  z-index: 1;
}

.chat-thread__header {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: 'JetBrains Mono', 'Geist Mono', ui-monospace, monospace;
  font-size: 10.5px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: rgba(43, 38, 34, 0.6);
}
.chat-thread__pulse {
  width: 7px; height: 7px;
  border-radius: 50%;
  background: #C9945C;
  box-shadow:
    0 0 0 4px rgba(201, 148, 92, 0.18),
    0 0 12px rgba(201, 148, 92, 0.5);
  animation: pulse-dot 2.2s var(--ease-flow) infinite;
}

/* Thread stage — fixed enough for stable layout during the cycle. */
.chat-thread__messages {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-height: 260px;
}

/* Each row holds one bubble. Entrance: opacity + scale + slight rise.
   Transitions on the BASE class so removing .is-visible also animates
   (fade out on cycle reset). */
.chat-thread__row {
  display: flex;
  opacity: 0;
  transform: translateY(8px) scale(0.96);
  transition: opacity 0.4s var(--ease-flow), transform 0.4s var(--ease-flow);
}
.chat-thread__row--customer { justify-content: flex-end; }
.chat-thread__row--ai       { justify-content: flex-start; }
.chat-thread__row.is-visible {
  opacity: 1;
  transform: translateY(0) scale(1);
}

.chat-thread__bubble {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 10px 14px;
  border-radius: 16px;
  max-width: 82%;
  font-family: 'Onest', 'Geist', sans-serif;
  font-size: 13.5px;
  line-height: 1.4;
  text-align: left;
  box-shadow: 0 4px 12px rgba(65, 48, 35, 0.06);
}

/* Customer bubble: amber tint, tail on bottom-right. */
.chat-thread__row--customer .chat-thread__bubble {
  background: rgba(201, 148, 92, 0.16);
  border: 1px solid rgba(201, 148, 92, 0.32);
  color: #25211E;
  border-bottom-right-radius: 5px;
}

/* AI bubble: cream, tail on bottom-left. */
.chat-thread__row--ai .chat-thread__bubble {
  background: rgba(255, 252, 247, 0.85);
  border: 1px solid rgba(214, 196, 174, 0.55);
  color: #25211E;
  border-bottom-left-radius: 5px;
}

/* Ghost rows — opening "conversation was active" bubbles. Show only the
   typing dots; no text, no time, dimmed opacity. After the connection
   drops they fade further via .is-faded-out. */
.chat-thread__row.is-visible.is-ghost {
  opacity: 0.55;
}
.chat-thread__row.is-visible.is-ghost.is-faded-out {
  opacity: 0.15;
  transition: opacity 0.45s var(--ease-flow);
}
.chat-thread__row.is-ghost .chat-thread__typing { display: inline-flex; }
.chat-thread__row.is-ghost .chat-thread__text   { display: none; }
.chat-thread__row.is-ghost .chat-thread__time   { display: none; }

/* Typed text holder — JS fills character-by-character. */
.chat-thread__text { display: inline; }

/* Blinking caret during typing — pure CSS, only shown while typing. */
.chat-thread__row.is-typing-text .chat-thread__text::after {
  content: '';
  display: inline-block;
  width: 2px;
  height: 0.95em;
  background: #B98958;
  margin-left: 2px;
  vertical-align: text-bottom;
  animation: chat-caret 0.7s steps(2) infinite;
}
@keyframes chat-caret { to { opacity: 0; } }

/* Timestamp under each bubble — invisible until typing completes. */
.chat-thread__time {
  font-family: 'JetBrains Mono', 'Geist Mono', ui-monospace, monospace;
  font-size: 9.5px;
  letter-spacing: 0.1em;
  color: rgba(111, 98, 88, 0.55);
  align-self: flex-end;
  opacity: 0;
  transition: opacity 0.3s var(--ease-flow);
}
.chat-thread__row.is-typed .chat-thread__time { opacity: 1; }

/* Three-dot typing indicator — shown before each AI message types.
   Mutually exclusive with the text via display toggling. */
.chat-thread__typing {
  display: none;
  align-items: center;
  gap: 4px;
  padding: 3px 0;
}
.chat-thread__row.is-typing .chat-thread__typing { display: inline-flex; }
.chat-thread__row.is-typing .chat-thread__text   { display: none; }
.chat-thread__row.is-typing .chat-thread__time   { display: none; }
.chat-thread__typing span {
  width: 5px; height: 5px;
  border-radius: 50%;
  background: rgba(185, 137, 88, 0.7);
  display: inline-block;
}
.chat-thread__typing span:nth-child(1) { animation: chat-typing 1.2s ease-in-out infinite; }
.chat-thread__typing span:nth-child(2) { animation: chat-typing 1.2s ease-in-out 0.2s infinite; }
.chat-thread__typing span:nth-child(3) { animation: chat-typing 1.2s ease-in-out 0.4s infinite; }
@keyframes chat-typing {
  0%, 60%, 100% { opacity: 0.3; transform: translateY(0); }
  30%           { opacity: 1;   transform: translateY(-3px); }
}

/* Disconnect indicator — red dot pulses urgently, hairlines on sides. */
.chat-thread__break {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 4px 0;
  font-family: 'JetBrains Mono', 'Geist Mono', ui-monospace, monospace;
  font-size: 9.5px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: rgba(180, 60, 60, 0.75);
  opacity: 0;
  transition: opacity 0.5s var(--ease-flow);
}
.chat-thread__break.is-visible { opacity: 1; }
.chat-thread__break::before,
.chat-thread__break::after {
  content: '';
  flex: 1;
  height: 1px;
  background: linear-gradient(90deg,
    transparent,
    rgba(196, 86, 86, 0.4) 30%,
    rgba(196, 86, 86, 0.4) 70%,
    transparent);
}
.chat-thread__break-dot {
  width: 6px; height: 6px;
  border-radius: 50%;
  background: #C45656;
  box-shadow: 0 0 10px rgba(196, 86, 86, 0.55);
  animation: chat-disconnect-pulse 1.2s ease-in-out infinite;
}
@keyframes chat-disconnect-pulse {
  0%, 100% { opacity: 1;   transform: scale(1); }
  50%      { opacity: 0.5; transform: scale(0.85); }
}

/* Cycle progress at the bottom — JS drives via CSS keyframe restart.
   The `animation` property is set inline by JS each cycle using the
   reflow-restart trick (set animation:none, force reflow, set new
   animation). That guarantees the bar restarts cleanly without the
   "stuck at end" symptom of a misaligned GSAP tween. */
.chat-thread__progress {
  height: 2px;
  background: rgba(201, 148, 92, 0.16);
  border-radius: 999px;
  overflow: hidden;
}
.chat-thread__progress-fill {
  display: block;
  width: 100%;
  height: 100%;
  transform: scaleX(0);
  transform-origin: 0 50%;
  background: linear-gradient(90deg,
    rgba(201, 148, 92, 0.55) 0%,
    #C9945C 60%,
    rgba(201, 148, 92, 0.85) 100%);
  box-shadow: 0 0 8px rgba(201, 148, 92, 0.55);
  /* No animation declared here — JS toggles it on each cycle. */
}
@keyframes chat-progress {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

/* ===================================================================
   VISUAL 03 (legacy) — VOICE PRINT
   Spectrum-style waveform bars with pill + axis legend.
   Kept for now (unused) — same dead-code rule as status-24 legacy.
   =================================================================== */
.voice-print {
  flex-direction: column;
  gap: 18px;
  width: 100%;
  max-width: 380px;
  padding: 0;
  background: transparent;
  border: none;
  align-items: stretch;
}
.voice-print__pill {
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 12px;
  border-radius: 999px;
  background: rgba(201, 148, 92, 0.10);
  border: 1px solid rgba(201, 148, 92, 0.32);
  color: #B98958;
  font-family: 'JetBrains Mono', 'Geist Mono', ui-monospace, monospace;
  font-size: 11px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
}
.voice-print__dot {
  width: 6px; height: 6px;
  border-radius: 50%;
  background: #C9945C;
  animation: pulse-dot 2s var(--ease-flow) infinite;
}
.voice-print__bars {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 4px;
  height: 120px;
  padding: 12px 4px;
}
.voice-print__bars span {
  display: block;
  flex: 1;
  background: linear-gradient(180deg, #DBB07D, #B98958);
  border-radius: 2px;
  transform-origin: center;
  animation: vp-pulse 1.6s ease-in-out infinite;
}
/* Hand-tuned heights + phase offsets so it reads like real audio,
   not a generic equaliser. */
.voice-print__bars span:nth-child(1)  { height: 14%; animation-delay: -0.10s; }
.voice-print__bars span:nth-child(2)  { height: 24%; animation-delay: -0.30s; }
.voice-print__bars span:nth-child(3)  { height: 18%; animation-delay: -0.50s; }
.voice-print__bars span:nth-child(4)  { height: 42%; animation-delay: -0.70s; }
.voice-print__bars span:nth-child(5)  { height: 60%; animation-delay: -0.20s; }
.voice-print__bars span:nth-child(6)  { height: 38%; animation-delay: -0.55s; }
.voice-print__bars span:nth-child(7)  { height: 72%; animation-delay: -0.05s; }
.voice-print__bars span:nth-child(8)  { height: 88%; animation-delay: -0.40s; }
.voice-print__bars span:nth-child(9)  { height: 64%; animation-delay: -0.65s; }
.voice-print__bars span:nth-child(10) { height: 96%; animation-delay: -0.15s; }
.voice-print__bars span:nth-child(11) { height: 80%; animation-delay: -0.45s; }
.voice-print__bars span:nth-child(12) { height: 100%; animation-delay: -0.25s; }
.voice-print__bars span:nth-child(13) { height: 78%; animation-delay: -0.60s; }
.voice-print__bars span:nth-child(14) { height: 56%; animation-delay: -0.35s; }
.voice-print__bars span:nth-child(15) { height: 70%; animation-delay: -0.08s; }
.voice-print__bars span:nth-child(16) { height: 44%; animation-delay: -0.52s; }
.voice-print__bars span:nth-child(17) { height: 58%; animation-delay: -0.18s; }
.voice-print__bars span:nth-child(18) { height: 32%; animation-delay: -0.42s; }
.voice-print__bars span:nth-child(19) { height: 26%; animation-delay: -0.68s; }
.voice-print__bars span:nth-child(20) { height: 18%; animation-delay: -0.12s; }
.voice-print__bars span:nth-child(21) { height: 30%; animation-delay: -0.58s; }
.voice-print__bars span:nth-child(22) { height: 14%; animation-delay: -0.32s; }
.voice-print__bars span:nth-child(23) { height: 22%; animation-delay: -0.06s; }
.voice-print__bars span:nth-child(24) { height: 12%; animation-delay: -0.48s; }
@keyframes vp-pulse {
  0%, 100% { transform: scaleY(0.55); opacity: 0.85; }
  50%      { transform: scaleY(1.00); opacity: 1; }
}
.voice-print__legend {
  display: flex;
  justify-content: space-between;
  padding-top: 10px;
  border-top: 1px solid rgba(214, 196, 174, 0.55);
  font-family: 'JetBrains Mono', 'Geist Mono', ui-monospace, monospace;
  font-size: 10.5px;
  color: rgba(111, 98, 88, 0.70);
  letter-spacing: 0.15em;
}

/* ===================================================================
   VISUAL 04 (current) — VOICE DEMO PLAYER
   Audio player with a 32-bar waveform. Idle: bars do a slow staggered
   ambient wave (CSS keyframes). Playing: bars are driven by REAL
   Web Audio API frequency analysis from the live audio. Big amber
   gradient play button toggles between play/pause icons. Time + total
   duration in mono. Audio source lives at assets/audio/demo.mp3 —
   gracefully degrades if file is missing.
   =================================================================== */
.voice-demo {
  position: relative;
  flex-direction: column;
  align-items: stretch;
  width: 100%;
  max-width: 400px;
  padding: clamp(22px, 2.6vw, 30px);
  gap: 18px;
  background: rgba(255, 252, 247, 0.55);
  border: 1px solid rgba(214, 196, 174, 0.5);
  border-radius: 22px;
  overflow: hidden;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.4),
    0 12px 30px rgba(65, 48, 35, 0.08);
}

/* Ambient amber glow orbiting inside — primary "alive" layer. */
.voice-demo::before {
  content: '';
  position: absolute;
  top: 0; left: 0;
  width: 75%;
  height: 70%;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 50%,
    rgba(201, 148, 92, 0.32) 0%,
    rgba(201, 148, 92, 0.12) 40%,
    transparent 70%);
  filter: blur(36px);
  pointer-events: none;
  animation: voice-demo-glow 18s ease-in-out infinite;
  z-index: 0;
}
@keyframes voice-demo-glow {
  0%, 100% { transform: translate(-15%, -15%) scale(1); }
  33%      { transform: translate(55%,  10%) scale(1.12); }
  66%      { transform: translate(15%,  55%) scale(0.95); }
}

.voice-demo__header,
.voice-demo__stage,
.voice-demo__play,
.voice-demo__meta {
  position: relative;
  z-index: 1;
}

.voice-demo__header {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  font-family: 'JetBrains Mono', 'Geist Mono', ui-monospace, monospace;
  font-size: 10.5px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: rgba(43, 38, 34, 0.6);
}
.voice-demo__pulse {
  width: 7px; height: 7px;
  border-radius: 50%;
  background: #C9945C;
  box-shadow:
    0 0 0 4px rgba(201, 148, 92, 0.18),
    0 0 12px rgba(201, 148, 92, 0.5);
  animation: pulse-dot 2.2s var(--ease-flow) infinite;
}

/* Waveform stage — fixed height so the bars have a stable canvas. */
.voice-demo__stage {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 96px;
  padding: 0 4px;
}
.voice-demo__waveform {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 3px;
  width: 100%;
  height: 100%;
}

/* Each bar — gradient amber fill, scale-Y driven by either the CSS
   idle keyframe OR the JS animation loop (when audio is playing). */
.voice-demo__bar {
  flex: 1;
  height: 100%;
  background: linear-gradient(180deg,
    rgba(201, 148, 92, 0.85) 0%,
    rgba(185, 137, 88, 0.6) 100%);
  border-radius: 999px;
  transform: scaleY(0.18);
  transform-origin: 50% 50%;
  transition: transform 0.12s var(--ease-flow);
  /* Negative animation-delay stagger creates a continuous left-to-right
     wave — each bar starts mid-cycle, slightly ahead of its neighbour. */
  animation: voice-demo-idle 2.6s ease-in-out infinite;
  animation-delay: calc(var(--i, 0) * -0.08s);
  will-change: transform;
}
@keyframes voice-demo-idle {
  0%, 100% { transform: scaleY(0.18); }
  50%      { transform: scaleY(0.42); }
}

/* When analysis is active (Web Audio graph connected + audio playing),
   suspend the CSS idle wave so JS-driven inline transforms take over.
   Gated on .is-analyzing rather than .is-playing — under file:// or
   any environment where Web Audio is blocked, .is-playing toggles for
   the button icon but the idle wave keeps running during playback.
   100ms ease-out adds a touch of CSS-level smoothing on top of the
   JS exponential moving average — bars feel like they're flowing
   rather than ticking. */
.voice-demo.is-analyzing .voice-demo__bar {
  animation: none;
  background: linear-gradient(180deg,
    #C9945C 0%,
    #B98958 100%);
  transition: transform 0.1s ease-out;
}

/* Play button — large amber pill with hover lift + icon swap. */
.voice-demo__play {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  align-self: center;
  padding: 12px 26px;
  background: linear-gradient(135deg, #C9945C 0%, #A57645 100%);
  color: #FFF8EE;
  border: none;
  border-radius: 999px;
  font-family: 'Onest', 'Geist', sans-serif;
  font-size: 14.5px;
  font-weight: 600;
  letter-spacing: 0.02em;
  cursor: pointer;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.3),
    0 8px 22px rgba(185, 137, 88, 0.32);
  transition: transform 0.25s var(--ease-flow),
              box-shadow 0.25s var(--ease-flow);
}
.voice-demo__play:hover {
  transform: translateY(-2px);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.3),
    0 12px 30px rgba(185, 137, 88, 0.42);
}
.voice-demo__play:active { transform: translateY(0); }
.voice-demo__play:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  transform: none;
}

.voice-demo__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px; height: 18px;
}
.voice-demo__icon svg {
  width: 18px; height: 18px;
}
.voice-demo__icon-pause { display: none; }
.voice-demo.is-playing .voice-demo__icon-play  { display: none; }
.voice-demo.is-playing .voice-demo__icon-pause { display: block; }

/* Time / duration counter. */
.voice-demo__meta {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  font-family: 'JetBrains Mono', 'Geist Mono', ui-monospace, monospace;
  font-size: 11px;
  letter-spacing: 0.1em;
  color: rgba(111, 98, 88, 0.65);
}
.voice-demo__sep { opacity: 0.5; }

/* Audio element itself is hidden — custom UI above. */
.voice-demo__audio { display: none; }

/* ===================================================================
   VISUAL 04 (current) — LIVE CAPTIONS
   A narrow secondary container that fades in below the player when
   audio starts. The stage holds a fixed 2-row viewport; captions are
   appended as flex rows from the bottom (justify-end), so the newest
   text is always anchored at the bottom and older text scrolls up
   into the clipped region.
   Each row carries data-sp="ai" or data-sp="caller" — a tiny coloured
   dot at the start differentiates speakers without adding label text.
   Idle (audio not playing): max-height 0 + opacity 0 → zero footprint,
   the card layout is identical to the no-captions baseline. Active:
   container expands and content fades in over 0.45s.
   =================================================================== */
.voice-demo__captions {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 340px;
  margin: 4px auto 0;
  box-sizing: border-box;

  /* Zero footprint when idle so adding the captions feature doesn't
     grow the card on phones where vertical budget is already tight. */
  opacity: 0;
  max-height: 0;
  padding: 0 16px;
  overflow: hidden;
  background: rgba(255, 252, 247, 0.55);
  border: 1px solid rgba(214, 196, 174, 0);
  border-radius: 14px;

  transition:
    opacity     0.45s var(--ease-flow),
    max-height  0.50s var(--ease-flow),
    padding     0.45s var(--ease-flow),
    border-color 0.45s var(--ease-flow);
}
.voice-demo.captions-active .voice-demo__captions {
  opacity: 1;
  max-height: 100px;
  padding: 12px 16px;
  border-color: rgba(214, 196, 174, 0.45);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.35),
    0 6px 18px rgba(65, 48, 35, 0.06);
}

.voice-demo__caption-stage {
  position: relative;
  height: 3.1em;          /* ~2 lines + a hair of breathing room */
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;  /* newest row sticks to the bottom */
  gap: 2px;
  font-family: 'Onest', 'Geist', sans-serif;
  font-size: 13px;
  line-height: 1.45;
  color: #25211E;
  text-align: center;
}
.voice-demo__caption-row {
  flex: 0 0 auto;
  /* Enter state — invisible + slightly below final position so the new
     row "rises" the moment it's appended. JS adds .is-visible on the
     next frame to trigger the transition. */
  opacity: 0;
  transform: translateY(6px);
  transition:
    opacity   0.40s var(--ease-flow),
    transform 0.40s var(--ease-flow);
  /* Wrap long captions naturally; flex justify-end + overflow:hidden on
     the stage handle scroll-up when a row exceeds 2 visible lines. */
  word-wrap: break-word;
  overflow-wrap: break-word;
}
.voice-demo__caption-row.is-visible {
  opacity: 1;
  transform: translateY(0);
}
/* Previous (non-current) rows dim slightly so the eye knows where the
   "live" line is. The current row stays full-bright. */
.voice-demo__caption-row.is-dimmed {
  opacity: 0.45;
}
/* Row leaving (replaced by a too-new caption or full reset). */
.voice-demo__caption-row.is-leaving {
  opacity: 0;
  transform: translateY(-10px);
}

/* Speaker indicator — tiny coloured dot before the text. AI gets the
   warm amber accent (matching the rest of card 04's voice = brand);
   the caller gets a muted taupe so the AI's lines visually lead. */
.voice-demo__caption-row::before {
  content: '';
  display: inline-block;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  margin-right: 9px;
  vertical-align: middle;
  background: rgba(111, 98, 88, 0.55);
  transform: translateY(-1px);
  flex-shrink: 0;
}
.voice-demo__caption-row[data-sp="ai"]::before {
  background: #C9945C;
  box-shadow: 0 0 8px rgba(201, 148, 92, 0.5);
}
.voice-demo__caption-row[data-sp="caller"]::before {
  background: rgba(111, 98, 88, 0.65);
}

/* ===================================================================
   VISUAL 04 (legacy) — CAPABILITY BENTO
   2×2 grid of tiles, each with an icon + label + sublabel.
   Kept for now (unused) — same dead-code rule as other legacy visuals.
   =================================================================== */
.cap-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
  width: 100%;
  max-width: 460px;
}
.cap-tile {
  position: relative;
  padding: 22px;
  background: #FFFFFF;
  border: 1px solid #E8DCCF;
  border-radius: 16px;
  box-shadow: 0 10px 28px rgba(65, 48, 35, 0.08);
  color: #2B2622;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 14px;
  transition: border-color 0.3s var(--ease-flow),
              background 0.3s var(--ease-flow),
              transform 0.3s var(--ease-flow),
              box-shadow 0.3s var(--ease-flow);
}
.cap-tile::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 16px;
  pointer-events: none;
  background: radial-gradient(60% 50% at 30% 0%,
                              rgba(201, 148, 92, 0.12), transparent 70%);
  opacity: 0;
  transition: opacity 0.4s var(--ease-flow);
}
.cap-tile:hover {
  background: #F3E7D8;
  border-color: #CBA16D;
  transform: translateY(-2px);
  box-shadow: 0 12px 34px rgba(185, 137, 88, 0.18);
}
.cap-tile:hover::after { opacity: 1; }
.cap-tile svg {
  width: 26px;
  height: 26px;
  color: #B98958;
}
.cap-tile__label {
  font-family: 'Onest', sans-serif;
  font-size: 16px;
  font-weight: 500;
  color: #2B2622;
  letter-spacing: -0.005em;
}
.cap-tile__sub {
  font-family: 'JetBrains Mono', 'Geist Mono', ui-monospace, monospace;
  font-size: 11px;
  color: rgba(111, 98, 88, 0.65);
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

/* ===================================================================
   MODALS — header nav opens deep-linked overlays. One shared dim+blur
   backdrop, multiple cards switched by .is-active. URL hash drives
   which card is open. Card animations use cubic-bezier(0.16, 1, 0.3, 1)
   for a smooth, premium ease-out. Open is 380ms, close is faster
   (240ms) so dismissal feels snappy. transform + opacity only —
   never `top`/`width` — so the GPU compositor handles all motion.
   =================================================================== */
.modal-overlay {
  position: fixed;
  inset: 0;
  z-index: 1500;
  display: grid;
  place-items: center;
  padding: clamp(16px, 3vw, 32px);

  /* IDLE state — fully transparent + zero blur. We animate the dim
     AND the blur (not opacity), because Safari snaps backdrop-filter
     instantly when opacity changes — the user would see the blur
     "pop" while the dark tint fades. Animating background color +
     backdrop-filter directly keeps the whole effect genuinely
     gradual: the user watches the blur build up through every
     stage from clear → light blur → mid blur → full blur. */
  background: rgba(15, 11, 8, 0);
  -webkit-backdrop-filter: blur(0px) saturate(100%);
          backdrop-filter: blur(0px) saturate(100%);

  pointer-events: none;
  /* 850ms with cubic-bezier(0.4, 0, 0.2, 1) — Material Standard.
     Motion is distributed evenly across the duration: 25% time =
     25% progress, etc. This gives the eye time to register each
     stage (clear → semi → mid → full) rather than the front-loaded
     "snap and settle" of Apple's ease-out curve.
     Card uses the IDENTICAL duration + easing so backdrop and card
     animate as one unified gesture. */
  transition:
    background              0.85s cubic-bezier(0.4, 0, 0.2, 1),
    -webkit-backdrop-filter 0.85s cubic-bezier(0.4, 0, 0.2, 1),
    backdrop-filter         0.85s cubic-bezier(0.4, 0, 0.2, 1);
}
.modal-overlay.is-open {
  /* Warm off-black, not pure #000. Saturate boost keeps the warm
     tones of whatever sits behind alive through the blur. */
  background: rgba(15, 11, 8, 0.64);
  -webkit-backdrop-filter: blur(22px) saturate(145%);
          backdrop-filter: blur(22px) saturate(145%);
  pointer-events: auto;
}

/* Lock the page scroll when a modal is active. iOS Safari IGNORES
   overflow:hidden for the DOCUMENT scroll — the background still drags
   behind the modal — so we PIN the body with position:fixed at a
   negative offset equal to the current scrollY (set inline by
   lockScroll() in site.js). That genuinely freezes the page while a
   card is open. !important wins against the html.scroll-ready
   overflow-y:auto release. The exact scrollY is restored in JS via
   window.scrollTo on close, since position:fixed resets the visual
   scroll to the top. (v=97) */
html.modal-open {
  overflow: hidden !important;
}
html.modal-open body {
  position: fixed;
  left: 0;
  right: 0;
  width: 100%;
  overflow: hidden !important;
  /* `top` is set inline by lockScroll() to -<scrollY>px. */
}

.modal-card {
  position: relative;
  /* All cards stack at the same grid cell (row 1 col 1) so only the
     .is-active one is ever visible — no display:none toggling, which
     used to break Safari's transitions (animating elements freshly
     promoted from display:none often skips the transition entirely). */
  grid-area: 1 / 1;
  width: 100%;
  max-width: 620px;
  /* Cap the card height to the viewport (svh — see card-wrapper rule
     for the long version on why svh, not dvh, on iOS Safari). Content
     scrolls inside the card if it overflows. */
  max-height: calc(100vh - 32px);
  max-height: calc(100svh - 32px);
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  /* Keep scroll momentum inside the card — don't let a flick that hits
     the top/bottom edge chain out to anything behind it (v=97). */
  overscroll-behavior: contain;
  /* Hide the native scrollbar — it sat against the card's right edge and
     broke the clean look. The card stays fully scrollable; only the
     visible track/thumb is removed. Firefox + legacy Edge below;
     WebKit (Safari/Chrome) via ::-webkit-scrollbar (v=100). */
  scrollbar-width: none;          /* Firefox */
  -ms-overflow-style: none;       /* old Edge / IE */

  background: #FFFCF7;
  border: 1px solid rgba(214, 196, 174, 0.65);
  border-radius: clamp(22px, 2.4vw, 28px);

  /* Three-shadow stack: inset top highlight (light from above),
     deep diffusion shadow (separates card from blurred backdrop),
     near contact shadow (defines the edge). Tinted warm so it
     reads as ambient light, not a hard drop. */
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.55),
    0 36px 90px rgba(20, 12, 8, 0.45),
    0 12px 24px rgba(20, 12, 8, 0.22);

  padding: clamp(28px, 4.2vw, 52px);

  /* IDLE STATE — invisible and clearly small. scale(0.82) gives a
     visible 18% growth so the user actually sees the card EXPAND
     gradually as the backdrop dims. visibility:hidden removes it
     from accessibility tree and pointer events when inactive; we
     transition visibility with a delay so it stays "visible" through
     the close animation and only flips after opacity reaches 0. */
  opacity: 0;
  visibility: hidden;
  transform: scale(0.82);
  pointer-events: none;

  /* IDENTICAL duration and easing to the backdrop — they're animated
     as one unified gesture. Material Standard ease (0.4, 0, 0.2, 1)
     distributes motion evenly across the duration so each stage
     (small → medium-small → medium → near-full → full) is
     individually visible. */
  transition:
    opacity    0.85s cubic-bezier(0.4, 0, 0.2, 1),
    transform  0.85s cubic-bezier(0.4, 0, 0.2, 1),
    visibility 0s linear 0.85s;
}
/* Hide the native scrollbar on WebKit (Safari/Chrome). Pairs with the
   scrollbar-width:none / -ms-overflow-style:none on .modal-card above.
   Card remains scrollable; only the visible track/thumb is removed. */
.modal-card::-webkit-scrollbar { width: 0; height: 0; display: none; }

.modal-card.is-active {
  /* Marker class — only the active card gets the open-state styles
     when the overlay also has .is-open. */
}
.modal-overlay.is-open .modal-card.is-active {
  opacity: 1;
  visibility: visible;
  transform: scale(1);
  pointer-events: auto;
  transition:
    opacity    0.85s cubic-bezier(0.4, 0, 0.2, 1),
    transform  0.85s cubic-bezier(0.4, 0, 0.2, 1),
    visibility 0s linear;
}

/* Features card runs wider — 2-col grid needs room to breathe. */
.modal-card--features { max-width: 720px; }

/* CLOSE BUTTON — top-right, warm-tan tinted bubble, hover rotates X. */
.modal-close {
  position: absolute;
  top: clamp(14px, 1.6vw, 20px);
  right: clamp(14px, 1.6vw, 20px);
  width: 38px;
  height: 38px;
  border-radius: 50%;
  background: rgba(214, 196, 174, 0.16);
  border: 1px solid rgba(214, 196, 174, 0.42);
  color: #6F6258;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  z-index: 2;
  transition:
    background 0.25s var(--ease-flow),
    border-color 0.25s var(--ease-flow),
    color 0.25s var(--ease-flow),
    transform 0.3s var(--ease-flow);
}
.modal-close:hover {
  background: rgba(201, 148, 92, 0.16);
  border-color: rgba(201, 148, 92, 0.42);
  color: #B98958;
  transform: rotate(90deg);
}
.modal-close:focus-visible {
  outline: 2px solid rgba(185, 137, 88, 0.55);
  outline-offset: 3px;
}
.modal-close svg { width: 16px; height: 16px; }

/* CARD CONTENT LAYOUT — head (eyebrow + title + lead) above body.
   All children centered horizontally within the card. The ch-based
   max-widths on title/lead/body still apply but the blocks are
   margin-auto'd into the centre so they read as a balanced column. */
.modal-card__head {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: clamp(14px, 1.6vw, 20px);
  margin-bottom: clamp(20px, 2.6vw, 32px);
}
.modal-card__body {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: clamp(18px, 2.2vw, 24px);
}

.modal-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  font-family: 'JetBrains Mono', 'Geist Mono', ui-monospace, monospace;
  font-size: 11.5px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: rgba(43, 38, 34, 0.45);
}
.modal-eyebrow__num   { color: #B98958; }
.modal-eyebrow__label { color: rgba(43, 38, 34, 0.72); }
.modal-eyebrow__dot {
  width: 4px; height: 4px;
  border-radius: 50%;
  background: #C9945C;
  box-shadow:
    0 0 0 4px rgba(201, 148, 92, 0.14),
    0 0 12px rgba(201, 148, 92, 0.40);
}

.modal-title {
  font-family: 'Playfair Display', 'Onest', Georgia, serif;
  font-weight: 500;
  font-size: clamp(32px, 4.4vw, 52px);
  line-height: 1.04;
  letter-spacing: -0.022em;
  color: #25211E;
  margin: 0 auto;
  max-width: 18ch;
}

.modal-lead {
  font-family: 'Onest', sans-serif;
  font-size: clamp(16px, 1.4vw, 19px);
  line-height: 1.5;
  letter-spacing: -0.005em;
  color: #6F6258;
  margin: 0 auto;
  max-width: 44ch;
}

.modal-body {
  font-family: 'Onest', sans-serif;
  font-size: clamp(15px, 1.18vw, 17px);
  line-height: 1.65;
  color: #5B5048;
  margin: 0 auto;
  max-width: 58ch;
}

.modal-card__foot { margin-top: 4px; }
.modal-pill {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 8px 14px;
  border-radius: 999px;
  background: rgba(201, 148, 92, 0.10);
  border: 1px solid rgba(201, 148, 92, 0.32);
  font-family: 'JetBrains Mono', 'Geist Mono', ui-monospace, monospace;
  font-size: 10.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: #B98958;
}
.modal-pill__dot {
  width: 6px; height: 6px;
  border-radius: 50%;
  background: #C9945C;
  box-shadow: 0 0 10px rgba(201, 148, 92, 0.55);
}

/* FEATURE GRID — 2 columns on desktop, 1 column on phones. NOT a
   3-col equal-card row (banned by the design system). Each cell is
   left-aligned, asymmetric within the cell (icon top-left, then
   stack title + desc). Hover lifts slightly. */
.feature-grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(14px, 1.6vw, 20px);
}
.feature-cell {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 10px;
  padding: clamp(16px, 1.8vw, 22px);
  background: rgba(255, 252, 247, 0.65);
  border: 1px solid rgba(214, 196, 174, 0.5);
  border-radius: 16px;
  transition:
    border-color 0.3s var(--ease-flow),
    transform 0.3s var(--ease-flow),
    box-shadow 0.3s var(--ease-flow);
}
.feature-cell:hover {
  border-color: rgba(201, 148, 92, 0.45);
  transform: translateY(-2px);
  box-shadow: 0 10px 24px rgba(65, 48, 35, 0.10);
}
.feature-cell__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: 12px;
  background: radial-gradient(circle at 50% 35%,
    rgba(201, 148, 92, 0.18) 0%,
    rgba(201, 148, 92, 0.05) 100%);
  border: 1px solid rgba(201, 148, 92, 0.26);
  color: #B98958;
  box-shadow:
    inset 0 1px 0 rgba(255, 245, 230, 0.5),
    0 4px 10px rgba(201, 148, 92, 0.08);
}
.feature-cell__icon svg { width: 22px; height: 22px; }
.feature-cell__title {
  font-family: 'Onest', sans-serif;
  font-size: 16px;
  font-weight: 600;
  letter-spacing: -0.005em;
  color: #25211E;
  margin: 0;
}
.feature-cell__desc {
  font-family: 'Onest', sans-serif;
  font-size: 13.5px;
  line-height: 1.5;
  color: #6F6258;
  margin: 0;
}

/* MODAL MOBILE — single-column features, tighter paddings, smaller
   close button. Match the existing site's 600px/900px breakpoints. */
@media (max-width: 700px) {
  .modal-overlay   { padding: 14px; }
  .modal-card      { padding: 28px 22px 32px; border-radius: 22px;
                     max-height: calc(100vh - 28px);
                     max-height: calc(100svh - 28px); }
  .modal-card--features { max-width: 100%; }
  .modal-title     { font-size: clamp(26px, 7.4vw, 34px); max-width: 100%; }
  .modal-lead      { font-size: 15.5px; }
  .modal-body      { font-size: 15px; }
  .modal-close     { top: 12px; right: 12px; width: 36px; height: 36px; }
  .modal-close svg { width: 14px; height: 14px; }
  .feature-grid    { grid-template-columns: 1fr; gap: 12px; }
  .feature-cell    { padding: 16px 16px 18px; }
}

/* ===================================================================
   SCROLL STACK — MOBILE
   Drop the asymmetric grid, stack each card's lead + visual vertically,
   shrink type, soften the fold-back so the GSAP rotation doesn't clip
   off-screen on tall narrow phones.
   =================================================================== */
@media (max-width: 900px) {
  .card-wrapper {
    padding: 108px 18px 40px;
    height: 100vh;
    /* svh, not dvh — see comment on the base rule. iOS Safari address
       bar would constantly half-toggle on slow drags; every micro-toggle
       changed dvh and every wrapper jittered in lockstep. */
    height: 100svh;
  }
  .glass-card,
  .glass-card--01,
  .glass-card--02,
  .glass-card--03,
  .glass-card--04 {
    grid-template-columns: 1fr;
    gap: 28px;
    padding: 28px 22px;
    min-height: auto;
    align-items: start;
  }
  /* On mobile, the lead column always reads first regardless of which
     side the visual was on at desktop. Visual sits below it. */
  .glass-card__lead { order: 1; max-width: 100%; }
  .card-visual      { order: 2; }

  .card-title { font-size: clamp(28px, 8vw, 38px); }
  .card-body  { font-size: 15px; max-width: 100%; }

  /* Cards 01-03 use the editorial title scale on phones — paragraph
     lead, not poster type. */
  .glass-card--01 .card-title,
  .glass-card--02 .card-title,
  .glass-card--03 .card-title {
    font-size: clamp(22px, 6.2vw, 30px);
    max-width: none;
  }

  /* Live "now" timeline — tighten spacing and shrink mono on phones. */
  .always-on              { gap: 12px; padding: 8px 0 4px; max-width: 100%; }
  .always-on__track       { margin-top: 28px; }
  .always-on__marker-label{ font-size: 9px; letter-spacing: 0.24em; }
  .always-on__ticks       { font-size: 9.5px; letter-spacing: 0.12em; }
  .always-on__caption     { font-size: 10.5px; letter-spacing: 0.2em; margin-top: 8px; }

  /* Rotating action-stream — compress the stage height and shrink the
     icon tile + label so the panel sits comfortably in the narrow
     mobile column without dominating the lead text above. */
  .action-stream          { padding: 18px; max-width: 100%; gap: 14px; }
  .action-stream__header  { font-size: 9.5px; letter-spacing: 0.2em; }
  .action-stream__list    { height: 160px; }
  .action-stream__icon    { width: 52px; height: 52px; border-radius: 14px; }
  .action-stream__icon svg{ width: 24px; height: 24px; }
  .action-stream__label   { font-size: 15.5px; max-width: 260px; }
  .action-stream__time    { font-size: 9.5px; letter-spacing: 0.1em; }

  /* Live conversation thread — tighten the bubble + thread metrics on
     phones so the typewriter sequence sits comfortably below the
     centred lead column. */
  .chat-thread                 { padding: 18px; max-width: 100%; gap: 12px; }
  .chat-thread__header         { font-size: 9.5px; letter-spacing: 0.2em; }
  .chat-thread__messages       { min-height: 230px; gap: 6px; }
  .chat-thread__bubble         { font-size: 12.5px; padding: 9px 12px; }
  .chat-thread__time           { font-size: 9px; letter-spacing: 0.06em; }
  .chat-thread__break          { font-size: 9px; letter-spacing: 0.18em; }

  /* Voice demo — compress the player so the waveform + button + meta
     fit naturally below the centred lead. Stage shrinks; bars stay
     readable; play button shrinks slightly. */
  .voice-demo                  { padding: 20px; max-width: 100%; gap: 14px; }
  .voice-demo__header          { font-size: 9.5px; letter-spacing: 0.2em; }
  .voice-demo__stage           { height: 78px; transition: height 0.4s var(--ease-flow); }
  .voice-demo__waveform        { gap: 2px; }
  .voice-demo__play            { padding: 11px 22px; font-size: 13.5px; }
  .voice-demo__meta            { font-size: 10px; letter-spacing: 0.08em; }

  /* When captions appear, shrink the waveform stage so the card's
     total height stays close to its idle value. svh budget on
     smaller phones is tight; this keeps content from overflowing
     and re-triggering the iOS sticky-overflow bug. */
  .voice-demo.captions-active .voice-demo__stage { height: 56px; }
  .voice-demo__captions        { max-width: 100%; }
  .voice-demo__caption-stage   { font-size: 12.5px; }

  .setup-flow,
  .status-24,
  .voice-print,
  .cap-grid { max-width: 100%; }

  .voice-print__bars { height: 86px; }
  .status-24__core   { width: 110px; height: 110px; }
  .status-24__time   { font-size: 40px; }

  /* iOS Safari sometimes botches backdrop-filter inside a sticky +
     transformed element. Bump the blur down a touch — still reads as
     glass, far cheaper on mobile GPUs. */
  .glass-card {
    -webkit-backdrop-filter: blur(22px) saturate(150%);
            backdrop-filter: blur(22px) saturate(150%);
    /* Drop the permanent will-change on the phone path (v=96). The mobile
       cards never run the filter fold-back (that's desktop-only), so a
       persistent filter-capable backing store on all four cards was pure
       GPU-memory pressure on iOS — one of the things making scroll
       stutter. backdrop-filter still gives each card its own composited
       layer, so nothing changes visually. */
    will-change: auto;
  }

  /* SCROLL-PERF · DOSE 1 — paper-breathe is a continuous full-viewport
     blend-mode recomposite (5 layers · multiply/soft-light/multiply/screen)
     that drains the iOS compositor even at rest. At 28s drift speed it's
     invisible on a phone anyway — the texture's colors, vignette and
     grain all stay; only the breathing motion stops. Frees the GPU so
     scroll has headroom. The will-change is also released since nothing
     is animating anymore on mobile. */
  .page-texture {
    animation: none;
    will-change: auto;
  }
}

@media (max-width: 600px) {
  .glass-card { padding: 24px 18px; border-radius: 22px; }
  .card-eyebrow { font-size: 10.5px; }
  .cap-grid { gap: 10px; }
  .cap-tile { padding: 18px; }
}

/* ===================================================================
   REDUCED MOTION — collapse the stack to a simple vertical reveal.
   No scrubs, no fold-back. Cards just sit naturally and breathe.
   =================================================================== */
@media (prefers-reduced-motion: reduce) {
  .card-wrapper { position: relative; height: auto; padding: 60px var(--gutter); }
  /* Image sequence frames already carry blur+dim baked in; nothing extra here. */
  .status-24__ring,
  .voice-print__bars span,
  .setup-flow__pulse,
  .voice-print__dot { animation: none; }
}

/* =====================================================================
   v=96 — iOS SCROLL PERFORMANCE (no visual change)

   The phone scroll lag isn't from scroll-linked transforms (the mobile
   path has none — cards just fade in once). It's from continuous
   compositing/paint work that competes with the scroll: four
   backdrop-filter cards, plus several infinite ambient animations
   (blurred amber glows, pulse dots, the idle waveform) that keep running
   forever — even on cards that are off-screen.

   Fix: pause those loops while their card is off-screen. Off-screen
   content is invisible, so freezing the loops changes nothing the user
   can see; it just frees GPU budget so the on-screen scroll stays
   smooth. site.js (wireViewportPause) adds .is-offscreen to any
   .card-wrapper / .hero that is FAR from the viewport — a generous
   300px rootMargin keeps the animation running well before any pixel
   appears, so there is no visible "snap-on".

   Fail-safe: the DEFAULT (no class, or JS not loaded) leaves every
   animation running exactly as before — this only ever PAUSES, never
   forces motion, so a missing IntersectionObserver is a no-op.
   ===================================================================== */
.card-wrapper.is-offscreen .action-stream::before,
.card-wrapper.is-offscreen .chat-thread::before,
.card-wrapper.is-offscreen .voice-demo::before,
.card-wrapper.is-offscreen .voice-demo__bar,
.card-wrapper.is-offscreen .action-stream__pulse,
.card-wrapper.is-offscreen .chat-thread__pulse,
.card-wrapper.is-offscreen .voice-demo__pulse,
.card-wrapper.is-offscreen .always-on__marker-pulse,
.hero.is-offscreen .hero__scroll svg,
.hero.is-offscreen .hero__scroll-chevron {
  animation-play-state: paused !important;
}

/* =====================================================================
   05 · CTA SECTION + „ИСКАМ" MODAL (v=98)
   Closing call-to-action after the card stack, plus the name+phone
   "magic" capture pop-up it opens (shared with the header/drawer
   „Искам Го" links via the #contact hash). On submit the form
   cross-fades to a typed confirmation; the real outbound call is wired
   in Phase 2 — the front-end here is complete + self-contained.
   ===================================================================== */

/* ---- CTA section ---------------------------------------------------- */
.cta {
  position: relative;
  z-index: 1;
  min-height: 90vh;
  min-height: 90svh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: clamp(18px, 2.6vw, 30px);
  padding: clamp(100px, 16vh, 180px) var(--gutter);
}
.cta .card-eyebrow { justify-content: center; }
.cta__title {
  font-family: 'Playfair Display', 'Onest', Georgia, serif;
  font-weight: 500;
  font-size: clamp(34px, 5.6vw, 68px);
  line-height: 1.03;
  letter-spacing: -0.025em;
  color: #25211E;
  margin: 0;
  max-width: 16ch;
}
.cta__subtitle {
  font-family: 'Onest', sans-serif;
  font-size: clamp(16px, 1.4vw, 20px);
  line-height: 1.6;
  color: #6F6258;
  margin: 0;
  max-width: 52ch;
}
.cta__btn {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  margin-top: clamp(8px, 1.4vw, 16px);
  padding: 18px 42px;
  font-family: 'Onest', sans-serif;
  font-size: clamp(16px, 1.3vw, 19px);
  font-weight: 600;
  letter-spacing: 0.01em;
  color: #FFF8EE;
  text-decoration: none;
  background: linear-gradient(135deg, #C9945C 0%, #A57645 100%);
  border: none;
  border-radius: 999px;
  cursor: pointer;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.32),
    0 16px 44px rgba(185, 137, 88, 0.34);
  transition: transform 0.25s var(--ease-flow),
              box-shadow 0.25s var(--ease-flow);
}
.cta__btn svg {
  width: 20px; height: 20px;
  transition: transform 0.25s var(--ease-flow);
}
.cta__btn:hover {
  transform: translateY(-3px);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.32),
    0 22px 56px rgba(185, 137, 88, 0.44);
}
.cta__btn:hover svg { transform: translateX(4px); }
.cta__btn:active { transform: translateY(-1px); }
.cta__btn:focus-visible {
  outline: 2px solid rgba(185, 137, 88, 0.7);
  outline-offset: 4px;
}

/* ---- „Искам" modal: form ↔ confirmation cross-fade ----------------- */
.want-stage { position: relative; }

.want-form {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(16px, 2vw, 22px);
  transition: opacity 0.4s var(--ease-flow), transform 0.4s var(--ease-flow);
}
.modal-card--want.is-sent .want-form {
  opacity: 0;
  transform: translateY(-10px);
  pointer-events: none;
}

.want-fields {
  display: flex;
  flex-direction: column;
  gap: 14px;
  width: 100%;
  max-width: 380px;
}
.want-field { display: flex; flex-direction: column; gap: 7px; text-align: left; }
.want-field label {
  font-family: 'JetBrains Mono', 'Geist Mono', ui-monospace, monospace;
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(43, 38, 34, 0.55);
}
.want-field input {
  width: 100%;
  padding: 14px 16px;
  font-family: 'Onest', sans-serif;
  font-size: 16px;            /* 16px keeps iOS Safari from zooming on focus */
  color: #25211E;
  background: #FFFCF7;
  border: 1px solid rgba(214, 196, 174, 0.7);
  border-radius: 12px;
  transition: border-color 0.25s var(--ease-flow),
              box-shadow 0.25s var(--ease-flow);
}
.want-field input::placeholder { color: rgba(111, 98, 88, 0.45); }
.want-field input:focus {
  outline: none;
  border-color: rgba(201, 148, 92, 0.75);
  box-shadow: 0 0 0 3px rgba(201, 148, 92, 0.15);
}

/* Phone field — FIXED +359 🇧🇬 prefix (Bulgarian numbers only). The
   wrapper carries the border; the inner input is borderless so the
   prefix + number read as one cohesive field. The visitor only types
   the local part; site.js prepends +359 on submit. */
.want-phone {
  display: flex;
  align-items: stretch;
  width: 100%;
  max-width: 380px;
  background: #FFFCF7;
  border: 1px solid rgba(214, 196, 174, 0.7);
  border-radius: 12px;
  overflow: hidden;
  transition: border-color 0.25s var(--ease-flow),
              box-shadow 0.25s var(--ease-flow);
}
.want-phone:focus-within {
  border-color: rgba(201, 148, 92, 0.75);
  box-shadow: 0 0 0 3px rgba(201, 148, 92, 0.15);
}
.want-phone__prefix {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 0 12px 0 16px;
  font-family: 'Onest', sans-serif;
  font-size: 16px;
  color: #25211E;
  white-space: nowrap;
  user-select: none;
  background: rgba(201, 148, 92, 0.06);
  border-right: 1px solid rgba(214, 196, 174, 0.7);
}
.want-phone__flag { font-size: 18px; line-height: 1; }
/* Borderless inner input — overrides the generic .want-field input box. */
.want-field .want-phone input {
  flex: 1;
  min-width: 0;
  margin: 0;
  border: none;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
  padding: 14px 16px;
  font-family: 'Onest', sans-serif;
  font-size: 16px;
  color: #25211E;
}
.want-field .want-phone input:focus {
  outline: none;
  border: none;
  box-shadow: none;
}

.want-error {
  min-height: 1.1em;
  margin: 0;
  font-family: 'Onest', sans-serif;
  font-size: 13px;
  color: #B4504A;
  text-align: center;
}

.want-submit {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 15px 38px;
  font-family: 'Onest', sans-serif;
  font-size: 16px;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: #FFF8EE;
  background: linear-gradient(135deg, #C9945C 0%, #A57645 100%);
  border: none;
  border-radius: 999px;
  cursor: pointer;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.3),
    0 12px 30px rgba(185, 137, 88, 0.32);
  transition: transform 0.25s var(--ease-flow),
              box-shadow 0.25s var(--ease-flow);
}
.want-submit:hover {
  transform: translateY(-2px);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.3),
    0 16px 38px rgba(185, 137, 88, 0.42);
}
.want-submit:active { transform: translateY(0); }
.want-submit:focus-visible {
  outline: 2px solid rgba(185, 137, 88, 0.7);
  outline-offset: 3px;
}

.want-fineprint {
  margin: 0;
  font-family: 'Onest', sans-serif;
  font-size: 11.5px;
  line-height: 1.5;
  color: rgba(111, 98, 88, 0.6);
  text-align: center;
  max-width: 320px;
}

/* Confirmation — overlays the stage, fades in as the form fades out. */
.want-done {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 22px;
  text-align: center;
  opacity: 0;
  transform: translateY(10px);
  pointer-events: none;
  transition: opacity 0.5s var(--ease-flow) 0.22s,
              transform 0.5s var(--ease-flow) 0.22s;
}
.modal-card--want.is-sent .want-done {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}

/* Pulsing amber dot — a subtle "incoming call" cue above the message. */
.want-done__ring {
  width: 16px; height: 16px;
  border-radius: 50%;
  background: #C9945C;
  box-shadow:
    0 0 0 4px rgba(201, 148, 92, 0.18),
    0 0 18px rgba(201, 148, 92, 0.6);
  animation: pulse-dot 2s var(--ease-flow) infinite;
}

.want-done__text {
  margin: 0;
  font-family: 'Onest', sans-serif;
  font-size: clamp(17px, 1.7vw, 22px);
  line-height: 1.5;
  color: #25211E;
  max-width: 26ch;
  min-height: 3em;     /* reserve space so the typewriter doesn't reflow */
}
/* Blinking caret while the line types itself out (reuses chat-caret). */
.want-done.is-typing .want-done__text::after {
  content: '';
  display: inline-block;
  width: 2px;
  height: 1em;
  margin-left: 2px;
  vertical-align: text-bottom;
  background: #B98958;
  animation: chat-caret 0.7s steps(2) infinite;
}

@media (max-width: 600px) {
  .cta { min-height: 84svh; }
  .cta__subtitle { font-size: 15px; }
  .cta__btn { width: 100%; max-width: 320px; justify-content: center; }
  .want-submit { width: 100%; }
}

/* =====================================================================
   v=101 — CARD CONTENT CHOREOGRAPHY · SPOTLIGHT · STACK RAIL

   Until now each glass card moved as ONE rigid slab (the scrubbed
   shell entrance) while everything inside it just sat there. This
   layer makes the inside of every card arrive as a cascade:

   1. CONTENT CASCADE — when a card becomes the "current" one
      (ScrollTrigger adds .is-active to the .glass-card), its content
      settles in sequence: eyebrow → title words (masked rise) →
      visual panel (GSAP, so the hover tilt can share the element) →
      body → meta hairline drawing itself + values.
      FAIL-SAFE: every hidden pre-state is gated on .is-armed, which
      ONLY site.js adds (and only when GSAP + ScrollTrigger exist and
      prefers-reduced-motion is off). No JS → cards render static and
      fully visible, exactly as before.
   2. SPOTLIGHT — desktop pointer only. A warm radial blob inside the
      card lags behind the cursor (GSAP quickTo) while the border
      warms up. The blob lives in its own clipped layer so the card
      itself needs no overflow change.
   3. STACK RAIL — fixed mono-numbered dashes at the right edge while
      the stack is on screen. Shows which of the four cards you're on;
      click jumps to a card. ≥1024px only.
   4. Card 01 gets its own micro-timeline: the 24h track fill sweeps
      out from the centre, ticks rise one by one, then the "СЕГА"
      marker pops in with an overshoot.

   Everything here is transform/opacity (compositor-only). The one-shot
   transitions add zero continuous GPU load on iOS.
   ===================================================================== */

/* The injected spotlight layer sits at z 0; real content above it. */
.glass-card { position: relative; }
.glass-card__lead,
.card-visual { position: relative; z-index: 1; }

/* ---- Title word masks ----------------------------------------------
   site.js splits .card-title / .cta__title into:
     <span class="tw"><span class="tw__inner">дума</span></span>
   The mask clips the word as it rises in. Generous padding (undone by
   negative margins, so layout is identical) keeps Cyrillic descenders
   (р у д ф) and Playfair's serif overhangs from being shaved by
   overflow:hidden at rest. -------------------------------------------- */
.tw {
  display: inline-block;
  overflow: hidden;
  vertical-align: top;
  padding: 0.12em 0.06em 0.18em;
  margin:  -0.12em -0.06em -0.18em;
}
.tw__inner { display: inline-block; }

.glass-card.is-armed .tw__inner,
.cta.is-armed .tw__inner {
  transform: translateY(150%);
  transition: transform 0.4s var(--ease-flow);
}
.glass-card.is-armed.is-active .tw__inner,
.cta.is-armed.is-active .tw__inner {
  transform: translateY(0);
  transition-duration: 0.9s;
  transition-delay: calc(0.14s + var(--wi, 0) * 0.06s);
}

/* ---- Lead column cascade (eyebrow → body → meta) ------------------- */
.glass-card.is-armed .card-eyebrow,
.glass-card.is-armed .card-body,
.glass-card.is-armed .card-meta {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity 0.3s var(--ease-flow), transform 0.3s var(--ease-flow);
}
.glass-card.is-armed.is-active .card-eyebrow,
.glass-card.is-armed.is-active .card-body,
.glass-card.is-armed.is-active .card-meta {
  opacity: 1;
  transform: translateY(0);
  transition-duration: 0.85s, 0.85s;
}
.glass-card.is-armed.is-active .card-eyebrow { transition-delay: 0.05s, 0.05s; }
.glass-card.is-armed.is-active .card-body    { transition-delay: 0.42s, 0.42s; }
.glass-card.is-armed.is-active .card-meta    { transition-delay: 0.56s, 0.56s; }

/* Meta hairline draws itself from the centre outward. The real
   border-top goes transparent (keeping its 1px of layout) and a
   pseudo-element takes its exact place so non-armed cards keep the
   plain static border. */
.glass-card.is-armed .card-meta { border-top-color: transparent; }
.glass-card.is-armed .card-meta::before {
  content: '';
  position: absolute;
  top: -1px; left: 0;
  width: 100%; height: 1px;
  background: rgba(214, 196, 174, 0.55);
  transform: scaleX(0);
  transition: transform 0.3s var(--ease-flow);
}
.glass-card.is-armed .card-meta { position: relative; }
.glass-card.is-armed.is-active .card-meta::before {
  transform: scaleX(1);
  transition: transform 1.0s cubic-bezier(0.22, 0.8, 0.2, 1) 0.62s;
}

/* ---- Card 01 micro-timeline — track sweep, tick rise, marker pop --- */
.glass-card--01.is-armed .always-on__track-fill {
  transform: scaleX(0);
  transition: transform 0.3s var(--ease-flow);
}
.glass-card--01.is-armed.is-active .always-on__track-fill {
  transform: scaleX(1);
  transition: transform 1.3s cubic-bezier(0.22, 0.8, 0.2, 1) 0.55s;
}
.glass-card--01.is-armed .always-on__marker {
  opacity: 0;
  transform: translate(-50%, -50%) scale(0.2);
  transition: opacity 0.25s var(--ease-flow), transform 0.25s var(--ease-flow);
}
.glass-card--01.is-armed.is-active .always-on__marker {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
  transition: opacity   0.4s  var(--ease-flow) 1.30s,
              transform 0.55s cubic-bezier(0.34, 1.56, 0.64, 1) 1.30s;
}
.glass-card--01.is-armed .always-on__ticks span {
  opacity: 0;
  transform: translateY(6px);
  transition: opacity 0.25s var(--ease-flow), transform 0.25s var(--ease-flow);
}
.glass-card--01.is-armed.is-active .always-on__ticks span {
  opacity: 1;
  transform: translateY(0);
  transition-duration: 0.6s, 0.6s;
}
.glass-card--01.is-armed.is-active .always-on__ticks span:nth-child(1) { transition-delay: 0.70s, 0.70s; }
.glass-card--01.is-armed.is-active .always-on__ticks span:nth-child(2) { transition-delay: 0.78s, 0.78s; }
.glass-card--01.is-armed.is-active .always-on__ticks span:nth-child(3) { transition-delay: 0.86s, 0.86s; }
.glass-card--01.is-armed.is-active .always-on__ticks span:nth-child(4) { transition-delay: 0.94s, 0.94s; }
.glass-card--01.is-armed.is-active .always-on__ticks span:nth-child(5) { transition-delay: 1.02s, 1.02s; }
.glass-card--01.is-armed .always-on__caption {
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 0.25s var(--ease-flow), transform 0.25s var(--ease-flow);
}
.glass-card--01.is-armed.is-active .always-on__caption {
  opacity: 1;
  transform: translateY(0);
  transition-duration: 0.7s, 0.7s;
  transition-delay: 1.45s, 1.45s;
}

/* ---- Spotlight — warm glow that follows the cursor ----------------- */
.glass-card__spotlight {
  position: absolute;
  inset: 0;
  z-index: 0;
  border-radius: inherit;
  overflow: hidden;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.6s var(--ease-flow);
}
.glass-card.is-lit .glass-card__spotlight { opacity: 1; }
.glass-card__spotlight-blob {
  position: absolute;
  top: -260px; left: -260px;     /* translate(x,y) centres it on cursor */
  width: 520px; height: 520px;
  border-radius: 50%;
  background: radial-gradient(circle,
    rgba(226, 174, 113, 0.20) 0%,
    rgba(226, 174, 113, 0.07) 42%,
    transparent 70%);
}

@media (hover: hover) and (pointer: fine) {
  .glass-card {
    transition: border-color 0.45s var(--ease-flow),
                box-shadow   0.45s var(--ease-flow);
  }
  .glass-card:hover {
    border-color: rgba(203, 161, 109, 0.9);
    box-shadow:
      0 36px 100px rgba(65, 48, 35, 0.17),
      0 0 0 1px rgba(203, 161, 109, 0.18);
  }
}

/* ---- CTA cascade — same language as the cards ---------------------- */
.cta.is-armed .card-eyebrow,
.cta.is-armed .cta__subtitle {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 0.3s var(--ease-flow), transform 0.3s var(--ease-flow);
}
.cta.is-armed .cta__btn {
  opacity: 0;
  transform: translateY(18px) scale(0.94);
  transition: opacity 0.3s var(--ease-flow), transform 0.3s var(--ease-flow);
}
.cta.is-armed.is-active .card-eyebrow,
.cta.is-armed.is-active .cta__subtitle {
  opacity: 1;
  transform: translateY(0);
  transition-duration: 0.85s, 0.85s;
}
.cta.is-armed.is-active .card-eyebrow   { transition-delay: 0.05s, 0.05s; }
.cta.is-armed.is-active .cta__subtitle  { transition-delay: 0.50s, 0.50s; }
.cta.is-armed.is-active .cta__btn {
  opacity: 1;
  transform: translateY(0) scale(1);
  transition:
    opacity   0.7s var(--ease-flow) 0.66s,
    transform 0.8s cubic-bezier(0.34, 1.4, 0.5, 1) 0.66s,
    box-shadow 0.25s var(--ease-flow);
}

/* ---- Stack rail — где-сте-в-колодата indicator ---------------------- */
.stack-rail {
  position: fixed;
  right: clamp(16px, 2vw, 34px);
  top: 50%;
  z-index: 60;
  display: flex;
  flex-direction: column;
  gap: 16px;
  transform: translateY(-50%) translateX(14px);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.5s var(--ease-flow), transform 0.5s var(--ease-flow);
}
.stack-rail.is-visible {
  opacity: 1;
  transform: translateY(-50%);
  pointer-events: auto;
}
.stack-rail__item {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 10px;
  padding: 5px 0;
  background: none;
  border: 0;
  cursor: pointer;
}
.stack-rail__num {
  font-family: 'JetBrains Mono', 'Geist Mono', ui-monospace, monospace;
  font-size: 9.5px;
  letter-spacing: 0.18em;
  color: rgba(43, 38, 34, 0.40);
  opacity: 0;
  transform: translateX(6px);
  transition: opacity 0.35s var(--ease-flow),
              transform 0.35s var(--ease-flow),
              color 0.35s var(--ease-flow);
}
.stack-rail__bar {
  width: 18px;
  height: 2px;
  border-radius: 999px;
  background: rgba(111, 98, 88, 0.35);
  transform-origin: 100% 50%;
  transition: background 0.35s var(--ease-flow),
              transform 0.35s var(--ease-flow),
              box-shadow 0.35s var(--ease-flow);
}
.stack-rail__item:hover .stack-rail__bar {
  background: rgba(111, 98, 88, 0.6);
  transform: scaleX(1.25);
}
.stack-rail__item:hover .stack-rail__num { opacity: 1; transform: translateX(0); }
.stack-rail__item.is-current .stack-rail__bar {
  background: #C9945C;
  transform: scaleX(1.6);
  box-shadow: 0 0 12px rgba(201, 148, 92, 0.55);
}
.stack-rail__item.is-current .stack-rail__num {
  opacity: 1;
  transform: translateX(0);
  color: #B98958;
}
.stack-rail__item:focus-visible {
  outline: 2px solid rgba(185, 137, 88, 0.6);
  outline-offset: 3px;
  border-radius: 6px;
}
@media (max-width: 1279px) { .stack-rail__num { display: none; } }
@media (max-width: 1023px) { .stack-rail { display: none; } }

/* ---- Mobile: tighter cascade offsets, faster word stagger ---------- */
@media (max-width: 900px) {
  .glass-card.is-armed .card-eyebrow,
  .glass-card.is-armed .card-body,
  .glass-card.is-armed .card-meta,
  .cta.is-armed .card-eyebrow,
  .cta.is-armed .cta__subtitle { transform: translateY(12px); }
  .glass-card.is-armed.is-active .tw__inner,
  .cta.is-armed.is-active .tw__inner {
    transition-delay: calc(0.10s + var(--wi, 0) * 0.045s);
  }
}

/* =====================================================================
   v=102 — HERO LIFT-AWAY · ACTIVATION MOMENTS (02/04) · CTA PRESENCE ·
   EYEBROW ODOMETER

   1. HERO LIFT-AWAY is GSAP-inline (desktop-only scrub created in
      wireScrollStack) — no CSS here. The mobile path keeps its
      zero-scroll-tween rule (v=96), so iOS pays nothing.
   2. EYEBROW ODOMETER — site.js wraps each card/CTA eyebrow number in
      a two-row mask (<span class="num-roll"><span>01</span><span>02
      </span></span>); activation rolls the previous number out and the
      current one in. Same masked-rise language as the title words.
   3. CARD 04 WAVEFORM BLOOM — armed bars hold flat; activation runs a
      one-shot left→right bloom that hands off into the regular idle
      wave at the idle keyframe's exact start amplitude (scaleY 0.18),
      so the chain is seamless. Pure CSS, one-shot, obeys the v=96
      offscreen pause. Real Web-Audio playback always wins via a
      higher-specificity .is-analyzing override.
   4. CTA HALO — a static amber radial behind „Искам" (zero runtime
      cost on iOS); desktop pointers additionally get a slow scale
      breathe, paused offscreen. The magnetic pull itself is GSAP
      (wireCtaReveal), desktop fine-pointers only.
   ===================================================================== */

/* ---- Eyebrow number odometer ---------------------------------------- */
.card-eyebrow__num.has-roll {
  display: inline-block;
  overflow: hidden;
  height: 1em;
  line-height: 1;
}
.card-eyebrow__num .num-roll {
  display: flex;
  flex-direction: column;
}
.card-eyebrow__num .num-roll > span {
  display: block;
  height: 1em;
  line-height: 1;
}
.glass-card.is-armed .num-roll,
.cta.is-armed .num-roll {
  transform: translateY(0);            /* shows the PREVIOUS number   */
  transition: transform 0.3s var(--ease-flow);
}
.glass-card.is-armed.is-active .num-roll,
.cta.is-armed.is-active .num-roll {
  transform: translateY(-1em);         /* rolls up to the CURRENT one */
  transition: transform 0.7s cubic-bezier(0.22, 0.8, 0.2, 1) 0.16s;
}

/* ---- Card 04 — waveform bloom on activation -------------------------- */
/* Armed & not yet active: idle suspended, bars flat. Invisible anyway —
   the visual panel is GSAP-hidden until activation — but it guarantees
   the bloom always starts from silence. */
.glass-card--04.is-armed:not(.is-active) .voice-demo__bar {
  animation: none;
  transform: scaleY(0.06);
}
/* Activation: bloom (one-shot, staggered left→right via --i) chained
   into the idle wave. The idle delay = bloom delay + bloom duration,
   and the bloom's 100% frame equals the idle's 0% frame (scaleY 0.18),
   so each bar hands off without a jump. `both` fill holds the flat
   pose during the per-bar delay. */
.glass-card--04.is-armed.is-active .voice-demo__bar {
  animation:
    voice-demo-bloom 0.55s cubic-bezier(0.34, 1.4, 0.5, 1)
      calc(0.45s + var(--i, 0) * 0.028s) 1 both,
    voice-demo-idle 2.6s ease-in-out
      calc(1.00s + var(--i, 0) * 0.028s) infinite;
}
@keyframes voice-demo-bloom {
  0%   { transform: scaleY(0.06); }
  62%  { transform: scaleY(0.58); }
  100% { transform: scaleY(0.18); }
}
/* Live frequency analysis (playback) must always beat the activation
   animation — same trick as the base .is-analyzing rule, one class
   deeper so it out-specifies the rule above. */
.glass-card--04.is-armed.is-active .voice-demo.is-analyzing .voice-demo__bar {
  animation: none;
}

/* ---- CTA — amber halo behind „Искам" --------------------------------- */
.cta__btn { position: relative; }
.cta__btn::before {
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
  width: min(380px, 88vw);
  height: 210px;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(closest-side,
    rgba(201, 148, 92, 0.34) 0%,
    rgba(201, 148, 92, 0.12) 52%,
    transparent 76%);
  z-index: -1;
  pointer-events: none;
  opacity: 1;
  /* Fades in late in the CTA cascade (after the button lands). */
  transition: opacity 0.9s var(--ease-flow) 0.85s;
}
.cta.is-armed:not(.is-active) .cta__btn::before {
  opacity: 0;
  transition: opacity 0.3s var(--ease-flow);
}
/* Desktop pointers: slow breathe while the CTA is the active section.
   Scale-only (opacity stays owned by the cascade transition above).
   Paused when the section is far offscreen (wireViewportPause). */
@media (hover: hover) and (pointer: fine) {
  .cta.is-active .cta__btn::before {
    animation: cta-halo-breathe 7s ease-in-out infinite;
  }
  .cta.is-offscreen .cta__btn::before {
    animation-play-state: paused;
  }
}
@keyframes cta-halo-breathe {
  0%, 100% { transform: translate(-50%, -50%) scale(1); }
  50%      { transform: translate(-50%, -50%) scale(1.10); }
}

/* ---- v=102 reduced-motion belt --------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .card-eyebrow__num .num-roll {
    transform: translateY(-1em) !important;  /* rest on the CURRENT number */
    transition: none !important;
  }
  .glass-card--04.is-armed .voice-demo__bar {
    animation: none !important;
    transform: scaleY(0.3) !important;       /* calm static bars */
  }
  .cta__btn::before { animation: none !important; }
}

/* ---- Reduced motion: belt & suspenders ------------------------------
   site.js never arms anything under prefers-reduced-motion, but if the
   OS setting flips mid-session the armed pre-states must not hide
   content. Force everything visible and static. */
@media (prefers-reduced-motion: reduce) {
  .glass-card.is-armed .card-eyebrow,
  .glass-card.is-armed .card-body,
  .glass-card.is-armed .card-meta,
  .glass-card.is-armed .tw__inner,
  .glass-card--01.is-armed .always-on__track-fill,
  .glass-card--01.is-armed .always-on__marker,
  .glass-card--01.is-armed .always-on__ticks span,
  .glass-card--01.is-armed .always-on__caption,
  .cta.is-armed .card-eyebrow,
  .cta.is-armed .cta__subtitle,
  .cta.is-armed .cta__btn,
  .cta.is-armed .tw__inner {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
  .glass-card--01.is-armed .always-on__marker {
    transform: translate(-50%, -50%) !important;
  }
  .glass-card.is-armed .card-meta::before { transform: scaleX(1) !important; }
  .glass-card__spotlight,
  .stack-rail { display: none !important; }
}
