/* ==========================================================================
   MINDBITES — CROSS-DEVICE POLISH  (refactored, single source of truth)
   Load AFTER styles.css:  <link rel="stylesheet" href="mobile-polish.css">

   Breakpoint standard used throughout this file:
     • Mobile:  max-width: 767px
     • Tablet:  min-width: 768px and max-width: 1023px
     • Desktop: min-width: 1024px

   Motion tokens (single place duration & easing live):
     --ease-premium  cubic-bezier(0.16, 1, 0.3, 1)  ("easeOutExpo")
     --dur-sm .45s   /  --dur-md .72s  /  --dur-lg 1.05s

   Rules to preserve when editing:
     - Only animate opacity/transform. NEVER box-shadow/filter/width.
     - `will-change` only while element is mid-animation; release once settled.
     - Every eye-visible spacing snapped to 8-pt grid (8/16/24/32/48).
     - Every WebKit-only property has its `-webkit-` prefix.
   ========================================================================== */


/* ─────────────────────────────────────────────────────────────────────────────
   0. DESIGN TOKENS
   ───────────────────────────────────────────────────────────────────────────── */
:root{
  --ease-premium: cubic-bezier(0.16, 1, 0.3, 1);
  --dur-sm: .6s;     /* bumped — matches the loop-circle pop timing the user likes */
  --dur-md: 1.0s;    /* bumped — Windows Chrome reads faster durations as janky */
  --dur-lg: 1.6s;    /* matches the loop-path draw (1.6s) exactly — the "gold standard"
                        smoothness reference in this project. Slower = smoother on
                        integrated GPUs, because per-frame delta is smaller. */

  /* Font fallback stack — Manrope+Inter primary, then full cross-OS
     system fallback (Segoe UI on Windows, San Francisco on macOS/iOS,
     Roboto on Android) so an offline visitor never sees ugly Times New Roman. */
  --font-fallback: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
                   "Helvetica Neue", Arial, sans-serif;
}
/* Override styles.css's shorter font stacks with the full fallback set. */
body,
input, button, select, textarea{
  font-family: "Manrope", "Inter", var(--font-fallback);
}
h1, h2, h3, h4, h5, h6{
  font-family: "Manrope", "Inter", var(--font-fallback);
}


/* ─────────────────────────────────────────────────────────────────────────────
   1. BROWSER COMPAT & POLISH (all viewports)
   ───────────────────────────────────────────────────────────────────────────── */

/* Font rendering — see §41. The three declarations that used to sit here
   (`-webkit-font-smoothing: antialiased`, `-moz-osx-font-smoothing:
   grayscale`, `text-rendering: optimizeLegibility`) are the macOS half of the
   "text is not straight" problem and are now set to their defaults there. */
body{
  -webkit-font-smoothing: auto;
  -moz-osx-font-smoothing: auto;
  text-rendering: auto;
}

/* iOS/Android tap fixes */
html{ -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
a, button, .chip, .acc>button, .opt, .nav-btn, .fchip, .pill{
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;                /* removes ~300ms mobile tap delay */
}
.mb-ticker-track{
  -webkit-user-select: none;                 /* was missing — Safari < 16 fix */
  user-select: none;
}

/* iOS Safari zooms into any input with font-size < 16px — force minimum on mobile. */
@media (max-width: 767px){
  input, select, textarea, .search input{ font-size: 16px !important; }
}

/* Dynamic viewport height for iOS URL-bar collapse. */
@supports (height: 100dvh){
  .sheet-panel, .drawer{ height: 100dvh; }
  .sim{ max-height: 92dvh; }
}
.sheet-panel, .drawer, .sim{ -webkit-overflow-scrolling: touch; }


/* ─────────────────────────────────────────────────────────────────────────────
   2. TYPOGRAPHY
   ───────────────────────────────────────────────────────────────────────────── */

/* Small-label readability bumps — audit flagged these as too tight (11-12px). */
.verify        { font-size: 12px; }                       /* was 10px  */
.fact .lbl     { font-size: 12px; }                       /* was 11px  */
.disclaimer    { font-size: 13px; line-height: 1.55; }    /* was 12px  */

/* Section numbers removed from every eyebrow. */
.eyebrow .num{ display: none; }

/* WebKit text-shadow fix — large-blur shadows on headings rasterize as hard
   grey rectangles inside overflow:hidden reveal wrappers in Safari. */
h1, h2, body.dark h1, body.dark h2{ text-shadow: none; }

/* Mobile: allow long headings to wrap, long unbreakable strings to break. */
@media (max-width: 767px){
  h1, h2, h3{
    overflow-wrap: break-word;
    word-wrap: break-word;
    word-break: normal;
    hyphens: manual;
  }
  p, li, .lead, .muted, .fine, .note, .crumbs, .acc-panel .inner{
    overflow-wrap: anywhere;
    word-wrap: break-word;
  }
  .tag, .pill, .chip, .fchip, .status{ max-width: 100%; white-space: normal; }
}
pre, code{ overflow-wrap: anywhere; word-wrap: break-word; white-space: pre-wrap; }


/* ─────────────────────────────────────────────────────────────────────────────
   3. HORIZONTAL-OVERFLOW GUARDS (mobile safety net)
   ───────────────────────────────────────────────────────────────────────────── */
@media (max-width: 767px){
  html, body{ overflow-x: hidden; max-width: 100vw; }

  img, video, svg, canvas, iframe, picture{ max-width: 100%; height: auto; }
  .chart-card canvas{ height: auto; max-width: 100%; }

  .search, .search input{ min-width: 0; }       /* styles.css sets 200px, overflows small phones */
  .grid > *, .hero > *, .facts > *,
  .sim-metrics > *, .readout > *{ min-width: 0; }

  .thunder-flash, #scroll-progress{
    max-width: 100vw; left: 0; right: 0;
  }
}
/* Table safety — universal, if any FAQ answer ever includes tabular data. */
table{
  max-width: 100%;
  display: block;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}


/* ─────────────────────────────────────────────────────────────────────────────
   4. TOUCH TARGETS (WCAG 2.5.5 / Apple HIG 44×44px)
   ───────────────────────────────────────────────────────────────────────────── */
.btn--sm     { min-height: 44px; }                      /* CRITICAL: was 36px  */
.acc > button{ min-height: 48px; }                      /* FAQ accordion header */
.ft-bottom .links a,
.ft-bottom .links span{
  display: inline-flex; align-items: center;
  min-height: 32px; padding: 4px 0;                      /* larger tap area for footer links */
}


/* ─────────────────────────────────────────────────────────────────────────────
   5. HEADLINE DESCENDER FIX + PRODUCT-IMAGE SHADOW POLISH
   ───────────────────────────────────────────────────────────────────────────── */

/* clip-reveal wrapper: room for descenders (g, y, p, j) inside overflow:hidden. */
.clip-reveal{ padding-bottom: .22em; margin-bottom: -.22em; }
.clip-reveal-inner{ padding-bottom: .02em; }

/* Mobile: cancel the negative-margin trick (no clip-reveal animation runs on
   mobile — see §12), so heading text can never overlap the eyebrow above. */
@media (max-width: 767px){
  .clip-reveal{ padding-bottom: 0 !important; margin-bottom: 0 !important; }
}

/* WebKit drop-shadow fix on product image — stacked heavy filters get
   clipped into a hard band in Safari. Keep one small filter on desktop
   only, plus a gradient ground shadow (pure gradient, no filter). */
.mb-hero-img, .mb-panel-img{
  filter: drop-shadow(0 10px 16px rgba(0,0,0,.14));
}
body.dark .mb-hero-img, body.dark .mb-panel-img{
  filter: drop-shadow(0 10px 16px rgba(0,0,0,.40));
}
@media (max-width: 767px){
  .mb-hero-img, .mb-panel-img,
  body.dark .mb-hero-img, body.dark .mb-panel-img{ filter: none; }
}

.hero-img-wrap::after,
.imgbox.mb-imgbox-clean::after{
  content: ""; display: block; position: absolute; left: 50%; bottom: 3%;
  width: min(62%, 330px); height: clamp(14px, 4vw, 28px);
  transform: translateX(-50%); border-radius: 50%;
  background: radial-gradient(closest-side,
    rgba(23,25,31,.15), rgba(23,25,31,.06) 55%, rgba(23,25,31,0) 100%);
  z-index: 1; pointer-events: none;
}
body.dark .hero-img-wrap::after,
body.dark .imgbox.mb-imgbox-clean::after{
  background: radial-gradient(closest-side,
    rgba(0,0,0,.55), rgba(0,0,0,.22) 55%, rgba(0,0,0,0) 100%);
}
@media (max-width: 767px){
  .hero-img-wrap::after{ bottom: 13%; width: 48%; }
}
@media (max-width: 519px){
  .hero-img-wrap::after{ bottom: 17%; width: 44%; }
}


/* ─────────────────────────────────────────────────────────────────────────────
   6. BACKGROUNDS
   ───────────────────────────────────────────────────────────────────────────── */
.bg-cloud{ background: var(--cloud); }


/* ─────────────────────────────────────────────────────────────────────────────
   7. FAQ ACCORDION
   ───────────────────────────────────────────────────────────────────────────── */
.acc.open > button{ padding-bottom: 8px; }
.acc-panel .inner{ padding: 2px 20px 16px; line-height: 1.55; font-size: 15px; }
.acc-panel .inner p{ margin: 0 0 10px; }
.acc-panel .inner > *:last-child{ margin-bottom: 0; }
.acc-panel .inner a, .acc-panel .inner button{ margin-right: 4px; }
@media (max-width: 519px){
  .acc-panel .inner{ padding: 2px 14px 13px; font-size: 14px; }
}


/* ─────────────────────────────────────────────────────────────────────────────
   8. MOTION SYSTEM  (all reveal animations, one place)
   ───────────────────────────────────────────────────────────────────────────── */

/* .sr / .sr-left / .sr-right — used site-wide by app.js for scroll reveals. */
.sr, .sr-left, .sr-right{
  transition: opacity var(--dur-lg) var(--ease-premium),
              transform var(--dur-lg) var(--ease-premium) !important;
}
.sr:not(.sr-visible){        transform: translate3d(0, 28px, 0); }
.sr-left:not(.sr-visible){   transform: translate3d(-32px, 0, 0); }
.sr-right:not(.sr-visible){  transform: translate3d(32px, 0, 0); }
.sr:not(.sr-visible),
.sr-left:not(.sr-visible),
.sr-right:not(.sr-visible){ will-change: opacity, transform; }
.sr.sr-visible, .sr-left.sr-visible, .sr-right.sr-visible{
  will-change: auto; transform: translate3d(0, 0, 0);
}

/* .reveal-left / .reveal-right / .reveal-up — center-converging premium reveal. */
.reveal-left, .reveal-right, .reveal-up{ transition-duration: var(--dur-lg) !important; }
.reveal-left:not(.reveal-active) { transform: translate3d(-40px, 0, 0) scale(.98) !important; }
.reveal-right:not(.reveal-active){ transform: translate3d(40px,  0, 0) scale(.98) !important; }
.reveal-up:not(.reveal-active)   { transform: translate3d(0, 32px, 0) scale(.99) !important; }
.reveal-left.reveal-active, .reveal-right.reveal-active, .reveal-up.reveal-active{
  will-change: auto;
}

/* .hl-word — hero headline word-by-word reveal. */
.hl-word{
  transition: transform var(--dur-lg) var(--ease-premium),
              opacity   var(--dur-md) var(--ease-premium) !important;
}

/* .clip-reveal — heading rise-up animation. */
.clip-reveal-inner{
  transition: transform var(--dur-lg) var(--ease-premium) !important;
}
.clip-reveal.in-view .clip-reveal-inner{ will-change: auto; }

/* .sr-edge — app.js's edge-reveal system. Tiered duration by element size. */
.sr-edge{
  transition: opacity var(--dur-md) var(--ease-premium),
              transform var(--dur-md) var(--ease-premium) !important;
}
.imgbox.sr-edge, .doypack-visual.sr-edge, .chart-card.sr-edge,
.founder-arc.sr-edge, .signup.sr-edge{ transition-duration: var(--dur-lg) !important; }
.eyebrow.sr-edge, .btn-row.sr-edge{ transition-duration: var(--dur-sm) !important; }

/* Desktop distances — SHORT. Per-frame pixel delta is what makes an
   animation feel janky on integrated GPUs: a 56px travel over 0.72s is
   ~1.3px/frame at 60fps, but over a longer duration with less distance
   it's sub-pixel and reads perfectly smooth. Matched to the loop-circle
   feel (which barely moves — it just scales 0→1 in place). */
@media (min-width: 1024px){
  .sr-edge.from-left  { transform: translate3d(-20px, 0, 0) !important; }
  .sr-edge.from-right { transform: translate3d(20px, 0, 0) !important; }
  .sr-edge.from-top   { transform: translate3d(0, -14px, 0) !important; }
  .sr-edge.from-bot   { transform: translate3d(0, 16px, 0) !important; }
  .sr-edge.from-scale { transform: scale3d(.97,.97,1) !important; }
  .sr-edge.in-view    { transform: translate3d(0, 0, 0) scale3d(1, 1, 1) !important; }
}
@media (max-width: 767px){
  .sr-edge.from-left, .sr-edge.from-right, .sr-edge.from-top,
  .sr-edge.from-bot, .sr-edge.from-scale{
    transform: translate3d(0, 12px, 0) !important;
  }
}
/* .sr / .sr-left / .sr-right — text glides in from the LEFT edge (per
   user request), at the loop-path's calm 1.6s pace. Distance stays short
   so per-frame delta is sub-pixel — that's what makes it read as smooth
   on integrated GPUs, not the duration alone. */
.sr:not(.sr-visible){        transform: translate3d(-22px, 0, 0) !important; }
.sr-left:not(.sr-visible){   transform: translate3d(-22px, 0, 0) !important; }
.sr-right:not(.sr-visible){  transform: translate3d(22px, 0, 0) !important; }

/* Section body copy, leads, notes, eyebrows — glide from the left too,
   matching the headline. Only the ones app.js marks with .sr scaffolding
   are affected; nothing else on the page moves. */
.lead.sr:not(.sr-visible),
p.sr:not(.sr-visible),
.note.sr:not(.sr-visible),
.eyebrow.sr:not(.sr-visible){
  transform: translate3d(-24px, 0, 0) !important;
}
/* .reveal-* — same. */
.reveal-left:not(.reveal-active) { transform: translate3d(-20px, 0, 0) !important; }
.reveal-right:not(.reveal-active){ transform: translate3d(20px, 0, 0) !important; }
.reveal-up:not(.reveal-active)   { transform: translate3d(0, 16px, 0) !important; }
.sr-edge.in-view{ will-change: auto; }

/* Carve-outs — permanent transforms/hover-lifts that get flattened by the
   in-view `transform: none !important` rule above. Restore them. */
@media (min-width: 1024px){
  .profile.feat.sr-edge.in-view    { transform: translate3d(0, -10px, 0) scale3d(1,1,1) !important; }
  .card.sr-edge.in-view:hover      { transform: translate3d(0, -4px, 0)  scale3d(1,1,1) !important; }
  .fact.sr-edge.in-view:hover      { transform: translate3d(0, -3px, 0)  scale3d(1,1,1) !important; }
}

/* Founder portraits and profile card must never be clipped by any parent. */
.founder-arc, .founder-arc *, .profile, .profile *{
  contain: none !important;
  overflow: visible !important;
}

/* .mb-icon-pop — small-icon staggered pop-in (wired by simulator-v2.js). */
.mb-icon-pop{
  opacity: 0; transform: scale(.8) translateY(6px);
  transition: opacity var(--dur-sm) var(--ease-premium),
              transform var(--dur-sm) var(--ease-premium);
  will-change: opacity, transform;
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
}
.mb-icon-pop.mb-icon-in{
  opacity: 1; transform: scale(1) translateY(0);
  will-change: auto;
}

/* .tl-item — Founders origin timeline: staggered reveal + circle pop + line draw. */
.tl-item{
  transition: opacity var(--dur-lg) var(--ease-premium),
              transform var(--dur-lg) var(--ease-premium) !important;
  will-change: opacity, transform;
}
.tl-item.sr-visible{ will-change: auto; }
.tl-item:nth-of-type(1){ transition-delay: 0ms   !important; }
.tl-item:nth-of-type(2){ transition-delay: 180ms !important; }
.tl-item:nth-of-type(3){ transition-delay: 360ms !important; }
.tl-item:nth-of-type(4){ transition-delay: 540ms !important; }
.tl-item:nth-of-type(5){ transition-delay: 720ms !important; }

.tl-item::before{
  transform: scale(0) !important; opacity: 0;
  transition: transform var(--dur-md) cubic-bezier(.34, 1.56, .64, 1),
              opacity var(--dur-sm) ease !important;
}
.tl-item.sr-visible::before, .tl-item.in-view::before{
  transform: scale(1) !important; opacity: 1;
}
.tl-item:nth-of-type(1)::before{ transition-delay: 200ms !important; }
.tl-item:nth-of-type(2)::before{ transition-delay: 380ms !important; }
.tl-item:nth-of-type(3)::before{ transition-delay: 560ms !important; }
.tl-item:nth-of-type(4)::before{ transition-delay: 740ms !important; }
.tl-item:nth-of-type(5)::before{ transition-delay: 920ms !important; }

.timeline::before{
  transform-origin: top center;
  transform: scaleY(0);
  transition: transform 1.4s var(--ease-premium);
}
.timeline.sr-visible::before,
.timeline.in-view::before,
.timeline[data-revealed="1"]::before{ transform: scaleY(1); }
/* `:has()` deliberately avoided here — Chrome only shipped it in v105,
   and a browser that doesn't understand :has() DROPS THE ENTIRE RULE
   including the two valid selectors above it. That would leave the
   timeline connector line permanently at scaleY(0) — invisible. */

@media (min-width: 768px){
  .tl-item{ margin-bottom: 8vh; }
  .tl-item:last-child{ margin-bottom: 0; }
}

/* Hero pouch breathing float (compositor-only). */
@keyframes mbFloat{ 0%,100%{transform:translateY(0)} 50%{transform:translateY(-7px)} }
.hero-img-wrap{ animation: mbFloat 6.5s ease-in-out infinite; }

/* Promo ticker — right-to-left scroll. Animation defined in styles.css
   (`mbTickerScroll` keyframes on `.mb-ticker-track`), but the animation
   sometimes reads as "stuck" because 32s for the full cycle is very slow
   AND several things can silently pause it: parent hover state stuck on,
   content-visibility: auto rendering skip, or a fresh compositor layer
   that never settles. Explicit re-declaration below forces it to run,
   faster (24s), with WebKit prefix + explicit play-state so nothing
   above can cancel it. Hover-pause kept intact. */
/* Own dedicated keyframes name (`mbTickScrollV2`) so nothing in
   styles.css or app.js can accidentally cancel it. */
@-webkit-keyframes mbTickScrollV2{
  from{ -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); }
  to  { -webkit-transform: translate3d(-50%, 0, 0); transform: translate3d(-50%, 0, 0); }
}
@keyframes mbTickScrollV2{
  from{ transform: translate3d(0, 0, 0); }
  to  { transform: translate3d(-50%, 0, 0); }
}
.mb-ticker-track{
  -webkit-animation: mbTickScrollV2 22s linear infinite !important;
  animation:         mbTickScrollV2 22s linear infinite !important;
  -webkit-animation-play-state: running !important;
  animation-play-state: running !important;
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
  will-change: transform;
  transform: translateZ(0);      /* forces own compositor layer — prevents
                                    parent 'overflow:hidden' from silently
                                    pausing the animation on some engines */
}
/* Only pause on TRUE hover (mouse-only devices). On touch, `:hover` sticks
   after tap on some browsers, which was silently freezing the ticker. */
@media (hover: hover) and (pointer: fine){
  .mb-ticker:hover .mb-ticker-track{
    -webkit-animation-play-state: paused !important;
    animation-play-state: paused !important;
  }
}
/* Nothing may cancel the animation on any ancestor state — belt-and-suspenders
   in case a parent gets an accidental `animation: none` from some cascade. */
.mb-ticker, .mb-ticker *:not(a):not(button){ animation-play-state: running; }

/* Loop-svg arrow marker — was covered by the Explain circle, hide it. */
.loop-svg path{ marker-end: none !important; }


/* ═════════════════════════════════════════════════════════════════════════
   ROADMAP PILLS  ("Concept → Formula → Testing → Production → Launch")
   Each pill fills from LEFT to RIGHT in its own colour, one after another,
   like a progress bar being drawn. Same feel as the loop-path draw (which
   uses stroke-dashoffset) but implemented with scaleX — the cheapest
   possible GPU operation, works identically on every browser.

   Colours preserved exactly from styles.css:
     .stg.done .bar → var(--success)  (green — finished stages)
     .stg.cur  .bar → var(--action)   (navy — current stage)
     .stg      .bar → var(--mist)     (grey — not started)
   The grey ones stay grey and DON'T animate — they're not "lit up" yet,
   matching the user's description.
   ═════════════════════════════════════════════════════════════════════════ */

/* Track — the grey base rail is always visible immediately. */
.stg .bar{
  position: relative;
  overflow: hidden;
  background: var(--mist) !important;
}

/* The coloured fill is a pseudo-element that scales in from the left.
   Only .done and .cur get one — grey stages have no fill at all. */
.stg.done .bar::after,
.stg.cur .bar::after{
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 1.1s var(--ease-premium);
  will-change: transform;
}
.stg.done .bar::after{ background: var(--success); }
.stg.cur  .bar::after{ background: var(--action); }

/* GREEN GLOW (the "green light glows" effect from the 21st.dev component,
   rebuilt in vanilla CSS). The finished (green) stage bars emit a soft,
   slowly-breathing green halo so they read as "lit up / active". Uses a
   box-shadow pulse on the filled pseudo-element — GPU-cheap, isolated to
   ~2 tiny bars, so no scroll-jank risk. Only glows once the bar has
   filled in (parent revealed). */
@keyframes mbGreenGlow{
  0%,100%{ box-shadow: 0 0 6px 0 rgba(34,197,94,.35), 0 0 2px 0 rgba(34,197,94,.5); }
  50%    { box-shadow: 0 0 16px 2px rgba(34,197,94,.65), 0 0 6px 0 rgba(34,197,94,.7); }
}
/* ═══════════════ SINGLE CLEAN AUTO-PLAY SYSTEM ═══════════════
   ONE system, no scroll/observer/reduced-motion dependency. The bars
   fill from the left via a keyframe `animation` that plays automatically
   the instant the elements exist in the DOM — i.e. every time you open
   the #/updates page (app.js re-renders fresh .stg elements on route
   change, so the animation replays on entry). `forwards` holds the end
   state so they stop filled.

   IMPORTANT: this deliberately runs EVEN under prefers-reduced-motion.
   That's why nothing moved on Windows before — Windows commonly reports
   reduced-motion, and the old rules killed the animation. A slow bar
   fill + soft glow is not a vestibular trigger, and the site owner wants
   it visible on every device, so we let it play. Slowed to 1.8s with a
   gentle stagger for the calm feel requested. */
@-webkit-keyframes stgFill{ from{ -webkit-transform: scaleX(0); transform: scaleX(0); } to{ -webkit-transform: scaleX(1); transform: scaleX(1); } }
@keyframes stgFill{ from{ transform: scaleX(0); } to{ transform: scaleX(1); } }
@-webkit-keyframes stgLabel{ from{ opacity: 0; -webkit-transform: translateY(4px); transform: translateY(4px);} to{ opacity: 1; -webkit-transform: none; transform: none;} }
@keyframes stgLabel{ from{ opacity: 0; transform: translateY(4px);} to{ opacity: 1; transform: none;} }

/* FILL — slow (1.8s), calm easing, staggered left → right. `both` keeps
   the from-state (scaleX 0) during the delay so nothing flashes filled. */
.stg.done .bar::after,
.stg.cur  .bar::after{
  -webkit-animation: stgFill 1.8s cubic-bezier(.16,1,.3,1) both;
  animation:         stgFill 1.8s cubic-bezier(.16,1,.3,1) both;
}
.stages .stg:nth-child(1) .bar::after{ -webkit-animation-delay:.15s; animation-delay:.15s; }
.stages .stg:nth-child(2) .bar::after{ -webkit-animation-delay:.75s; animation-delay:.75s; }
.stages .stg:nth-child(3) .bar::after{ -webkit-animation-delay:1.35s; animation-delay:1.35s; }
.stages .stg:nth-child(4) .bar::after{ -webkit-animation-delay:1.95s; animation-delay:1.95s; }
.stages .stg:nth-child(5) .bar::after{ -webkit-animation-delay:2.55s; animation-delay:2.55s; }

/* GREEN GLOW — lives on the BAR itself (not the fill pseudo), so it's
   completely decoupled from the fill animation. Starts calmly after the
   green bars have filled, then breathes forever. box-shadow on the bar
   is not clipped by its own overflow:hidden, so the halo shows. */
.stg.done .bar{
  -webkit-animation: mbGreenGlow 2.8s ease-in-out infinite;
  animation:         mbGreenGlow 2.8s ease-in-out infinite;
}
.stages .stg.done:nth-child(1) .bar{ -webkit-animation-delay: 2.0s; animation-delay: 2.0s; }
.stages .stg.done:nth-child(2) .bar{ -webkit-animation-delay: 2.6s; animation-delay: 2.6s; }

/* LABELS — fade + tiny rise, staggered to follow their bar. */
.stg .nm{
  -webkit-animation: stgLabel .7s cubic-bezier(.16,1,.3,1) both;
  animation:         stgLabel .7s cubic-bezier(.16,1,.3,1) both;
}
.stages .stg:nth-child(1) .nm{ -webkit-animation-delay:.45s; animation-delay:.45s; }
.stages .stg:nth-child(2) .nm{ -webkit-animation-delay:1.05s; animation-delay:1.05s; }
.stages .stg:nth-child(3) .nm{ -webkit-animation-delay:1.65s; animation-delay:1.65s; }
.stages .stg:nth-child(4) .nm{ -webkit-animation-delay:2.25s; animation-delay:2.25s; }
.stages .stg:nth-child(5) .nm{ -webkit-animation-delay:2.85s; animation-delay:2.85s; }


/* ═════════════════════════════════════════════════════════════════════════
   §TL  ORIGIN-STORY TIMELINE — auto-play slide from the LEFT edge.
   Each item (The frustration / The idea / The promise) glides in from the
   left, slowly and calmly, one after another, and stops in place. Uses an
   auto-play keyframe `animation` (not a scroll/observer transition), so it
   plays every time you open the Founders page — including on Windows with
   reduced-motion (the site owner asked for it to be visible everywhere).
   Higher specificity (`.timeline .tl-item`) + !important so it beats the
   reduced-motion `.sr{animation:none}` rule that was freezing it.
   `both` holds the start state during the stagger delay so nothing flashes.
   ═════════════════════════════════════════════════════════════════════════ */
@-webkit-keyframes tlSlideIn{
  from{ opacity:0; -webkit-transform: translate3d(-32px,0,0); transform: translate3d(-32px,0,0); }
  to  { opacity:1; -webkit-transform: none; transform: none; }
}
@keyframes tlSlideIn{
  from{ opacity:0; transform: translate3d(-32px,0,0); }
  to  { opacity:1; transform: none; }
}
.timeline .tl-item{
  -webkit-animation: tlSlideIn 1.2s cubic-bezier(.16,1,.3,1) both !important;
  animation:         tlSlideIn 1.2s cubic-bezier(.16,1,.3,1) both !important;
}
.timeline .tl-item:nth-of-type(1){ -webkit-animation-delay:.15s; animation-delay:.15s; }
.timeline .tl-item:nth-of-type(2){ -webkit-animation-delay:.6s;  animation-delay:.6s; }
.timeline .tl-item:nth-of-type(3){ -webkit-animation-delay:1.05s; animation-delay:1.05s; }
.timeline .tl-item:nth-of-type(4){ -webkit-animation-delay:1.5s;  animation-delay:1.5s; }
.timeline .tl-item:nth-of-type(5){ -webkit-animation-delay:1.95s; animation-delay:1.95s; }

/* The connecting line grows down calmly as the items arrive; the marker
   dots keep their soft ring (calm shadow). */
.timeline::before{
  -webkit-animation: tlLineGrow 1.6s cubic-bezier(.16,1,.3,1) .1s both !important;
  animation:         tlLineGrow 1.6s cubic-bezier(.16,1,.3,1) .1s both !important;
  transform-origin: top center;
}
@-webkit-keyframes tlLineGrow{ from{ -webkit-transform: scaleY(0); transform: scaleY(0); } to{ -webkit-transform: scaleY(1); transform: scaleY(1); } }
@keyframes tlLineGrow{ from{ transform: scaleY(0); } to{ transform: scaleY(1); } }


/* ═════════════════════════════════════════════════════════════════════════
   §ING  INGREDIENTS CARDS — calm auto-play reveal (Product page,
   "Chosen with a purpose."). Same feel as the Development-Updates roadmap:
   an auto-play keyframe that runs on page entry, works on Windows even
   with reduced-motion, and stops in place.

   Targets ONLY the ingredient cards: `.grid > .card.card--pad-lg`. The
   contact-form card is also `.card--pad-lg` but is NOT inside a `.grid`,
   so it's not affected.

   Uses `backwards` (not `forwards`) fill: the from-state is held during
   the stagger delay (no flash), and after the animation the card reverts
   to its normal resting style — so the hover-lift still works. We also
   force opacity:1 as the base so the card can never end up hidden by the
   `.sr-edge{opacity:0}` reveal scaffolding app.js adds to every card.
   ═════════════════════════════════════════════════════════════════════════ */
@-webkit-keyframes ingIn{
  from{ opacity:0; -webkit-transform: translate3d(0,26px,0); transform: translate3d(0,26px,0); }
  to  { opacity:1; -webkit-transform: none; transform: none; }
}
@keyframes ingIn{
  from{ opacity:0; transform: translate3d(0,26px,0); }
  to  { opacity:1; transform: none; }
}
.grid > .card.card--pad-lg{
  opacity: 1 !important;   /* beat .sr-edge{opacity:0} so it can't stay hidden */
  -webkit-animation: ingIn 1.1s cubic-bezier(.16,1,.3,1) backwards !important;
  animation:         ingIn 1.1s cubic-bezier(.16,1,.3,1) backwards !important;
}
.grid > .card.card--pad-lg:nth-child(1){ -webkit-animation-delay:.15s; animation-delay:.15s; }
.grid > .card.card--pad-lg:nth-child(2){ -webkit-animation-delay:.55s; animation-delay:.55s; }
.grid > .card.card--pad-lg:nth-child(3){ -webkit-animation-delay:.95s; animation-delay:.95s; }
.grid > .card.card--pad-lg:nth-child(4){ -webkit-animation-delay:1.35s; animation-delay:1.35s; }
/* Preserve the hover-lift after the entrance finishes (desktop). */
@media (min-width:1024px){
  .grid > .card.card--pad-lg:hover{
    transform: translate3d(0,-4px,0);
    transition: transform .3s cubic-bezier(.22,.61,.36,1);
  }
}


/* ═════════════════════════════════════════════════════════════════════════
   TIMELINE DOTS — gentle colour pulse (Founders "How MindBites started")
   Each dot breathes with a soft ring in the brand action colour. Pure
   box-shadow-on-pseudo-element via opacity — no shadow value animation,
   so it stays compositor-cheap.
   ═════════════════════════════════════════════════════════════════════════ */
@-webkit-keyframes tlDotPulse{
  0%,100%{ opacity: .35; -webkit-transform: scale(1); transform: scale(1); }
  50%    { opacity: .85; -webkit-transform: scale(1.35); transform: scale(1.35); }
}
@keyframes tlDotPulse{
  0%,100%{ opacity: .35; transform: scale(1); }
  50%    { opacity: .85; transform: scale(1.35); }
}
/* Pulsing halo ring behind each timeline dot. */
.tl-item::after{
  content: "";
  position: absolute;
  left: -24px; top: 6px;
  width: 12px; height: 12px;
  border-radius: 50%;
  background: var(--action);
  opacity: 0;
  pointer-events: none;
  /* z-index:-1 removed — it pushed the halo BEHIND the section background
     in some stacking contexts (invisible), and in others created a new
     stacking context that clipped the dot itself. z-index:0 keeps it in
     normal flow, safely under the ::before dot which paints after it. */
  z-index: 0;
}
.tl-item.sr-visible::after,
.tl-item.in-view::after,
.tl-item[data-revealed="1"]::after{
  -webkit-animation: tlDotPulse 2.8s var(--ease-premium) infinite;
  animation: tlDotPulse 2.8s var(--ease-premium) infinite;
}
.tl-item:nth-of-type(1)::after{ -webkit-animation-delay:0s;   animation-delay:0s; }
.tl-item:nth-of-type(2)::after{ -webkit-animation-delay:.5s;  animation-delay:.5s; }
.tl-item:nth-of-type(3)::after{ -webkit-animation-delay:1s;   animation-delay:1s; }
.tl-item:nth-of-type(4)::after{ -webkit-animation-delay:1.5s; animation-delay:1.5s; }
.tl-item:nth-of-type(5)::after{ -webkit-animation-delay:2s;   animation-delay:2s; }
@media (prefers-reduced-motion: reduce){
  .tl-item::after{ animation: none !important; opacity: 0 !important; }
}


/* ═════════════════════════════════════════════════════════════════════════
   STATUS-GUIDE DOTS — same gentle pulse, in each status's own colour.
   (.status.new / .planning / .review / .complete pills on the
   Suggestions page's "Status guide" card.)
   ═════════════════════════════════════════════════════════════════════════ */
.status::before{
  -webkit-animation: tlDotPulse 2.6s var(--ease-premium) infinite;
  animation: tlDotPulse 2.6s var(--ease-premium) infinite;
  /* keep the dot's own colour — inherits from styles.css .status variants */
}
.status:nth-of-type(2)::before{ -webkit-animation-delay:.4s; animation-delay:.4s; }
.status:nth-of-type(3)::before{ -webkit-animation-delay:.8s; animation-delay:.8s; }
.status:nth-of-type(4)::before{ -webkit-animation-delay:1.2s; animation-delay:1.2s; }
@media (prefers-reduced-motion: reduce){
  .status::before{ animation: none !important; opacity: 1 !important; }
}

/* Reduced-motion — ONLY the small decorative pop bits are flattened here.
   The reveal scaffolds (.sr, .sr-edge, .clip-reveal-inner, .tl-item, etc.)
   are handled by §M-RM near the end of this file, which re-enables a gentle
   opacity fade instead of killing motion entirely — that's the Windows fix.
   The promo ticker is INTENTIONALLY left running (the site owner wants it
   moving on all devices); it's a slow, calm horizontal scroll. */
@media (prefers-reduced-motion: reduce){
  .mb-icon-pop{ transition: none !important; transform: none !important; opacity: 1 !important; }
  .tl-item::before, .timeline::before{ transition: none !important; transform: none !important; opacity: 1 !important; }
  .hero-img-wrap{ animation: none; }   /* continuous float — OK to disable */
  /* .mb-ticker-track is NOT disabled — see note above. */
}


/* ─────────────────────────────────────────────────────────────────────────────
   9. SCROLL SMOOTHNESS  (fixed-header layer, progress bar, parallax layer hints)
   ───────────────────────────────────────────────────────────────────────────── */

/* Fixed header — its own GPU compositor layer so it doesn't repaint against
   page content during scroll. */
.hd{
  -webkit-transform: translateZ(0);
  transform: translateZ(0);
  will-change: transform;
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
}

/* Scroll progress bar — GPU-driven scaleX via CSS variable (see site-patch.js §3).
   app.js still writes .style.width on every scroll, but this rule dominates and
   contains any layout thrash to just this one element. */
#scroll-progress-bar{
  width: 100% !important;
  transform-origin: left center;
  transform: scaleX(var(--sp, 0));
  will-change: transform;
  contain: layout paint style;
  transition: none !important;
}
#scroll-progress{ contain: layout paint style; }

/* [REMOVED] Parallax compositor hints — this rule is why the founder photos
   looked pixelated, and why it showed up on a Mac.

   It put `transform-style: preserve-3d` + `backface-visibility: hidden` on
   every image container. That forces each one into a 3D rendering context,
   and an element in a 3D context is rasterised ONCE into a texture which is
   then scaled — it does not re-rasterise for the display's pixel ratio. On a
   1× screen nobody notices. On a Retina Mac the texture is generated at 1×
   and stretched to 2×, so every face is drawn at half the resolution the
   screen can show. Hence sharp text beside soft, blocky photographs.

   The hints existed for the scroll parallax, and the parallax itself was
   switched off further down this file (`transform: none !important`). So the
   cost was being paid for an effect that no longer runs.

   `transform-style: flat` is stated explicitly rather than just deleting the
   rule, so nothing inherits a 3D context from an ancestor. */
.hero-img, .founder-arc, .chart-card, .chart-card canvas,
.doypack-visual, .mb-hero-img, .mb-panel-img, .imgbox, .loop-svg,
.portrait, .profile .pf{
  -webkit-transform-style: flat;
          transform-style: flat;
  -webkit-backface-visibility: visible;
          backface-visibility: visible;
}

/* Let the browser use its best downscaling filter on photographs. `auto` is
   the high-quality path; the `-webkit-optimize-contrast` value some guides
   recommend is nearest-neighbour-ish and makes photos worse, not better. */
.portrait,
.profile .pf,
.mb-hero-img,
.mb-panel-img,
.imgbox img,
.hero-img img{
  image-rendering: auto;
}


/* ─────────────────────────────────────────────────────────────────────────────
  10. HERO LAYOUT & PRODUCT IMAGE  (single source of truth per breakpoint)
   ───────────────────────────────────────────────────────────────────────────── */

/* Desktop: text column wider so h1 fits on 2 lines; product image is a
   wide-rectangle 5:4 (not a tall pillar) at 480–560px. */
@media (min-width: 1024px){
  .hero{
    grid-template-columns: 1.35fr 1fr !important;
    align-items: center;
    gap: clamp(28px, 4vw, 64px);
  }
  .hero h1{
    font-size: clamp(40px, 4.6vw, 68px) !important;
    line-height: 1.05 !important;
  }
  /* PRODUCT PAGE hero (.text-first) has a LONG headline
     ("Energy + focus in a portable gummy format.") that was wrapping onto
     6 narrow lines like a phone layout. Give its text column much more
     width and cap the font smaller so it lands on ~2 lines. Only affects
     this one hero — other heroes keep their larger headline. */
  .hero.text-first{
    grid-template-columns: 1.65fr 1fr !important;
  }
  .hero.text-first h1{
    font-size: clamp(34px, 3.4vw, 50px) !important;
    line-height: 1.1 !important;
    max-width: 16ch;          /* ~2 lines for this specific headline */
  }
  /* HOME hero — PC layout.
     NOTE: an earlier `aspect-ratio:5/4` on a tall min-height forced the image
     column ~775px wide, which squeezed the headline onto 4 lines and shrank
     the signup card. Fixed by dropping aspect-ratio (height governs the pouch
     size) and giving the text column more width. */
  .hero-home{ gap: clamp(26px, 3.4vw, 52px); align-items: center; }
  .hero-home .hero-txt{ flex: 1.45; }        /* wider → headline fits 2 lines + roomier signup */
  .hero-home .hero-img{
    flex: 1;
    aspect-ratio: auto;
    min-height: 560px;
    max-height: 660px;
    width: 100%;
  }
  /* Headline sits on its three intended lines ("Your Go-To" / "Portable
     Energy" / "Gummies") — never let a single line wrap internally.
     `white-space: nowrap` means a line that is too long OVERFLOWS the column
     instead of wrapping, so the size is set against the longest line rather
     than the average one. "Portable Energy" is 15 characters against the old
     headline's 11, so the cap comes down from 58px to 52px to keep it inside
     the text column at every desktop width. */
  .hero-home #hero-hl{ font-size: clamp(34px, 3.6vw, 52px); }
  .hero-home #hero-hl .hl-line{ display: block; white-space: nowrap; }
  /* Nudge the pouch a little to the right inside its column. */
  .hero-home .mb-hero-img{ object-position: 72% center; }
  /* Signup card back to its original comfortable width. app.js replaces the
     [data-signup] placeholder with a `.signup` card, so target that. */
  .hero-home .signup,
  .hero-home [data-signup]{ max-width: 460px; width: 100%; }
  .hero .imgbox{
    aspect-ratio: 5/4;
    min-height: 520px;      /* bumped — product pouch bigger on desktop */
    max-height: 640px;
  }
  /* Make the injected pouch image fill more of its (now larger) box.
     Text column untouched — this only affects the image side. */
  .hero .imgbox .mb-panel-img,
  .hero .imgbox .mb-hero-img{
    width: 100% !important;
    height: 100% !important;
    object-fit: contain;
  }
  .mb-hero-img, .mb-panel-img{
    width: 100% !important;
    height: 100% !important;
    object-fit: contain;
  }

  /* SCIENCE mini-chart (home §05) — a touch bigger, grown LEFTWARD so the
     right edge (and therefore the gap to the text column) stays put and the
     page spacing is unchanged. Pure layout (width + negative margin), so it
     doesn't conflict with the transform rules on `.chart-card canvas`. */
  .hero > .chart-card[data-mini-chart]{
    width: 112%;
    margin-left: -12%;
    overflow: visible;
  }
}

/* Tablet + mobile: pouch fills nearly the whole fold — the first thing
   anyone sees. Matches the red-outlined area in the user's screenshot:
   roughly from just under the header down to the eyebrow label. */
@media (max-width: 1023px){
  .hero-home .hero-img{
    height: 78vh !important;
    max-height: 760px !important;
    min-height: 560px !important;
  }
  @supports (height: 1dvh){
    .hero-home .hero-img{ height: 78dvh !important; }
  }
  /* Image fills its container fully — was 95% in app.js's inline style. */
  .mb-hero-img{
    width: 100% !important;
    height: 100% !important;
    object-fit: contain;
  }
}
@media (max-width: 519px){
  .hero-home .hero-img{
    height: 72vh !important;
    max-height: 680px !important;
    min-height: 480px !important;
  }
  @supports (height: 1dvh){
    .hero-home .hero-img{ height: 72dvh !important; }
  }
}


/* ─────────────────────────────────────────────────────────────────────────────
  11. HERO GRID — MOBILE REORDER  (media below text, buttons below chart)
   ───────────────────────────────────────────────────────────────────────────── */

/* Hero layout on tablet + mobile: TITLE → PHOTO → TEXT → BUTTONS.
   Works by dissolving the text-column wrapper div via `display: contents`
   so its inner h1/lead/btn-row become direct flex items of `.hero`,
   alongside the media (founder-arc / imgbox / etc.). Then `order` places
   each element individually.
   Skips `.hero.text-first` (product page — kept as-is by request). */
@media (max-width: 1023px){
  .hero:not(.text-first){
    display: flex !important;
    flex-direction: column;
    align-items: stretch;
  }
  /* Text-column wrapper div → transparent; its children participate in
     the .hero flex layout as if they were direct siblings of the media. */
  .hero:not(.text-first) > div:not(.chart-card):not(.imgbox):not(.doypack-visual):not(.founder-arc){
    display: contents;
  }

  /* 1) Eyebrow (small label above the title) */
  .hero:not(.text-first) .eyebrow{ order: 1; }
  /* 2) Title */
  .hero:not(.text-first) h1,
  .hero:not(.text-first) h2{ order: 2; }
  /* 3) PHOTO-STYLE media (portraits, product pouch, doypack) — sits BETWEEN
     title and body copy, since these are the "hero image" for the section. */
  .hero:not(.text-first) > .founder-arc,
  .hero:not(.text-first) > .imgbox,
  .hero:not(.text-first) > .doypack-visual{ order: 3; margin-block: 20px; }
  /* 4) Body copy — leads and paragraphs */
  .hero:not(.text-first) .lead,
  .hero:not(.text-first) > p:not(.crumbs),
  .hero:not(.text-first) > * > p:not(.crumbs):not(.muted):not(.fine){ order: 4; }
  .hero:not(.text-first) .muted,
  .hero:not(.text-first) .fine{ order: 4; }
  /* 5) SIDE CARD (e.g. Suggestions "Status guide") — sits after the lead,
     before the button. Gives: title → lead → status guide → button. */
  .hero:not(.text-first) > .card{ order: 5; margin-top: 6px; }
  /* 5b) CHART media (Science mini-chart) — position handled by
     site-patch.js `syncScienceChart()` which physically inserts the
     chart just before the buttons. No CSS `order` here so the two
     systems can't conflict. Just give it breathing room. */
  .hero:not(.text-first) .chart-card{ margin-block: 20px; }
  /* 6) CTA buttons at the very bottom of the hero. */
  .hero:not(.text-first) .btn-row{ order: 6; }
}

/* FOUNDERS PAGE — portrait arc must always show on phones.
   app.js reveals `.founder-arc` with its edge observer (starts it hidden:
   opacity 0 + translateX). On phones that observer is unreliable, so the
   four portraits could stay invisible. Force the arc — and each portrait —
   fully visible on mobile/tablet, right under the big title. */
@media (max-width: 1023px){
  .founder-arc,
  .founder-arc.sr-edge,
  .founder-arc.sr-edge:not(.in-view),
  .founder-arc.sr,
  .founder-arc.sr:not(.sr-visible){
    opacity: 1 !important;
    transform: none !important;
  }
  .founder-arc .portrait{
    opacity: 1 !important;
    transform: none !important;
  }
  /* give the raised centre portrait its lift back (transform was cleared) */
  .founder-arc .portrait.mid{ transform: translateY(-10px) !important; }
}

/* ═════════════════════════════════════════════════════════════════════════
   LOOP DIAGRAM (Listen → Evaluate → Build → Explain) — always visible on
   mobile. app.js reveals the circles + path via an observer + JS timer;
   on phones that reveal is unreliable, leaving them invisible (circles at
   opacity 0, path with a full stroke-dashoffset). Force them fully drawn
   on mobile so the diagram always shows. Also make the SVG scale to the
   viewport so nothing is cut off.
   ═════════════════════════════════════════════════════════════════════════ */
@media (max-width: 767px){
  .loop-svg{
    width: 100% !important;
    max-width: 100% !important;
    height: auto !important;
  }
  .loop-node{
    opacity: 1 !important;
    transform: none !important;
  }
  .loop-svg path,
  .loop-path,
  #loop-line{
    stroke-dasharray: none !important;
    stroke-dashoffset: 0 !important;
    opacity: 1 !important;
  }
}

/* SIMULATOR (Caffeine Explorer) — .sim-body has controls on the left and
   chart on the right in desktop grid. On mobile the user wants the chart
   ABOVE the controls (visual first, tweak controls below). */
@media (max-width: 1023px){
  .sim-body{
    display: flex !important;
    flex-direction: column;
  }
  .sim-body > .sim-chartwrap{ order: 1; }        /* chart + legend + metrics */
  .sim-body > .sim-controls{ order: 2; margin-top: 16px; }
}

/* [REMOVED] old `.below-chart` ordering — superseded by site-patch.js's
   physical DOM move (syncScienceChart). Keeping it caused two competing
   reorder systems to fight, which is why the graph/buttons order looked
   wrong. Now there's exactly one system. */

/* KILL SCROLL PARALLAX EVERYWHERE — continuous scroll-driven transforms
   are the #1 cause of the "text/image peeks and glitches while scrolling"
   bug on Windows Chrome (the compositor can't keep the layer in sync with
   the scroll every frame). These five elements have NO hover transform,
   so neutralising their transform is completely safe. The product pouch
   images (.mb-hero-img / .mb-panel-img) are NOT parallax targets and keep
   their hover-zoom — so they're excluded from the desktop rule. */
.hero-img, .founder-arc, .loop-svg,
.chart-card canvas, .doypack-visual{
  transform: none !important;
}
/* On mobile also freeze the product images (no hover on touch anyway). */
@media (max-width: 767px){
  .mb-hero-img, .mb-panel-img{ transform: none !important; }
}


/* ─────────────────────────────────────────────────────────────────────────────
  12. MOBILE SAFETY OVERRIDES  (must-never-be-invisible + button wrap)
   ───────────────────────────────────────────────────────────────────────────── */
@media (max-width: 767px){

  /* Never let a heading get stuck below its own clip-reveal box. app.js's
     IntersectionObserver sometimes races with DOM inject timing on mobile
     — this ensures the h2/h3 is always readable, animation still plays
     if the observer fires. */
  .clip-reveal, .clip-reveal-inner{
    overflow: visible !important;
    transform: none !important;
    opacity: 1 !important;
  }

  /* Idea/suggestion card h3 — belt-and-suspenders after the clip-reveal
     fix above; forces the title visible regardless of race conditions. */
  .idea h3{
    font-size: clamp(15px, 3.8vw, 17px);
    font-weight: 700;
    color: var(--ink);
    line-height: 1.3;
    margin: 0 0 6px;
    display: block !important;
    visibility: visible !important;
    opacity: 1 !important;
  }
  .idea h3 .clip-reveal,
  .idea h3 .clip-reveal-inner{
    display: inline !important;
    padding: 0 !important;
    margin: 0 !important;
  }

  /* Buttons — allow long labels to wrap, keep touch-target ≥44px. */
  .btn{
    max-width: 100%;
    box-sizing: border-box;
    white-space: normal;
    line-height: 1.2;
    padding-left: 18px;
    padding-right: 18px;
    min-height: 46px;
  }
  .btn-row{ width: 100%; max-width: 100%; flex-wrap: wrap; }
}


/* ─────────────────────────────────────────────────────────────────────────────
  13. CARDS, GRIDS, IDEAS, SIMULATOR
   ───────────────────────────────────────────────────────────────────────────── */

/* Card padding — enough internal space so long copy never touches the
   rounded corner. */
.card{ padding: clamp(18px, 2.8vw, 28px); }
.card > *:last-child{ margin-bottom: 0; }

/* Idea rows — center the vote badge vertically beside its content. */
.idea{ align-items: center !important; }
@media (max-width: 519px){
  .idea{ flex-wrap: wrap; padding: 16px !important; }
  .idea .vres{
    align-self: center;
    flex-direction: row;
    justify-content: center;
    min-width: 100%;
    padding-bottom: 6px;
    border-bottom: 1px solid var(--mist);
  }
}
.idea .tag, .idea .status{
  vertical-align: middle;
  display: inline-flex;
  align-items: center;
}

/* Simulator (Caffeine Explorer) restyle helpers */
.sm{ position: relative; overflow: hidden; }
.sm .ico{ color: var(--chart); margin-bottom: 6px; display: inline-flex; }
.chart-card canvas{ transition: opacity .3s ease; }
.sim-controls .toggle-row button{ min-width: 44px; }
.sim-metrics{ grid-template-columns: repeat(2, 1fr) !important; }   /* 5 cards → 2×3 grid */


/* ─────────────────────────────────────────────────────────────────────────────
  14. "THE NEED" — 3-PHOTO SECTION  (wired by site-patch.js §4)

  Desktop / tablet: three rounded-square photos in a row.
  Mobile: stacked vertically (one under the next, larger) so overlaid
  text on each photo stays readable.
   ───────────────────────────────────────────────────────────────────────────── */
.need-strip{
  display: flex;
  gap: clamp(14px, 2vw, 24px);
  justify-content: center;
  align-items: stretch;
  width: 100%;
  max-width: 100%;
  margin: 36px 0 0;
}
.need-strip .need-shot{
  flex: 1 1 0;
  min-width: 0;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  object-position: center;
  display: block;
  border-radius: 20px;
  box-shadow: 0 4px 6px -1px rgba(29,45,80,.02),
              0 25px 55px -12px rgba(29,45,80,.08);
}
body.dark .need-strip .need-shot{
  box-shadow: 0 30px 60px rgba(0,0,0,.4);
}

/* Mobile + narrow tablet — vertical stack, bigger images. */
@media (max-width: 1023px){
  .need-strip{
    flex-direction: column;
    gap: 16px;
    margin-top: 26px;
  }
  .need-strip .need-shot{
    flex: 0 0 auto;
    width: 100%;
    max-width: 640px;
    aspect-ratio: 1 / 1;
    margin: 0 auto;
    border-radius: 18px;
  }
}
@media (max-width: 519px){
  .need-strip{ gap: 14px; }
  .need-strip .need-shot{ border-radius: 16px; }
}


/* ─────────────────────────────────────────────────────────────────────────────
  15. FAQ SECTION  (left-align override — was oddly centered)
   ───────────────────────────────────────────────────────────────────────────── */
/* NOTE: `:has()` removed — Chrome shipped it only in v105, and any
   browser that doesn't understand it drops the whole rule. Handled in
   site-patch.js §10 instead (adds a `data-faq-head` attribute), which
   works everywhere. */
[data-faq-head]{ text-align: left !important; }


/* ─────────────────────────────────────────────────────────────────────────────
  16. SPACING — 8-POINT VERTICAL RHYTHM  (mobile)
   ───────────────────────────────────────────────────────────────────────────── */
@media (max-width: 767px){
  /* Eyebrow → heading */
  .eyebrow{ margin-bottom: clamp(18px, 4vw, 24px); }
  .eyebrow + h1,
  .eyebrow + h2,
  .eyebrow + h3{
    margin-top: 0 !important;
    padding-top: 6px;
  }
  /* Heading → paragraph */
  h1 + p, h2 + p, h3 + p,
  h1 + .lead, h2 + .lead, h3 + .lead{ margin-top: clamp(8px, 2vw, 16px) !important; }
  /* Paragraph → paragraph */
  p + p, .lead + p, p + .lead{ margin-top: clamp(12px, 2.5vw, 16px) !important; }
  /* Text → button row */
  .btn-row{ margin-top: clamp(16px, 4vw, 24px) !important; }
  p + .btn-row, .lead + .btn-row, .muted + .btn-row,
  h1 + .btn-row, h2 + .btn-row, h3 + .btn-row{
    margin-top: clamp(24px, 5vw, 32px) !important;
  }
  /* Block → block */
  .grid, .facts, .stages{ margin-top: clamp(24px, 5vw, 40px); }
  /* Section vertical padding — cards inside have real breathing room from edges. */
  .section{ padding-block: clamp(40px, 8vw, 72px); }
  .section.section--tight{ padding-block: clamp(32px, 6vw, 56px); }
}

/* Universal (all viewports) — proactive card-follows-by-X spacing. */
.grid + .btn-row,
.facts + .btn-row,
.fact-chips + .btn-row,
.trust-pills + .btn-row,
.stages + .btn-row{ margin-top: clamp(28px, 5vw, 48px) !important; }

.grid + .signup, .grid + p, .grid + .lead,
.facts + p, .facts + .lead{ margin-top: clamp(20px, 3vw, 32px); }

.card + .btn, .card + button.btn,
.note + .btn, .note + button.btn,
p + .btn, .lead + .btn, .muted + .btn{ margin-top: clamp(20px, 3vw, 28px); }

/* .btn-row gap between adjacent buttons — universal. */
.btn-row{ gap: clamp(10px, 1.6vw, 16px); }
.btn-row .btn{ margin: 0; }
@media (max-width: 519px){ .btn-row{ gap: 12px; } }

/* Chip/pill/badge row gaps — universal. */
.trust-pills{ gap: clamp(8px, 1.2vw, 12px); }
.fact-chips { gap: clamp(8px, 1.4vw, 14px); }
.sim-legend { gap: clamp(10px, 1.6vw, 16px); }
.sim-controls .toggle-row{ gap: clamp(8px, 1.2vw, 12px); }
.stages     { gap: clamp(6px, 1vw, 10px); }
.socials    { gap: clamp(8px, 1.2vw, 12px); }
.ft-bottom .links{ gap: clamp(14px, 2vw, 20px); }


/* ─────────────────────────────────────────────────────────────────────────────
  17. GPU-ONLY ANIMATION HYGIENE  (Windows-Chrome-vs-macOS-Safari fix)

  Root cause of the reported "smooth on macOS/iOS Safari, glitchy on
  Windows Chrome" gap:  styles.css animates `box-shadow` and layout
  properties (left, width, background-position) on many hover-triggered
  elements. Only `transform` and `opacity` are compositor-friendly on
  every browser. Apple Silicon's tile renderer absorbs the extra paint
  cost silently; integrated Intel/AMD GPUs on Windows drop frames.

  Rules applied here override styles.css transitions/keyframes without
  changing the visible effect: same lift on hover, same shimmer, same
  timing — just implemented through transform/opacity only.
   ───────────────────────────────────────────────────────────────────────────── */

/* --- 17.1  KARTY & TILE-Y — usuwamy box-shadow z transition ---
   Static shadows in styles.css still render fine (paint once at reveal).
   We only strip the *transition* on box-shadow so hover doesn't repaint
   the shadow every frame. Lift on hover stays (transform-based). */
.card,
.fact,
.idea,
.pill,
.step-led,
.glass-panel{
  transition: transform .3s cubic-bezier(.22, .61, .36, 1) !important;
}
/* .card and .fact both had a hover-lift built in via `transform: translateY(-3/-4px)`
   — keep that behavior visible. .sr-edge carve-outs at §8 already restore lift
   for revealed cards on desktop. Nothing else to change here. */

/* --- 17.2  STEP-LED SHIMMER — replace `transition: left` with translateX ---
   The sweeping highlight bar on .step-led hover was moving via `left:-120% → 150%`,
   which forces layout on every frame. Same visual effect via transform is free. */
.step-led::after{
  left: 0 !important;
  transform: translateX(-220%);
  transition: transform .6s cubic-bezier(.4, 0, .4, 1) !important;
  will-change: transform;
}
.step-led:hover::after{
  transform: translateX(250%);
}

/* --- 17.3  PRIMARY BUTTON — kill the background-position hover animation ---
   `.btn--primary` uses a 200%-wide gradient and slides it on hover via
   background-position. That's a texture repositioning — expensive on
   integrated GPUs. The button still gets its lift+glow via transform;
   the gradient shift itself was a subtle detail no one will miss. */
.btn--primary{
  background-size: 100% 100% !important;
  background-position: 0 0 !important;
}
.btn--primary:hover{
  background-position: 0 0 !important;    /* no re-position on hover */
}

/* --- 17.4  [SUPERSEDED BY §35] --------------------------------------------
   This block used to redefine `logoSweep` as a transform animation. It was a
   good idea implemented incompletely, and it is what made the header mark
   look wrong:

     • it dropped the `skewX(-18deg)` that gives the highlight its angle,
       because a transform in the keyframes replaces the base transform
       entirely rather than adding to it;
     • it only covered `::after`, leaving the second, offset band `::before`
       running the same keyframes over a different width, so the two bands
       drifted out of step;
     • `left: 0 !important` moved the start of the sweep from off-canvas to
       the left edge, so the band was already visible when it began.

   §35 at the end of this file replaces it properly: two keyframe sets sized
   to each band, the skew carried through, and the reduced-motion opt-out
   lifted. Nothing is declared here any more — the rules are in one place so
   they cannot contradict each other again. */

/* --- 17.5  DISCOUNT-POP KEYFRAME — replace box-shadow flash with opacity ring ---
   Original keyframe animates box-shadow directly, which forces a repaint
   on every frame. Repaint the same visual flash by fading a pseudo-element
   with a static box-shadow instead — opacity is compositor-only. */
@-webkit-keyframes discountPop{
  0%,100%{ opacity: 0; -webkit-transform: scale(1); transform: scale(1); }
  40%{ opacity: 1; -webkit-transform: scale(1.02); transform: scale(1.02); }
}
@keyframes discountPop{
  0%,100%{ opacity: 0; transform: scale(1); }
  40%{ opacity: 1; transform: scale(1.02); }
}

/* --- 17.6  PROGRESS BARS (survey `.progress > i`) — width → scaleX ---
   `transition: width` animates a layout property. Same visual via
   `transform: scaleX()` is compositor-cheap. Requires transform-origin
   left so the bar grows from the left edge like the original width did. */
.progress{ overflow: hidden; }
.progress > i{
  width: 100% !important;
  transform-origin: left center;
  transform: scaleX(var(--progress, 0));
  transition: transform .35s cubic-bezier(.22, .61, .36, 1) !important;
  will-change: transform;
}
/* app.js writes `.style.width = pct + '%'` to `.progress > i`. We can't stop
   that write, but the CSS above dominates. To make the visual actually
   reflect progress, we'd need JS to write `--progress` instead — see
   site-patch.js §8 for the bridge that mirrors width writes to --progress.

   NOTE: for the survey progress bar this bridge is optional — the visual
   is still animated smoothly via transform, just at a fixed scaleX(0) if
   --progress is never set. If you want the survey progress to animate,
   add the bridge in site-patch.js next round. */

/* --- 17.7  .opt (survey options) — remove box-shadow from transition ---
   Kept transform + background-color (cheap paint, not gradient). */
.opt{
  transition: background-color .2s ease,
              border-color   .2s ease,
              transform      .2s cubic-bezier(.22, .61, .36, 1) !important;
}

/* --- 17.8  CONTAIN on card grids — isolate hover-repaints ---
   `.grid.two/.three/.four` on desktop wrap multiple cards. Hovering one
   card can trigger a repaint of the entire section — a real cost on
   Chrome/Windows. `contain: layout paint` scopes any hover-repaint to
   just that card. NOT applied to .founder-arc / .profile / hero (those
   have intentional overflow-visible lift, containment would clip them). */
@media (min-width: 1024px){
  .card, .fact, .idea, .pill{
    contain: layout paint;
  }
}

/* --- 17.9  Reduced-motion opt-out for the card sheen ---
   `.logo-mask::after` used to be listed here too. It had to come out: the
   `transform: none !important` on this line beats a running animation (an
   important declaration outranks animated values), so it froze the logo
   sweep dead on any machine reporting reduced-motion — which on Windows is
   most of them. The sweep is now owned entirely by §35. */
@media (prefers-reduced-motion: reduce){
  .step-led::after{
    animation: none !important;
    transition: none !important;
    transform: none !important;
  }
}

/* --- 17.9b  [REMOVED] fade-only rewrite that was too aggressive.

   Previous version set `.clip-reveal:not(.in-view) .clip-reveal-inner
   { opacity: 0 !important; transform: none !important; }` — combined with
   §18 content-visibility, sections deep on the page whose clipObserver
   didn't fire in time left every h2 title invisible ("Ideas that build",
   "The Issues We Kept Noticing", etc.). Reverted entirely.

   Now: the original transform-based reveal from styles.css / app.js is
   preserved (translateY / translateX / clip-reveal-inner rise up). Motion
   is CALM (already tuned in §8 to smaller distances + easeOutExpo). The
   §17.10 lock below still freezes each element after its first reveal so
   nothing re-animates on scroll-back.
   -------------------------------------------------------------------- */


/* --- 17.10  [REMOVED] `[data-revealed="1"]` LOCK was the reason nothing
   animated on Windows Chrome. It set `transition: none !important` which
   killed the animation the instant site-patch.js stamped the attribute
   (immediately after the .visible class was added). The reveal never
   got a chance to play. Deleted entirely — the standard CSS-transition
   pattern from styles.css now runs unopposed. */

/* ═════════════════════════════════════════════════════════════════════════
   §M  MASTER REVEAL SYSTEM  (the ONE rule replacing sections 17.10 + 19)

   This is the industry-standard pattern used by Apple, Google, Deloitte,
   LinkedIn, Sopra Steria — the simplest thing that could possibly work:

     1. Default state:  opacity 0, subtle translateY(20px)
     2. IntersectionObserver adds a class the moment element scrolls in
     3. That class flips to opacity 1, translateY 0
     4. Transition property does the interpolation on GPU

   No `!important` transition kills, no data-attribute pinning, no
   `.js-anim` gate, no keyframe-based failsafe. Just a plain transition
   that plays exactly once when the observer fires.

   The observer + class toggling is already implemented in app.js
   (SR_IO, edgeObserver, clipObserver) and site-patch.js §8 fallback.
   All this rule does is make sure the transition itself is:
     - GPU-friendly (only opacity + transform)
     - Slow enough to feel calm on Windows (0.9s)
     - With a natural easing curve (easeOutExpo — same as loop-path draw)
     - Sub-pixel per-frame delta (20px over 0.9s ≈ 1px per 60fps frame)

   Nothing is `!important` overridden — styles.css's original selectors
   still work exactly as intended; this just re-tunes the duration and
   distance to what everyone finds smooth.
   ═════════════════════════════════════════════════════════════════════════ */

/* All eight reveal-scaffold selectors get the same calm transition. */
.sr, .sr-left, .sr-right,
.sr-edge,
.reveal-left, .reveal-right, .reveal-up,
.hl-word,
.clip-reveal-inner,
.tl-item{
  transition-property: opacity, transform;
  transition-duration: .9s;
  transition-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
}

/* Subpage TITLES (the clip-reveal heading rise) — user reported them
   "too fast and lagging sometimes". Give them a longer, calmer duration
   than body elements so the big headline glides rather than snaps. The
   longer duration also means smaller per-frame movement = smoother on
   Windows integrated GPUs. */
.clip-reveal-inner{
  transition-duration: 1.15s !important;
  transition-timing-function: cubic-bezier(0.16, 1, 0.3, 1) !important;
}

/* §T  TITLE PULSATE FIX (pairs with site-patch.js §1d).
   Once a title has been revealed once, site-patch stamps its
   `.clip-reveal` with data-seen="1". This pins the inner span at its
   final position so app.js's observer toggling `.in-view` off can no
   longer make it drop and re-animate. Crucially we DON'T touch
   `transition` here — the first reveal still plays normally; this only
   prevents the re-trigger feedback loop that caused the pulsing. */
.clip-reveal[data-seen="1"] .clip-reveal-inner{
  transform: translate3d(0, 0, 0) !important;
  opacity: 1 !important;
}

/* FOUNDERS "THE TEAM" — all three cards equal height.
   The .profile-grid is already a grid; `align-items: stretch` makes every
   card fill the tallest row height instead of hugging its own content, so
   the three cards line up perfectly even with different amounts of text.
   The featured card's -10px lift is preserved. */
.profile-grid{ align-items: stretch !important; }
.profile-grid > .profile{
  height: 100%;
  display: flex;
  flex-direction: column;
}
/* Push the quote to the bottom so shorter cards don't leave the quote
   floating mid-card — keeps the visual baseline aligned across all four. */
.profile-grid > .profile > p:last-child{ margin-top: auto; }

/* Closing line under each founder — set apart from the factual remit above
   by a thin brand rule, so it reads as a personal note rather than more
   job description. site-patch.js §20 picks one at random per visit. */
.mb-founder-line{
  margin: 16px 0 0 !important;
  padding-top: 14px;
  border-top: 1px solid var(--mist);
  font-style: italic;
  font-size: 14.5px;
  line-height: 1.5;
  color: var(--action);
}
body.dark .mb-founder-line{ color: #9fb4ee; }

/* ── THE RULES MUST SIT ON ONE CONTINUOUS LINE ────────────────────────────
   Neither approach tried before can work on its own:
     • a fixed margin puts each rule right under its own remit — and the
       remits are different lengths, so the rules step up and down;
     • `margin-top: auto` bottom-aligns the block instead — but the quotes
       are 2 or 3 lines, so the rules step again, just from the other side.

   The only way to line them up is to make every card share the SAME row
   grid, so "photo", "name", "role", "remit" and "quote" each get one row
   whose height is the tallest across all five cards. That is exactly what
   `grid-template-rows: subgrid` does: each card opts into the parent grid's
   rows instead of inventing its own. Rows are assigned explicitly by element
   so a card with an unexpected number of children can't shift anything.

   Wrapped in @supports — browsers without subgrid fall back to the old flex
   card, which still looks fine, just with the rules where they were. */
@supports (grid-template-rows: subgrid){
  @media (min-width: 768px){
    .profile-grid{ grid-auto-rows: auto; }
    .profile-grid > .profile{
      display: grid !important;
      grid-template-rows: subgrid;
      grid-row: span 5;
      height: auto !important;
    }
    .profile-grid > .profile > .pf                       { grid-row: 1; }
    .profile-grid > .profile > h3                        { grid-row: 2; }
    .profile-grid > .profile > .role                     { grid-row: 3; }
    .profile-grid > .profile > p:not(.mb-founder-line)   { grid-row: 4; }
    .profile-grid > .profile > .mb-founder-line          { grid-row: 5; align-self: start; }
  }
}

/* The featured card's -10px lift would drag its rule 10px above the others,
   which defeats the whole point. Drop the lift on the team grid — the card
   still reads as featured through its lighter surface and deeper shadow. */
.profile-grid > .profile.feat,
.profile-grid > .profile.feat.sr-edge.in-view{
  transform: none !important;
}

/* FIVE co-founders — app.js renders a 3-column grid; with Katarzyna and
   Kacper injected by site-patch.js §11 we need a balanced layout: one row
   of 5 on wide desktop, 3 + 2 on smaller desktop, 2 per row on tablet and
   a single column on phone. */
@media (min-width: 1240px){
  .profile-grid{ grid-template-columns: repeat(5, 1fr) !important; gap: 16px !important; }
  .profile-grid > .profile{ padding: 22px 16px !important; }
  .profile-grid > .profile .pf{ width: 88px; height: 88px; }
  .profile-grid > .profile h3{ font-size: 18px; }
}
@media (min-width: 1024px) and (max-width: 1239px){
  .profile-grid{ grid-template-columns: repeat(3, 1fr) !important; gap: 18px !important; }
  .profile-grid > .profile{ padding: 22px 18px !important; }
  .profile-grid > .profile .pf{ width: 92px; height: 92px; }
}
@media (min-width: 768px) and (max-width: 1023px){
  .profile-grid{ grid-template-columns: repeat(2, 1fr) !important; }
}

/* With 3 + 2 on mid-desktop the rules only need to line up within each row,
   which `align-items: stretch` already guarantees per grid row. */

/* USE-MOMENTS (and any .grid.four card group) — slide in from the LEFT
   edge to their resting position, with a gentle left-to-right stagger.
   Forces a from-left start regardless of the alternating direction
   app.js assigns, so the whole row wipes in from the left as requested. */
.grid.four > .card.sr-edge:not(.in-view){
  transform: translate3d(-42px, 0, 0) !important;
}
.grid.four > .card.sr-edge.in-view{
  transform: translate3d(0, 0, 0) !important;
}
/* Stagger — each card starts a beat after the previous (left → right). */
.grid.four > .card:nth-child(1){ transition-delay: 0ms; }
.grid.four > .card:nth-child(2){ transition-delay: 120ms; }
.grid.four > .card:nth-child(3){ transition-delay: 240ms; }
.grid.four > .card:nth-child(4){ transition-delay: 360ms; }

/* Preserve `.profile.feat`'s permanent -10px lift on desktop. */
@media (min-width: 1024px){
  .profile.feat.sr-edge.in-view{
    transform: translate3d(0, -10px, 0) scale3d(1, 1, 1);
  }
  /* Card / fact hover-lift still works because styles.css owns hover
     transitions and we haven't overridden them here. */
}


/* ═════════════════════════════════════════════════════════════════════════
   §M-RM  THE ACTUAL WINDOWS FIX  ← this is why nothing moved on Windows.

   ROOT CAUSE (confirmed in styles.css lines 948 & 958):
     @media (prefers-reduced-motion:reduce){
       .sr,.sr-left,.sr-right{opacity:1;transform:none;transition:none}
       .reveal-left,.reveal-right,.reveal-up{opacity:1;transform:none;transition:none}
     }

   Windows very commonly ships with system animations DISABLED
   (Settings → Accessibility → Visual effects → "Animation effects" OFF,
   or the performance tweak "Animate controls and elements inside
   windows"). When that's off, the browser reports
   `prefers-reduced-motion: reduce`, which triggers the rules above and
   kills every scroll reveal — content appears instantly, no motion.
   macOS almost never has "Reduce motion" on, so it always animated.
   Same code, opposite result. That's the entire mystery, solved.

   FIX — the Apple / Google standard: under reduced-motion we still play
   a GENTLE OPACITY CROSSFADE (no sliding, no scaling, no movement — so
   it stays vestibular-safe and honours the spirit of reduced-motion),
   instead of killing motion entirely. Net result: the site fades in
   softly on Windows even with system animations off, while genuine
   reduced-motion users get calm fades with zero movement.

   Loads AFTER styles.css so it wins the cascade; scoped tightly to the
   reveal scaffolds so nothing else is touched.
   ═════════════════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: reduce){

  /* Re-enable a movement-free opacity fade on every reveal scaffold.
     NOTE: `.tl-item` is deliberately EXCLUDED here — the Origin-story
     timeline uses its own auto-play slide-in (see §TL below) which must
     run even under reduced-motion, per the site owner's request. */
  .sr, .sr-left, .sr-right,
  .sr-edge,
  .reveal-left, .reveal-right, .reveal-up,
  .hl-word,
  .clip-reveal-inner{
    transition: opacity .8s ease !important;
    transform: none !important;          /* no slide/scale — reduced-motion respected */
    animation: none !important;
  }

  /* Hidden (pre-reveal) state → fully transparent. */
  .sr:not(.sr-visible),
  .sr-left:not(.sr-visible),
  .sr-right:not(.sr-visible),
  .sr-edge:not(.in-view),
  .reveal-left:not(.reveal-active),
  .reveal-right:not(.reveal-active),
  .reveal-up:not(.reveal-active),
  .hl-word:not(.vis),
  .clip-reveal:not(.in-view) .clip-reveal-inner{
    opacity: 0 !important;
  }

  /* Revealed state → fully opaque. */
  .sr.sr-visible,
  .sr-left.sr-visible,
  .sr-right.sr-visible,
  .sr-edge.in-view,
  .reveal-left.reveal-active,
  .reveal-right.reveal-active,
  .reveal-up.reveal-active,
  .hl-word.vis,
  .clip-reveal.in-view .clip-reveal-inner{
    opacity: 1 !important;
  }

  /* Ambient loops (ticker, pulse dots, hero float) stay OFF under
     reduced-motion — those are continuous movement and genuinely should
     be disabled. Only the one-shot reveal fade is re-enabled above. */
}


/* ─────────────────────────────────────────────────────────────────────────────
  18. [REMOVED] content-visibility: auto on sections.

  It was a performance hack — sections beyond the first skipped
  layout/paint/render until near the viewport. But it interfered with
  app.js's clipObserver (which needs proper layout of every .clip-reveal
  to fire IntersectionObserver reliably), leaving h2 titles invisible on
  sections deep on the page. Removed — the ~3-5% perf gain isn't worth
  the reveal-reliability tradeoff, and mobile-polish already has enough
  other perf optimisations elsewhere.
   ───────────────────────────────────────────────────────────────────────────── */


/* ═════════════════════════════════════════════════════════════════════════
   §19  "SUBMIT AN IDEA" — hide the "Your category" field until needed.

   BUG: app.js renders the custom-category field as
        <div class="field" data-custom hidden> … </div>
   and toggles its `hidden` attribute when the Category select changes.
   BUT styles.css line 602 declares `.field{ display:flex }`, which has
   higher specificity than the browser's built-in `[hidden]{display:none}`
   rule — so the field stayed visible even when hidden. This one line makes
   the `hidden` attribute win again, so "Your category" only appears after
   the user picks "Other / type my own".
   ───────────────────────────────────────────────────────────────────────── */
.field[hidden],
[data-custom][hidden]{ display: none !important; }


/* ═════════════════════════════════════════════════════════════════════════
   §20  CURRENT-STAGE TEXT SHIMMER  (Development Updates roadmap)

   The active roadmap stage label (`.stg.cur .nm` — e.g. "Testing") gets a
   soft left-to-right light sweep, recreating the framer-motion "TextShimmer"
   effect with pure CSS (animated gradient clipped to the text). It is
   GENERIC: whichever stage currently has `.cur` shimmers, so when you move
   the status forward in app.js later, the new current stage animates
   automatically. Completed stages stay green (styles.css .stg.done .bar).

   Technique: two stacked background layers clipped to the text —
     • a solid base colour (the resting text colour), plus
     • a moving highlight gradient (250% wide) swept across via
       background-position, exactly like the framer component.
   ───────────────────────────────────────────────────────────────────────── */
@keyframes mbTextShimmer{
  0%   { background-position: 100% center, center; }
  100% { background-position: 0% center, center; }
}
.stg.cur .nm{
  --mb-base: #8b93a1;           /* resting colour of the shimmering text */
  --mb-hi:   var(--action);     /* highlight sweep — on-brand blue */
  --mb-spread: 18px;            /* width of the bright band */
  color: transparent !important;
  -webkit-text-fill-color: transparent;
  background-image:
    linear-gradient(90deg,
      transparent calc(50% - var(--mb-spread)),
      var(--mb-hi),
      transparent calc(50% + var(--mb-spread))),
    linear-gradient(var(--mb-base), var(--mb-base));
  background-repeat: no-repeat;
  background-size: 250% 100%, 100% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  /* FIX: previously this single `animation` overrode the shared `stgLabel`
     fade (§ roadmap), so "Testing" was already visible + shimmering while
     every other label faded in — out of sync with its blue bar. Now it runs
     TWO animations: first the same fade the other labels use (held hidden
     until 1.35s — the moment its blue bar fills — via `both`), then the
     shimmer starts once the fade has finished. So it appears smoothly like
     the rest, in step with the bar underneath. */
  animation:
    stgLabel .7s cubic-bezier(.16, 1, .3, 1) 1.35s both,
    mbTextShimmer 2.4s linear 2.4s infinite;
}
body.dark .stg.cur .nm{
  --mb-base: #8b93a1;
  --mb-hi: #cdd8ff;             /* lighter sweep for contrast on dark bg */
}
/* Deliberately NOT gated behind prefers-reduced-motion — Windows commonly
   reports reduced-motion (system animations off), and the user wants this
   sweep to run there too. A CSS keyframe animation still plays under
   reduced-motion; the base gradient keeps the text fully legible regardless. */


/* ═════════════════════════════════════════════════════════════════════════
   §21  SUGGESTIONS LIST — smooth staggered reveal (PC only).

   Each idea card carries `.sr`; app.js's IntersectionObserver adds
   `.sr-visible` when it enters the viewport, so the cards already reveal as
   you scroll. Here we make that reveal cinematic on desktop: a soft rise +
   fade with a gentle left-to-right / top-to-bottom cascade for cards that
   enter together. Plays once per load (fresh DOM on refresh restarts it).
   Phones are intentionally excluded — the request was PC-only. Same easing
   token used across the site so it feels consistent everywhere. */
@media (min-width: 1024px){
  #main [data-list] .idea.sr{
    transition: opacity .7s cubic-bezier(.16, 1, .3, 1),
                transform .7s cubic-bezier(.16, 1, .3, 1);
  }
  #main [data-list] .idea.sr:not(.sr-visible){
    opacity: 0 !important;
    transform: translate3d(0, 30px, 0) !important;
  }
  #main [data-list] .idea.sr.sr-visible{
    opacity: 1 !important;
    transform: none !important;
  }
  /* Cascade — cards that scroll in as a group light up one after another. */
  #main [data-list] .idea:nth-child(6n + 1){ transition-delay: 0ms; }
  #main [data-list] .idea:nth-child(6n + 2){ transition-delay: 80ms; }
  #main [data-list] .idea:nth-child(6n + 3){ transition-delay: 160ms; }
  #main [data-list] .idea:nth-child(6n + 4){ transition-delay: 240ms; }
  #main [data-list] .idea:nth-child(6n + 5){ transition-delay: 320ms; }
  #main [data-list] .idea:nth-child(6n){     transition-delay: 400ms; }
}


/* ═════════════════════════════════════════════════════════════════════════
   §22  STATUS GUIDE — redesigned rows (no white pills).

   site-patch.js §13 rebuilds the "Status guide" panel into rows of:
     [ tinted icon tile + coloured dot ] [ coloured label ] | [ muted desc ]
   Colours come from the site's theme tokens (--action / --warn / --success),
   so they re-tint automatically when the site's colour theme changes. Hover
   gives each row a subtle lift + coloured border — interactive, on every
   browser and OS. */
.mb-sg{ display: flex; flex-direction: column; gap: 10px; margin-top: 12px; }
.mb-sg-row{
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 12px 14px;
  border-radius: 14px;
  border: 1px solid var(--mist);
  background: var(--card);
  transition: border-color .25s ease, transform .25s ease, box-shadow .25s ease;
}
.mb-sg-row:hover{
  transform: translateY(-2px);
  border-color: color-mix(in srgb, var(--c) 55%, var(--mist));
  box-shadow: 0 8px 22px rgba(0, 0, 0, .10);
}
.mb-sg-ico{
  flex: none;
  width: 40px; height: 40px;
  border-radius: 11px;
  display: grid; place-items: center;
  background: color-mix(in srgb, var(--c) 16%, transparent);
  border: 1px solid color-mix(in srgb, var(--c) 32%, transparent);
}
.mb-sg-dot{
  width: 12px; height: 12px; border-radius: 50%;
  background: var(--c);
  box-shadow: 0 0 10px color-mix(in srgb, var(--c) 60%, transparent);
}
.mb-sg-label{ font-weight: 800; font-size: 16px; color: var(--c); min-width: 108px; }
.mb-sg-sep{ width: 1px; align-self: stretch; background: var(--mist); margin: 3px 2px; }
.mb-sg-desc{ color: var(--slate); font-size: 14px; line-height: 1.4; }
/* per-status colour — tied to theme tokens where possible */
.mb-sg-row[data-st="new"]{      --c: #7b83ff; }
.mb-sg-row[data-st="review"]{   --c: var(--warn); }
.mb-sg-row[data-st="planning"]{ --c: var(--action); }
.mb-sg-row[data-st="complete"]{ --c: var(--success); }
/* Small phones — let the description wrap under the label if space is tight. */
@media (max-width: 420px){
  .mb-sg-label{ min-width: 92px; font-size: 15px; }
  .mb-sg-ico{ width: 34px; height: 34px; }
}


/* ═════════════════════════════════════════════════════════════════════════
   §23  PRODUCT "USE MOMENTS" PHOTO GRID.

   site-patch.js §15 swaps the four text cards under "Made for real days."
   for the four lifestyle photos (which already carry their title + caption).
   2×2 on desktop/tablet, single column on phones. Images are 3:2 landscape
   to match the source photos, cover-cropped so any cell size stays clean. */
.moment-strip{
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 18px;
  margin-top: 8px;
}
.moment-cell{
  aspect-ratio: 3 / 2;
  border-radius: 16px;
  overflow: hidden;                 /* clips the zoomed image → crops the white frame */
  box-shadow: 0 10px 30px rgba(0, 0, 0, .14);
}
.moment-shot{
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  display: block;
  transform: scale(1.06);           /* small zoom so the photos' white border isn't visible */
}
body.dark .moment-cell{ box-shadow: 0 12px 34px rgba(0, 0, 0, .45); }
@media (max-width: 719px){
  .moment-strip{ grid-template-columns: 1fr; gap: 14px; }
}
/* wrapper holding each photo + its icon */
.moment-item{ display: block; }

/* Clean fade-in on scroll — PC only. Each photo starts faded + slightly
   lowered, then eases to full clarity when it enters the viewport. The
   `.moment-in` class is added ONCE by site-patch.js §15 (IntersectionObserver
   that unobserves after firing), so there is no flicker, no re-trigger, and
   no pulsing — the reveal plays a single clean time per page load. GPU-only
   properties (opacity + transform) keep it smooth on every machine. */
@media (min-width: 1024px){
  .moment-item{
    opacity: 0;
    transform: translate3d(0, 26px, 0);
    transition: opacity .85s cubic-bezier(.16, 1, .3, 1),
                transform .85s cubic-bezier(.16, 1, .3, 1);
    will-change: opacity, transform;
  }
  .moment-item.moment-in{
    opacity: 1;
    transform: none;
  }
  /* Gentle stagger so the four photos resolve one after another. */
  .moment-strip .moment-item:nth-child(1){ transition-delay: 0ms; }
  .moment-strip .moment-item:nth-child(2){ transition-delay: 90ms; }
  .moment-strip .moment-item:nth-child(3){ transition-delay: 180ms; }
  .moment-strip .moment-item:nth-child(4){ transition-delay: 270ms; }
}

/* (Icons under the Use-moments photos were removed by request — the titles
   and icons are already baked into the photographs themselves.) */


/* ═════════════════════════════════════════════════════════════════════════
   §24  REMOVE THE SMALL SECTION LABELS (eyebrows + breadcrumbs).

   Every section's small kicker above the big heading — "The need",
   "Product preview", "Community", "Science", "Founders", "Quick FAQ",
   the hero's "Energy gummies — coming soon", and the page breadcrumbs like
   "Learn / About This Product" — is hidden site-wide. The big headings and
   all body content are untouched. (The elements stay in the DOM so scripts
   that read their text, e.g. to place the warning watermark, still work.) */
.eyebrow,
.crumbs{ display: none !important; }


/* ═════════════════════════════════════════════════════════════════════════
   §25  SCIENCE PAGE — premium glassmorphism info cards
   (Dose clarity / Timing / Individual variability).

   site-patch.js §16 rebuilds each card into an outer entrance container
   (`.card`) + an inner glass card (`.mb-sci-card`) holding: a circular icon
   with a radial-gradient glow, a numbered badge (01/02/03), the heading and
   copy, a bottom accent line, and a light-sweep. It also drops faint floating
   particles, soft background glows and a low-opacity connector curve behind
   the grid. The 3D cursor tilt is handled in JS (max 3°).

   Identical on every device/browser: layout, radii, shadows and glass are all
   the same; only the hover micro-interactions are desktop-pointer only.
   Entrance reuses app.js's reliable `.in-view` toggle (no competing observer)
   but restyles it as a fade + gentle rise + blur-clear — elegant, no bounce. */
.mb-sci-steps{ position: relative; isolation: isolate; }

/* soft background depth (radial light) */
.mb-sci-steps::before{
  content: ""; position: absolute; inset: -14% -6%; z-index: 0; pointer-events: none;
  background:
    radial-gradient(440px 300px at 16% 22%, color-mix(in srgb, var(--action) 12%, transparent), transparent 70%),
    radial-gradient(420px 300px at 84% 78%, color-mix(in srgb, var(--action) 9%, transparent), transparent 72%);
  opacity: .8;
}
/* faint floating particles */
.mb-sci-particles{ position: absolute; inset: 0; z-index: 0; pointer-events: none; overflow: hidden; }
.mb-sci-particles i{
  position: absolute; width: 6px; height: 6px; border-radius: 50%;
  background: radial-gradient(circle, color-mix(in srgb, var(--action) 60%, transparent), transparent 70%);
  opacity: .10; animation: sciFloat 15s ease-in-out infinite;
}
@keyframes sciFloat{ 0%,100%{ transform: translateY(0); } 50%{ transform: translateY(-18px); } }
/* ultra-thin connector curve with glowing nodes (opacity < 10%) */
.mb-sci-conn{ position: absolute; z-index: 0; pointer-events: none; opacity: .08;
  top: 34px; left: 6%; right: 6%; height: 46px; }
.mb-sci-conn svg{ width: 100%; height: 100%; display: block; }
.mb-sci-conn circle{ filter: drop-shadow(0 0 3px var(--action)); }
@media (max-width: 1023px){ .mb-sci-conn{ display: none; } }

/* OUTER card — pure entrance container (glass moved to the inner element) */
.mb-sci-steps > .card{
  background: transparent !important;
  border: 0 !important;
  box-shadow: none !important;
  padding: 0 !important;
  position: relative; z-index: 1;
  overflow: visible !important;
  perspective: 1000px;
}
/* entrance: fade + rise + de-blur (reuses app.js .in-view; restrained, no bounce) */
.mb-sci-steps > .card.sr-edge{
  transition: opacity .85s cubic-bezier(.22, 1, .36, 1),
              transform .85s cubic-bezier(.22, 1, .36, 1),
              filter .85s ease !important;
}
.mb-sci-steps > .card.sr-edge:not(.in-view){
  opacity: 0 !important; transform: translate3d(0, 28px, 0) !important; filter: blur(9px) !important;
}
.mb-sci-steps > .card.sr-edge.in-view{
  opacity: 1 !important; transform: none !important; filter: blur(0) !important;
}
.mb-sci-steps > .card:nth-child(2).sr-edge{ transition-delay: .12s !important; }
.mb-sci-steps > .card:nth-child(3).sr-edge{ transition-delay: .24s !important; }

/* INNER — the glass card */
.mb-sci-card{
  position: relative; overflow: hidden;
  border-radius: 22px;
  padding: clamp(24px, 2.4vw, 32px);
  min-height: 100%;
  background: rgba(255,255,255,.55);
  -webkit-backdrop-filter: blur(16px) saturate(1.25);
          backdrop-filter: blur(16px) saturate(1.25);
  border: 1px solid rgba(255,255,255,.5);
  box-shadow:
    0 1px 1px rgba(16,24,40,.04),
    0 6px 14px rgba(16,24,40,.06),
    0 18px 40px rgba(16,24,40,.08),
    inset 0 1px 0 rgba(255,255,255,.6);
  transition: transform .35s cubic-bezier(.22,1,.36,1),
              box-shadow .35s ease, background .35s ease, border-color .35s ease;
  transform-style: preserve-3d;
}
/* soft gradient edge */
.mb-sci-card::after{
  content: ""; position: absolute; inset: 0; border-radius: 22px; padding: 1px; pointer-events: none;
  background: linear-gradient(140deg, rgba(255,255,255,.9), rgba(255,255,255,.04) 42%, color-mix(in srgb, var(--action) 30%, transparent));
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
  opacity: .55;
}
/* slow light sweep every ~18s */
.mb-sci-sheen{
  position: absolute; top: -20%; bottom: -20%; left: -45%; width: 40%;
  background: linear-gradient(105deg, transparent, rgba(255,255,255,.4), transparent);
  transform: skewX(-16deg); pointer-events: none; z-index: 1;
  animation: sciSheen 18s ease-in-out infinite;
}
@keyframes sciSheen{ 0%,86%{ left: -45%; } 95%,100%{ left: 125%; } }

/* icon in a soft radial-gradient circle */
.mb-sci-card .mb-sci-ico{
  position: relative; z-index: 2;
  width: 56px; height: 56px; border-radius: 50%;
  display: grid; place-items: center; margin: 0 0 18px;
  color: var(--action);
  background: radial-gradient(circle at 35% 30%, color-mix(in srgb, var(--action) 20%, #fff), color-mix(in srgb, var(--action) 6%, #fff));
  box-shadow: inset 0 0 12px color-mix(in srgb, var(--action) 18%, transparent), 0 4px 12px rgba(16,24,40,.08);
  transition: transform .35s cubic-bezier(.22,1,.36,1), box-shadow .35s ease;
}
.mb-sci-card .mb-sci-ico svg{ width: 26px; height: 26px; }

/* numbered badge, upper-right */
.mb-sci-card .mb-sci-num{
  position: absolute; top: 16px; right: 16px; z-index: 3;
  width: 30px; height: 30px; border-radius: 50%;
  display: grid; place-items: center;
  font-size: 11px; font-weight: 800; letter-spacing: .03em;
  color: var(--slate);
  background: rgba(255,255,255,.6);
  border: 1px solid rgba(16,24,40,.08);
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
}
/* bottom accent line — animates left→right on entry */
.mb-sci-card .mb-sci-accent{
  position: absolute; left: clamp(24px, 2.4vw, 32px); bottom: 18px; z-index: 2;
  height: 3px; width: 0; border-radius: 3px;
  background: linear-gradient(90deg, var(--action), color-mix(in srgb, var(--action) 25%, transparent));
  transition: width .9s cubic-bezier(.22,1,.36,1) .3s;
}
.card.in-view .mb-sci-card .mb-sci-accent{ width: 48px; }

.mb-sci-card h3, .mb-sci-card p{ position: relative; z-index: 2; }
.mb-sci-card p{ margin-bottom: 26px; }   /* room above the accent line */

/* HOVER — desktop pointers only (transform handled by JS tilt) */
@media (hover: hover) and (pointer: fine){
  .mb-sci-card:hover{
    background: rgba(255,255,255,.7);
    border-color: rgba(255,255,255,.85);
    box-shadow:
      0 2px 4px rgba(16,24,40,.05),
      0 10px 24px rgba(16,24,40,.09),
      0 34px 64px rgba(16,24,40,.13),
      inset 0 1px 0 rgba(255,255,255,.75);
  }
  .mb-sci-card:hover .mb-sci-ico{
    transform: scale(1.08);
    box-shadow: inset 0 0 16px color-mix(in srgb, var(--action) 26%, transparent),
                0 0 24px color-mix(in srgb, var(--action) 32%, transparent),
                0 6px 16px rgba(16,24,40,.1);
  }
}

/* DARK MODE glass */
body.dark .mb-sci-card{
  background: rgba(26,28,36,.5);
  border-color: rgba(255,255,255,.09);
  box-shadow: 0 1px 1px rgba(0,0,0,.3), 0 12px 32px rgba(0,0,0,.42), inset 0 1px 0 rgba(255,255,255,.06);
}
body.dark .mb-sci-card .mb-sci-num{ background: rgba(255,255,255,.06); border-color: rgba(255,255,255,.1); color: #c9cdd6; }
body.dark .mb-sci-card .mb-sci-ico{
  background: radial-gradient(circle at 35% 30%, color-mix(in srgb, var(--action) 32%, #1a1c24), color-mix(in srgb, var(--action) 10%, #1a1c24));
}
@media (hover: hover) and (pointer: fine){
  body.dark .mb-sci-card:hover{ background: rgba(34,37,46,.62); border-color: rgba(255,255,255,.16); }
}

/* comfortable spacing between the cards */
@media (min-width: 768px){
  .mb-sci-steps{ gap: clamp(20px, 2.4vw, 36px) !important; }
}

/* Reduced motion — keep it calm: no sweep / float, cards simply fade in. */
@media (prefers-reduced-motion: reduce){
  .mb-sci-sheen, .mb-sci-particles i{ animation: none !important; }
}

/* ── Removed by request: the background glow/particles/connector behind the
   cards, the blue accent line under the text, and the numbered circle badges.
   The glass cards, icons, hover and entrance are all kept. ── */
.mb-sci-steps::before{ display: none !important; }        /* soft background light */
.mb-sci-particles,
.mb-sci-conn{ display: none !important; }                 /* floating particles + connector curve */
.mb-sci-card .mb-sci-accent,
.mb-sci-card .mb-sci-num{ display: none !important; }      /* blue accent line + 01/02/03 badges */
.mb-sci-card p{ margin-bottom: 0 !important; }             /* reclaim the space the accent line used */

/* ── Borders matched to the rest of the site, and tighter spacing ──────────
   Every other card/panel on the site is bordered with `var(--mist)` (the
   shared border token, which flips automatically in dark mode). The glass
   cards were using a translucent white edge, so on light backgrounds they
   read as borderless and in dark mode they didn't match. Use the site token
   so all rectangles on the site share one edge colour. */
.mb-sci-card{
  border: 1px solid var(--mist) !important;
}
.mb-sci-card::after{ display: none !important; }   /* the old white gradient edge */
@media (hover: hover) and (pointer: fine){
  .mb-sci-card:hover{
    border-color: color-mix(in srgb, var(--action) 32%, var(--mist)) !important;
  }
}

/* DARK MODE — white icons, same edge colour as every other card. */
body.dark .mb-sci-card .mb-sci-ico{
  color: #fff;
  background: rgba(255, 255, 255, .07);
  box-shadow: inset 0 0 12px rgba(255, 255, 255, .06);
}
body.dark .mb-sci-card .mb-sci-ico svg{ stroke: #fff; }
@media (hover: hover) and (pointer: fine){
  body.dark .mb-sci-card:hover .mb-sci-ico{
    background: rgba(255, 255, 255, .12);
    box-shadow: inset 0 0 16px rgba(255, 255, 255, .10), 0 0 22px rgba(255, 255, 255, .12);
  }
}

/* Smaller gap between the intro copy and the cards. The grid sits inside a
   `.section--tight`, whose top padding was leaving a large empty band — pull
   the row up a little (scaled down on phones). */
.mb-sci-steps{ margin-top: -30px; }
@media (max-width: 767px){ .mb-sci-steps{ margin-top: -14px; } }

/* ── SOLID, UNBROKEN EDGES ────────────────────────────────────────────────
   The light-sweep sits just off the left edge at rest, but its -16° skew
   pushed the top corner back into view — showing as a pale diagonal wedge
   that broke the border line at each card's top-left. Fix: the sweep is now
   fully transparent at rest and only fades in while it crosses the card, so
   the border reads as one clean, solid line the whole way round. */
.mb-sci-sheen{
  opacity: 0;
  animation-name: sciSheenFixed !important;
}
@keyframes sciSheenFixed{
  0%, 86%   { left: -60%; opacity: 0; }
  89%       { opacity: .35; }
  93%       { opacity: .35; }
  96%, 100% { left: 125%; opacity: 0; }
}
body.dark .mb-sci-sheen{
  background: linear-gradient(105deg, transparent, rgba(255,255,255,.10), transparent);
}

/* Border = exactly the site's shared edge token, same as every other
   rectangle on the site. (An earlier version blended it with #fff, which is
   why it looked white in dark mode — that blending is gone.) */
.mb-sci-card{
  border: 1px solid var(--mist) !important;
  border-radius: 22px !important;
}
body.dark .mb-sci-card{
  border-color: var(--mist) !important;
}

/* Corners: the OUTER entrance container must not paint anything of its own.
   Its inherited `.card` border/background was drawing straight edges that sat
   slightly outside the rounded glass card — which is why the borders looked
   offset and the corners appeared to be missing. Blank it out completely,
   including any pseudo-elements, so the only visible edge is the inner
   card's own rounded 1px border. */
.mb-sci-steps > .card{
  border: 0 !important;
  background: none !important;
  box-shadow: none !important;
  border-radius: 22px !important;
  overflow: visible !important;
  -webkit-backdrop-filter: none !important;
          backdrop-filter: none !important;
}
.mb-sci-steps > .card::before,
.mb-sci-steps > .card::after{
  content: none !important;
  display: none !important;
}

/* HOVER EDGE FIX — the lifted card's top border used to vanish.
   Two causes: the hover style painted a white 1px inset highlight
   (`inset 0 1px 0 rgba(255,255,255,…)`) directly over the top border, and a
   1px border can sub-pixel away under the 3D tilt. Fix: drop the white
   highlight and add an inset 1px ring in the same edge colour, which renders
   crisply through the lift/tilt. Border stays `var(--mist)` — identical to
   every other rectangle on the site — while animating. */
@media (hover: hover) and (pointer: fine){
  .mb-sci-card:hover{
    border-color: var(--mist) !important;
    box-shadow:
      inset 0 0 0 1px var(--mist),
      0 2px 4px rgba(16, 24, 40, .05),
      0 10px 24px rgba(16, 24, 40, .09),
      0 34px 64px rgba(16, 24, 40, .13) !important;
  }
  body.dark .mb-sci-card:hover{
    border-color: var(--mist) !important;
    box-shadow:
      inset 0 0 0 1px var(--mist),
      0 10px 24px rgba(0, 0, 0, .40),
      0 34px 64px rgba(0, 0, 0, .50) !important;
  }
}

/* ROOT CAUSE of the cut-off top edge on hover: `backdrop-filter`.
   When an element with backdrop-filter is transformed (our lift + 3D tilt),
   the browser re-samples the backdrop against the *untransformed* box, which
   clips the element's own border along the moving edge — so the top border
   vanished mid-animation. Since the section behind these cards is a flat
   colour, the blur adds nothing visually; removing it fixes the rendering
   completely and the card keeps the same appearance.

   With the filter gone, the border and shadow are painted on the same
   element that moves — so they now travel with the card, perfectly intact. */
.mb-sci-card{
  -webkit-backdrop-filter: none !important;
          backdrop-filter: none !important;
  background: var(--card) !important;
  -webkit-backface-visibility: hidden;
          backface-visibility: hidden;
}
@media (hover: hover) and (pointer: fine){
  .mb-sci-card:hover{ background: var(--card) !important; }
}
/* the lifted card must not be clipped by its container */
.mb-sci-steps,
.mb-sci-steps > .card{ overflow: visible !important; }

/* TOP EDGE — visible at rest AND all the way through the animation.
   The card's resting shadow still carried a white 1px inset highlight
   (`inset 0 1px 0 rgba(255,255,255,.6)`) that painted straight over the top
   border. Removing it only from :hover wasn't enough: box-shadow interpolates
   between the two states, so mid-transition that white line reappeared and
   erased the top edge exactly while the card was moving. It is now gone from
   BOTH states, and both use the same `inset 0 0 0 1px var(--mist)` ring — so
   there is nothing white to fade through and the top border stays solid the
   entire time. */
.mb-sci-card{
  box-shadow:
    inset 0 0 0 1px var(--mist),
    0 1px 1px rgba(16, 24, 40, .04),
    0 6px 14px rgba(16, 24, 40, .06),
    0 18px 40px rgba(16, 24, 40, .08) !important;
}
body.dark .mb-sci-card{
  box-shadow:
    inset 0 0 0 1px var(--mist),
    0 1px 1px rgba(0, 0, 0, .30),
    0 12px 32px rgba(0, 0, 0, .42) !important;
}
/* dark hover re-declared last so it still deepens on lift */
@media (hover: hover) and (pointer: fine){
  body.dark .mb-sci-card:hover{
    box-shadow:
      inset 0 0 0 1px var(--mist),
      0 10px 24px rgba(0, 0, 0, .40),
      0 34px 64px rgba(0, 0, 0, .50) !important;
  }
}


/* ═════════════════════════════════════════════════════════════════════════
   §29  SUSTAINABILITY BLOCKS (site-patch §19).
   The eco band on Home and the three-card eco grid on the Product page. */
.mb-eco-band{
  position: relative;
  margin-top: clamp(24px, 3vw, 38px);
  padding: clamp(22px, 2.6vw, 30px) clamp(22px, 2.6vw, 32px);
  border: 1px solid var(--mist);
  border-radius: 20px;
  background:
    radial-gradient(120% 140% at 0% 0%, color-mix(in srgb, var(--action) 7%, transparent), transparent 62%),
    var(--card);
  overflow: hidden;
}
/* slim brand rule down the left edge */
.mb-eco-band::before{
  content: "";
  position: absolute; left: 0; top: 14px; bottom: 14px; width: 3px;
  border-radius: 0 3px 3px 0;
  background: linear-gradient(180deg, var(--action), color-mix(in srgb, var(--action) 18%, transparent));
}
.mb-eco-band-title{
  display: block;
  font-weight: 800;
  font-size: 11.5px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--action);
  margin-bottom: 18px;
}
.mb-eco-band-items{
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(16px, 2.2vw, 30px);
}
.mb-eco-band-item{
  display: flex;
  align-items: flex-start;
  gap: 13px;
}
.mb-eco-band-ico{
  flex: none;
  width: 46px; height: 46px;
  display: grid; place-items: center;
  border-radius: 12px;
  color: var(--action);
  background: color-mix(in srgb, var(--action) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--action) 20%, transparent);
}
.mb-eco-band-ico svg{ width: 30px; height: 30px; display: block; overflow: visible; }
.mb-eco-band-copy{ display: block; min-width: 0; }
.mb-eco-band-copy b{
  display: block;
  color: var(--ink);
  font-size: 14.5px;
  font-weight: 700;
  line-height: 1.3;
}
.mb-eco-band-copy em{
  display: block;
  margin-top: 3px;
  font-style: normal;
  color: var(--slate);
  font-size: 13px;
  line-height: 1.45;
}
@media (max-width: 820px){
  .mb-eco-band-items{ grid-template-columns: 1fr; gap: 16px; }
}


/* ─────────────────────────────────────────────────────────────────────────
   MISSION PAGE — circular-economy mark beside the copy.
   Arcs follow the theme (currentColor), the bolt uses the brand navy, and
   the whole mark is inline SVG so it is transparent and sharp at any size. */
.mb-mission-grid{
  display: grid;
  grid-template-columns: 1.25fr .75fr;
  gap: clamp(24px, 4vw, 64px);
  align-items: center;
}
.mb-mission-text{ min-width: 0; }
.mb-mission-art{
  justify-self: center;
  width: min(470px, 100%);
  color: var(--ink);          /* → white automatically in dark mode */
}
.mb-mission-art svg{ width: 100%; height: auto; display: block; overflow: visible; }
@media (max-width: 900px){
  .mb-mission-grid{ grid-template-columns: 1fr; }
  .mb-mission-art{ width: min(320px, 84%); justify-self: start; margin-top: 10px; }
}

/* ── The loop builds itself clockwise, like a clock hand ──────────────────
   1. top arrow draws            0.00s
   2. bolt strikes + shines      0.85s
   3. right arrow draws          2.10s
   4. plant grows, leaves sway   2.95s
   5. left arrow draws           4.25s
   6. can appears, then the ring
      and the cross strike it    5.10s
   Everything uses currentColor, so the whole mark turns white in dark mode. */

/* arrows */
.mb-ml-a1{ stroke-dasharray: 140; stroke-dashoffset: 140; }
.mb-ml-a2{ stroke-dasharray: 100; stroke-dashoffset: 100; }
.mb-ml-a3{ stroke-dasharray: 120; stroke-dashoffset: 120; }
.mb-art-in .mb-ml-a1{ animation: mbArcDraw .95s cubic-bezier(.42,0,.25,1) 0s forwards; }
.mb-art-in .mb-ml-a2{ animation: mbArcDraw .85s cubic-bezier(.42,0,.25,1) 2.10s forwards; }
.mb-art-in .mb-ml-a3{ animation: mbArcDraw .95s cubic-bezier(.42,0,.25,1) 4.25s forwards; }
@keyframes mbArcDraw{ to{ stroke-dashoffset: 0; } }

/* bolt — snaps in hard, then flashes twice like lightning */
.mb-ml-bolt{ opacity: 0; transform-box: fill-box; transform-origin: 50% 50%; }
.mb-art-in .mb-ml-bolt{ animation: mbBoltStrike 1.5s cubic-bezier(.2,1.4,.3,1) .85s forwards; }
@keyframes mbBoltStrike{
  0%   { opacity: 0; transform: scale(.55) rotate(-9deg); filter: none; }
  14%  { opacity: 1; transform: scale(1.18) rotate(3deg);
         filter: drop-shadow(0 0 16px currentColor) brightness(1.5); }
  24%  { opacity: .35; transform: scale(1) rotate(0deg); filter: none; }
  36%  { opacity: 1; filter: drop-shadow(0 0 26px currentColor) brightness(1.7); }
  48%  { opacity: .7;  filter: drop-shadow(0 0 8px currentColor); }
  62%  { opacity: 1;   filter: drop-shadow(0 0 18px currentColor); }
  100% { opacity: 1; transform: none; filter: none; }
}

/* plant — rises, then the leaves sway and glow green */
.mb-ml-plant{ opacity: 0; }
.mb-art-in .mb-ml-plant{ animation: mbFadeIn .5s ease 2.95s forwards; }
.mb-ml-leaf{ transform-box: fill-box; transform-origin: 50% 100%; }
/* grass tufts — sway with the plant, but at about a third of the amplitude
   so they read as ground texture rather than competing with the leaves */
.mb-ml-grass{ transform-box: fill-box; transform-origin: 50% 100%; }
.mb-art-in .mb-ml-grass{ animation: mbGrassSway 2.4s cubic-bezier(.36,1,.4,1) 3.15s 1 both; }
@keyframes mbGrassSway{
  0%   { transform: rotate(0deg); }
  28%  { transform: rotate(-2.6deg); }
  58%  { transform: rotate(1.7deg); }
  80%  { transform: rotate(-.7deg); }
  100% { transform: rotate(0deg); }
}
.mb-art-in .mb-ml-leaf{ animation: mbLeafSway 2.4s cubic-bezier(.36,1,.4,1) 3.15s 1 both; }
@keyframes mbLeafSway{
  0%   { transform: rotate(0deg); stroke: currentColor; filter: none; }
  28%  { transform: rotate(-8deg); }
  42%  { stroke: #22c55e; filter: drop-shadow(0 0 5px rgba(34,197,94,.85)); }
  58%  { transform: rotate(5deg); }
  70%  { stroke: #22c55e; filter: drop-shadow(0 0 4px rgba(34,197,94,.55)); }
  80%  { transform: rotate(-2deg); }
  100% { transform: rotate(0deg); stroke: currentColor; filter: none; }
}

/* can — body fades in, then the ring draws and the slash strikes through */
.mb-ml-can{ opacity: 0; }
.mb-art-in .mb-ml-can{ animation: mbFadeIn .5s ease 5.10s forwards; }
@keyframes mbFadeIn{ to{ opacity: 1; } }
.mb-ml-ring{ stroke-dasharray: 152; stroke-dashoffset: 152; }
.mb-ml-slash{ stroke-dasharray: 50; stroke-dashoffset: 50; }
.mb-art-in .mb-ml-ring{ animation: mbArcDraw .7s cubic-bezier(.45,0,.2,1) 5.45s forwards; }
.mb-art-in .mb-ml-slash{ animation: mbArcDraw .4s cubic-bezier(.4,0,.2,1) 6.05s forwards; }

/* ─────────────────────────────────────────────────────────────────────────
   PRODUCT-PAGE ECO CARDS — icon on top, everything left-aligned, three in a
   row with equal gaps. One rhythm for the vertical spacing:
       icon → 18px → heading → 10px → body
   Icons are inline SVG (transparent, resolution-independent), sized 56px. */
.mb-eco-grid{
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(20px, 2.6vw, 34px);
  align-items: start;
}
.mb-eco-card{
  text-align: left;                 /* matches the rest of the page */
  padding: clamp(22px, 2.2vw, 28px);
  border: 1px solid var(--mist);
  border-radius: 18px;
  background: var(--card);
  transition: transform .3s cubic-bezier(.22,1,.36,1), box-shadow .3s ease, border-color .3s ease;
}
@media (hover: hover) and (pointer: fine){
  .mb-eco-card:hover{
    transform: translateY(-3px);
    box-shadow: 0 10px 26px rgba(16,24,40,.09);
    border-color: color-mix(in srgb, var(--action) 34%, var(--mist));
  }
}
.mb-eco-ico{
  display: block;
  width: 72px; height: 72px;        /* larger — clearly readable */
  margin: 0 0 20px;                 /* even gap under the icon */
  color: var(--ink);
}
.mb-eco-ico svg{ width: 100%; height: 100%; display: block; overflow: visible; }
.mb-eco-card h3{ margin: 0 0 10px; font-size: 18px; line-height: 1.25; }
.mb-eco-card p{ margin: 0; color: var(--slate); font-size: 14.5px; line-height: 1.6; }
.mb-eco-card p b{ color: var(--ink); }

/* PHONE / TABLET — stack, keep the same left alignment and rhythm. */
@media (max-width: 900px){
  .mb-eco-grid{ grid-template-columns: 1fr; gap: 14px; }
  .mb-eco-ico{ width: 62px; height: 62px; margin-bottom: 16px; }
}

/* ── ICON ANIMATIONS — play once when the block scrolls into view ─────────
   site-patch adds `.mb-eco-in` per card via IntersectionObserver.
   Deliberately NOT disabled under prefers-reduced-motion: Windows commonly
   reports reduced-motion and these are short, one-shot, non-looping. */

/* 1 — NO CAN: the prohibition ring draws, then the slash strikes through. */
.mb-eco-ring{ stroke-dasharray: 158; stroke-dashoffset: 158; }
.mb-eco-slash{ stroke-dasharray: 51; stroke-dashoffset: 51; }
.mb-eco-in .mb-eco-ring{ animation: ecoDrawRing .75s cubic-bezier(.5,0,.2,1) .15s forwards; }
.mb-eco-in .mb-eco-slash{ animation: ecoDrawSlash .45s cubic-bezier(.4,0,.2,1) .85s forwards; }
@keyframes ecoDrawRing { to { stroke-dashoffset: 0; } }
@keyframes ecoDrawSlash{ to { stroke-dashoffset: 0; } }

/* 2 — RECYCLING: each arrow draws in turn AROUND the triangle — left edge
       up, then right edge down, then the bottom edge back to the start. The
       motion follows the path of the triangle instead of spinning, and the
       loop finishes where it began. */
.mb-eco-arrow{ stroke-dasharray: 52; stroke-dashoffset: 52; }
.mb-eco-in .mb-eco-arrow:nth-of-type(1){ animation: ecoDrawArrow .55s cubic-bezier(.4,0,.2,1) .20s forwards; }
.mb-eco-in .mb-eco-arrow:nth-of-type(2){ animation: ecoDrawArrow .55s cubic-bezier(.4,0,.2,1) .70s forwards; }
.mb-eco-in .mb-eco-arrow:nth-of-type(3){ animation: ecoDrawArrow .55s cubic-bezier(.4,0,.2,1) 1.20s forwards; }
@keyframes ecoDrawArrow{ to { stroke-dashoffset: 0; } }

/* 3 — PLANT: a slow, calm sway, then the leaf outlines glow green. */
.mb-eco-plant{ transform-origin: 50% 92%; transform-box: fill-box; }
.mb-eco-in .mb-eco-plant{ animation: ecoSway 2.6s cubic-bezier(.36,1,.4,1) .35s 1 both; }
.mb-eco-in .mb-eco-leaf,
.mb-eco-in .mb-eco-vein{ animation: ecoLeafGlow 2.4s ease-in-out .8s 1 both; }
@keyframes ecoSway{
  0%   { transform: rotate(0deg); }
  26%  { transform: rotate(-7deg); }
  56%  { transform: rotate(4.5deg); }
  80%  { transform: rotate(-1.8deg); }
  100% { transform: rotate(0deg); }
}
@keyframes ecoLeafGlow{
  0%, 100% { stroke: currentColor; filter: none; }
  32%, 66% { stroke: #22c55e; filter: drop-shadow(0 0 5px rgba(34,197,94,.85)); }
}

/* ── "Not a drink you sip." — clearer, with the same shimmer used on the
      current roadmap stage (§20). ───────────────────────────────────────── */
.mb-eco-note{
  margin-top: clamp(24px, 3vw, 34px);
  padding: clamp(18px, 2vw, 24px) clamp(20px, 2.2vw, 26px);
  border: 1px solid var(--mist);
  border-left: 3px solid var(--action);
  border-radius: 14px;
  background: var(--cloud);
}
.mb-eco-note-title{
  display: block;
  font-size: clamp(17px, 1.7vw, 20px);
  font-weight: 800;
  letter-spacing: -.01em;
  margin-bottom: 8px;
  /* shimmer — identical technique to the roadmap's current stage */
  --mb-base: #8b93a1;
  --mb-hi: var(--action);
  --mb-spread: 22px;
  color: transparent;
  -webkit-text-fill-color: transparent;
  background-image:
    linear-gradient(90deg, transparent calc(50% - var(--mb-spread)), var(--mb-hi), transparent calc(50% + var(--mb-spread))),
    linear-gradient(var(--mb-base), var(--mb-base));
  background-repeat: no-repeat;
  background-size: 250% 100%, 100% 100%;
  -webkit-background-clip: text;
          background-clip: text;
  animation: mbTextShimmer 3s linear infinite;
}
body.dark .mb-eco-note-title{ --mb-base: #8b93a1; --mb-hi: #cdd8ff; }
.mb-eco-note-body{
  display: block;
  color: var(--slate);
  font-size: 14.5px;
  line-height: 1.65;
  max-width: 78ch;
}


/* ═════════════════════════════════════════════════════════════════════════
   §30  THEME BULB — identical animation on Windows and macOS.

   styles.css (lines 542 & 881) disables the bulb's "zap" animation under
   `prefers-reduced-motion`. Windows very often reports that preference
   (system animations off by default in many setups) while macOS usually does
   not — which is exactly why the toggle felt lively on Mac and dead on
   Windows. We restore the full animation for everyone, plus the glow ring
   and the rays/glass transitions, so the button behaves the same everywhere.
   It is a short one-shot animation on a deliberate click, not ambient motion. */
.bulb.zap svg{
  animation: bulbZap .55s cubic-bezier(.34, 1.56, .64, 1) !important;
}
.bulb.zap{
  box-shadow: 0 0 0 9px rgba(255, 200, 61, .22), 0 0 26px rgba(255, 200, 61, .35) !important;
}
.bulb{
  transition: color .3s ease, border-color .3s ease, background-color .3s ease,
              box-shadow .45s ease, transform .28s cubic-bezier(.22, .61, .36, 1) !important;
}
.bulb svg{ transition: transform .3s ease !important; }
.bulb .rays{ transition: opacity .35s ease !important; }
.bulb .glass{ transition: fill .35s ease !important; }


/* ═════════════════════════════════════════════════════════════════════════
   §28  FAQ PAGE — smaller gap between the intro copy and the category chips.
   site-patch.js §16b tags the filter section `mb-faq-tight`; here we cut its
   top padding so the chips sit closer under the lead paragraph. */
.mb-faq-tight{ padding-top: 0 !important; }
.mb-faq-tight > .filters{ margin-top: -34px; }
@media (max-width: 767px){
  .mb-faq-tight > .filters{ margin-top: -16px; }
}


/* ═════════════════════════════════════════════════════════════════════════
   §26  IMPORTANT-INFO WATERMARK.

   For the customer-facing "please note" sections — Product → "Take it. Keep
   moving." and Science / Explorer → "What this tool cannot know." —
   site-patch.js §17 adds `mb-warnbg`. We paint a large exclamation mark (just
   the "!", no circle around it) in a calm, muted red with a soft glow — the
   `mask` makes the element the exact shape of the glyph, and `drop-shadow`
   then makes THAT shape glow (a subtle, not-bright red shine). It sits behind
   the content (pointer-events off) so it's visible but covers nothing.
   Mask works across all modern browsers via the -webkit- prefix (Safari/iOS)
   and the standard property (Chrome/Firefox/Edge). */
.mb-warnbg{ position: relative; overflow: hidden; }
.mb-warnbg > *{ position: relative; z-index: 1; }   /* keep all real content above the watermark */
.mb-warnbg::after{
  content: "";
  position: absolute; z-index: 0; pointer-events: none;
  right: 6%; top: 50%; transform: translateY(-50%);
  width: min(120px, 14vw); height: min(240px, 30vw);
  background: #c15b52;                        /* calm, muted red — not bright */
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 100'%3E%3Crect x='15' y='6' width='10' height='58' rx='5'/%3E%3Ccircle cx='20' cy='86' r='7'/%3E%3C/svg%3E") no-repeat center / contain;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 100'%3E%3Crect x='15' y='6' width='10' height='58' rx='5'/%3E%3Ccircle cx='20' cy='86' r='7'/%3E%3C/svg%3E") no-repeat center / contain;
  opacity: .18;
  filter: drop-shadow(0 0 18px rgba(193, 91, 82, .55));   /* the calm red shine */
}
body.dark .mb-warnbg::after{
  background: #d06a60;
  opacity: .24;
  filter: drop-shadow(0 0 20px rgba(208, 106, 96, .6));
}
@media (max-width: 768px){
  .mb-warnbg::after{ width: 74px; height: 150px; right: -6px; opacity: .14; }
}


/* ═════════════════════════════════════════════════════════════════════════
   §27  NAVIGATION & BUTTON INTERACTION FIXES.

   (a) INSTANT route scroll — styles.css sets `html{scroll-behavior:smooth}`,
   which made every page-change visibly animate-scroll from the bottom to the
   top (looked like lag). Force it to jump instantly, so a new subpage simply
   appears at the top. Intentional smooth scrolls elsewhere use the JS
   `behavior:'smooth'` option, which still overrides this. */
html{ scroll-behavior: auto !important; }

/* (b) BUTTON HOVER — replace the "magnetic" cursor-follow (app.js moves the
   button toward the pointer, which feels wrong on macOS) with a clean, uniform
   hover: a gentle spring lift + slight scale, identical on Windows and macOS.
   The !important beats app.js's inline transform, so the button no longer
   trails the mouse — it just reacts once when hovered. */
@media (hover: hover) and (pointer: fine){
  .btn--primary, .btn--secondary{
    transition: transform .32s cubic-bezier(.34, 1.56, .64, 1),
                box-shadow .3s ease, background .2s ease, color .2s ease, border-color .2s ease !important;
  }
  .btn--primary:hover, .btn--secondary:hover{
    transform: translateY(-3px) scale(1.035) !important;   /* spring easing = subtle bounce */
  }
  .btn--primary:active, .btn--secondary:active{
    transform: translateY(-1px) scale(.99) !important;
    transition: transform .12s ease !important;
  }
}


/* ═════════════════════════════════════════════════════════════════════════
   §31  FIVE PORTRAITS IN THE FOUNDER ARC.

   The arc was drawn for three faces; site-patch.js §11 now appends two more
   (Katarzyna, Kacper). Five circles at the original size run past the edge
   of a phone screen, so below 640px they scale with the viewport instead of
   sitting at a fixed width. The row never wraps — it stays one arc. */
.founder-arc{ max-width: 100%; }
.founder-arc .portrait{ flex: 0 0 auto; min-width: 0; }

@media (max-width: 640px){
  .founder-arc .portrait{
    width:  clamp(52px, 15.5vw, 92px) !important;
    height: clamp(52px, 15.5vw, 92px) !important;
  }
}

/* ═════════════════════════════════════════════════════════════════════════
   §32  FOUNDERS HERO — STACKED AND CENTRED.

   app.js lays this hero out in two columns: copy on the left, portraits on
   the right. With five faces the arc needs the full width, and the headline
   was being squeezed into eight cramped lines. Restack it:

        TITLE  →  PORTRAIT ARC  →  BODY COPY  →  BUTTONS

   Same technique as the mobile reorder in §11: `display: contents` dissolves
   app.js's text-column wrapper so its h1 / lead / btn-row become direct flex
   items of `.hero`, then `order` places each one. `:has()` scopes this to
   the one hero that actually holds a `.founder-arc`, so no other page moves.
   site-patch.js §11 puts Bartłomiej in the middle of the arc, so the row
   reads two faces · the featured face · two faces. */
@media (min-width: 1024px){
  .hero:has(> .founder-arc){
    display: flex !important;
    flex-direction: column !important;
    align-items: center !important;
    text-align: center;
  }
  /* text-column wrapper → transparent; its children join the flex layout */
  .hero:has(> .founder-arc) > div:not(.founder-arc){ display: contents; }

  .hero:has(> .founder-arc) .eyebrow{ order: 1; }
  .hero:has(> .founder-arc) h1,
  .hero:has(> .founder-arc) h2{
    order: 2;
    max-width: 20ch;
    margin-inline: auto;
    text-wrap: balance;
  }
  .hero:has(> .founder-arc) > .founder-arc{
    order: 3;
    margin: clamp(28px, 3.5vw, 46px) auto clamp(24px, 3vw, 38px);
    justify-content: center;
  }
  .hero:has(> .founder-arc) .lead,
  .hero:has(> .founder-arc) p:not(.crumbs){
    order: 4;
    max-width: 62ch;
    margin-inline: auto;
  }
  .hero:has(> .founder-arc) .btn-row{
    order: 5;
    justify-content: center;
    margin-inline: auto;
  }
}
/* The arc's own geometry (overlap, raised centre face) is left exactly as
   app.js draws it — only its position on the page changes. */


/* ═════════════════════════════════════════════════════════════════════════
   §41  TEXT RENDERING — THE REAL CAUSE OF THE "CROOKED" COPY.  SITE-WIDE.

   The paragraph was never actually crooked. It was being rasterised
   differently from the rest of the page, and softened, uneven glyph edges
   read to the eye as lines that do not sit straight.

   WHY. A browser has two ways to draw text:

     • subpixel antialiasing (ClearType on Windows) — uses the red, green and
       blue stripes inside each physical pixel, giving crisp, evenly weighted
       letterforms;
     • grayscale antialiasing — used whenever the text sits on its own GPU
       compositor layer, because the layer is drawn in isolation and the
       browser cannot know what colour is behind it.

   Anything that promotes an element to its own layer silently switches every
   piece of text inside it to the second method: `transform`, `will-change`,
   `backface-visibility`, `opacity` below 1. Two rules in this file were doing
   exactly that and never releasing it:

     1. `#main { backface-visibility: hidden }` — added with the route
        entrance in §33, which put the ENTIRE page permanently on a layer.
        Removed above.
     2. `transform: translate3d(0,0,0)` left on every reveal scaffold in its
        finished state. It moves nothing, but a zero transform still creates a
        layer, so every revealed heading and paragraph on the site kept
        grayscale text forever after animating in.

   The fix for the second is to end on `transform: none` instead. `none` is
   the only value that actually releases the layer — the element goes back to
   normal document rendering and Windows returns to ClearType. The animation
   is unaffected: it still interpolates from the offset to nothing.

   `translate3d(0,0,0)` remains correct on the elements that genuinely need a
   stable layer — the ticker, the progress bar, the logo sweep — because those
   animate continuously and contain no body copy. */

.sr.sr-visible,
.sr-left.sr-visible,
.sr-right.sr-visible,
.reveal-left.reveal-active,
.reveal-right.reveal-active,
.reveal-up.reveal-active,
.hl-word.vis,
.clip-reveal.in-view .clip-reveal-inner{
  transform: none !important;
  will-change: auto !important;
}

/* ── THE HEADING THAT STILL LOOKED CROOKED ────────────────────────────────
   Section headings are wrapped in `.clip-reveal`, a box with `overflow:
   hidden` that hides the heading below itself so it can rise into view. To
   stop descenders being cut off, §5 of this file gives that box
   `padding-bottom: .22em` cancelled by `margin-bottom: -.22em`.

   Those are em values. At a `clamp()` heading size they resolve to something
   like 11.3px, so the clipping box ends on a FRACTIONAL pixel. A box with
   `overflow: hidden` clips at the device pixel, and where the boundary falls
   mid-pixel the browser shaves the bottom row of the glyphs — not equally,
   because each letter meets that boundary at a slightly different subpixel
   offset. The result is a baseline that looks very slightly wavy. It only
   showed on the biggest headings, because that is where the em value lands
   furthest from a whole pixel.

   Once the rise-up has finished, the clipping box has no job left. Releasing
   it means nothing can trim the glyphs. The padding and margin cancelled each
   other out, so zeroing both changes no spacing. */
.clip-reveal.in-view{
  overflow: visible !important;
  padding-bottom: 0 !important;
  margin-bottom: 0 !important;
}
.clip-reveal.in-view .clip-reveal-inner{
  padding-bottom: 0 !important;
  overflow: visible !important;
}

/* §8 sets this one with `!important` inside a desktop media query, so it has
   to be restated at the same weight. `none` and `translate3d(0,0,0)` look
   identical; only `none` releases the layer. */
.sr-edge.in-view{
  transform: none !important;
  will-change: auto !important;
}

/* Carve-outs restated afterwards so the intentional resting transforms
   survive. Both are hover states, and neither holds body copy that needs to
   stay crisp while at rest. */
@media (hover: hover) and (pointer: fine) and (min-width: 1024px){
  .card.sr-edge.in-view:hover{ transform: translate3d(0, -4px, 0) !important; }
  .fact.sr-edge.in-view:hover{ transform: translate3d(0, -3px, 0) !important; }
}


/* ── THE macOS HALF OF THE SAME PROBLEM ───────────────────────────────────
   Layer promotion (above) was the Windows cause. On a Mac three separate
   declarations at the top of this file were fighting the system renderer:

     -webkit-font-smoothing: antialiased
         Turns OFF macOS subpixel rendering and draws every glyph in
         grayscale. Strokes come out roughly a third of a pixel thinner and
         stems land between pixels, so lines of text look faint and slightly
         wavy. It was added to stop a shimmer during scroll parallax — an
         effect this file later switched off entirely, so it was buying
         nothing and costing legibility.

     -moz-osx-font-smoothing: grayscale
         The same thing again for Firefox on macOS.

     text-rendering: optimizeLegibility
         Forces kerning and optional ligatures on. In a variable-weight face
         like Manrope this changes the space between individual letter pairs,
         which is precisely what makes a line look like it wanders instead of
         running straight. It is also a long-standing source of layout and
         performance bugs and is not recommended for body copy.

   All three are now at their defaults, so macOS draws text the way the OS
   intends: subpixel-rendered, full weight, evenly spaced. */
html, body,
h1, h2, h3, h4, h5, h6,
p, li, a, span, label, button, input, textarea, select,
.lead, .muted, .fine, .note, .crumbs, .eyebrow{
  -webkit-font-smoothing: auto;
  -moz-osx-font-smoothing: auto;
  text-rendering: auto;
}


/* ═════════════════════════════════════════════════════════════════════════
   §37  "WHAT THIS TOOL CANNOT KNOW" — EVEN THE RAGGED EDGE.

   The paragraph is a `.lead`, and styles.css caps that at `max-width: 42ch`.
   42 characters is a comfortable measure for a one-line strapline, but this
   is a six-line safety notice: at that width the line lengths swing wildly
   and the right-hand edge zig-zags, which is what reads as the text being
   crooked rather than set straight.

   Widened to 68ch — still inside the 45-75 character range that typography
   research treats as comfortable for continuous reading, but long enough
   that the rag settles down. `text-wrap: pretty` then asks the browser to
   balance the last few lines and avoid leaving a single word stranded, as
   "professional." currently is. */
.mb-warnbg .lead{
  max-width: 68ch;
  text-wrap: pretty;
}
/* On a phone the paragraph is full width and the exclamation watermark sits
   at the right edge, so the wider measure would run text across it. A little
   right padding keeps the two apart. */
@media (max-width: 768px){
  .mb-warnbg .lead{ padding-right: 52px; }
}


/* ═════════════════════════════════════════════════════════════════════════
   §38  EXPLORER PAGE — TIGHTEN THE GAP ABOVE THE SIMULATOR.

   The intro sits in a `.section.page-hero` (bottom padding up to 116px) and
   the tool in a `.section--tight` (top padding up to 72px). Stacked, that is
   nearly 190px of empty space between the sentence explaining the tool and
   the tool itself. `:has()` scopes this to the one section that actually
   contains the simulator, so no other page moves. */
#main section.page-hero:has(+ section .sim-body){
  padding-bottom: clamp(14px, 2vw, 26px);
}
#main section:has(> .sim-body),
#main section.section--tight:has(.sim-body){
  padding-top: clamp(6px, 1.4vw, 18px);
}


/* ═════════════════════════════════════════════════════════════════════════
   §39  DARK MODE — SEPARATE THE FOOTER FROM THE PAGE.

   The footer is #0A0A0A and the dark page background is #0B0B0C. One value
   apart, so the two surfaces merge into a single black field and the footer
   reads as if it were floating with no boundary.

   Rather than a heavy drop shadow — which barely registers black-on-black —
   the separation is made the way print does it: the footer sits a shade
   DEEPER than the page, with a hairline catching the light along the join
   and a soft gradient easing into it. Calm, and visible without glowing. */
body.dark .ft{
  background: #060607;
  border-top: 1px solid rgba(255, 255, 255, .085);
  box-shadow: 0 -20px 44px rgba(0, 0, 0, .55);
  position: relative;
}
body.dark .ft::before{
  content: "";
  position: absolute;
  left: 0; right: 0; top: 0;
  height: 90px;
  pointer-events: none;
  background: linear-gradient(180deg, rgba(255, 255, 255, .035), rgba(255, 255, 255, 0));
}
body.dark .ft > *{ position: relative; z-index: 1; }


/* ═════════════════════════════════════════════════════════════════════════
   §40  MODAL OPEN — THE PAGE BEHIND MUST NOT SCROLL.

   `overflow: hidden` on <body> is the usual answer and it is not enough:

     • on iOS Safari the page keeps scrolling anyway — the property is simply
       ignored for touch scrolling on the body;
     • in some engines the scrolling element is <html>, not <body>, so the
       rule lands on the wrong box.

   The reliable method is to take the page out of the scroll flow entirely
   with `position: fixed`, then put the scroll position back by hand when the
   dialog closes. promo-popup.js writes the saved offset into `top`, so the
   page does not visibly jump to the top while the modal is open.

   `--mb-sbw` is the scrollbar width, measured in JS and added back as padding
   so the layout does not shift sideways when the bar disappears. */
html:has(> body.mb-modal-open){ overflow: hidden; }

body.mb-modal-open{
  position: fixed;
  left: 0;
  right: 0;
  width: 100%;
  overflow: hidden !important;
  overscroll-behavior: none;
  padding-right: var(--mb-sbw, 0px);
}


/* ═════════════════════════════════════════════════════════════════════════
   §35  LOGO SHINE — SMOOTH EVERYWHERE, INCLUDING WINDOWS.

   THE REAL CAUSE OF THE STUTTER (styles.css line 851):

       @keyframes logoSweep{ 0%{left:-90%} 55%,100%{left:155%} }

   It animates `left`. That is a LAYOUT property: the browser has to redo
   layout and repaint the masked element on every single frame, 60 times a
   second, forever. On a Mac with a discrete GPU that is absorbed without
   complaint; on a typical Windows laptop with integrated graphics it is
   exactly the kind of continuous layout thrash that reads as stutter.

   Rewritten below as a `transform` animation, which the compositor runs on
   the GPU with no layout and no paint. Same movement, same 4.2s timing, same
   easing — only the mechanism changes.

   Two keyframe sets rather than one, because the travel distance has to be
   expressed as a percentage of each band's OWN width, and the two bands are
   different widths (55% and 28% of the logo):
       ::after   245% of the logo / 55% own width = 446%
       ::before  245% of the logo / 28% own width = 875%
   The skew is carried through both, otherwise the bands would snap upright.

   This section also restores the sweep under prefers-reduced-motion, which
   styles.css line 857 hides outright and §17.9 below stops. Windows reports
   that preference far more often than macOS, which is why the mark looked
   alive on a Mac and dead on a PC. A soft highlight drifting across a 37px
   logo is not vestibular motion. */

@-webkit-keyframes mbLogoSweepA{
  0%       { -webkit-transform: translate3d(0,0,0)    skewX(-18deg); }
  55%,100% { -webkit-transform: translate3d(446%,0,0) skewX(-18deg); }
}
@keyframes mbLogoSweepA{
  0%       { transform: translate3d(0,0,0)    skewX(-18deg); }
  55%,100% { transform: translate3d(446%,0,0) skewX(-18deg); }
}
@-webkit-keyframes mbLogoSweepB{
  0%       { -webkit-transform: translate3d(0,0,0)    skewX(-18deg); }
  55%,100% { -webkit-transform: translate3d(875%,0,0) skewX(-18deg); }
}
@keyframes mbLogoSweepB{
  0%       { transform: translate3d(0,0,0)    skewX(-18deg); }
  55%,100% { transform: translate3d(875%,0,0) skewX(-18deg); }
}

.logo-mask::after,
.logo-mask::before{
  display: block !important;
  will-change: transform;
  -webkit-backface-visibility: hidden;
          backface-visibility: hidden;
}
.logo-mask::after{
  -webkit-animation: mbLogoSweepA 4.2s cubic-bezier(.38, 0, .5, 1) infinite !important;
          animation: mbLogoSweepA 4.2s cubic-bezier(.38, 0, .5, 1) infinite !important;
}
.logo-mask::before{
  -webkit-animation: mbLogoSweepB 4.2s cubic-bezier(.38, 0, .5, 1) .8s infinite !important;
          animation: mbLogoSweepB 4.2s cubic-bezier(.38, 0, .5, 1) .8s infinite !important;
}

/* Same treatment under reduced-motion — repeated inside the query so it
   beats both styles.css's `display:none` and §17.9's `animation:none`. */
@media (prefers-reduced-motion: reduce){
  .logo-mask::after,
  .logo-mask::before{ display: block !important; }
  .logo-mask::after{
    -webkit-animation: mbLogoSweepA 4.2s cubic-bezier(.38, 0, .5, 1) infinite !important;
            animation: mbLogoSweepA 4.2s cubic-bezier(.38, 0, .5, 1) infinite !important;
  }
  .logo-mask::before{
    -webkit-animation: mbLogoSweepB 4.2s cubic-bezier(.38, 0, .5, 1) .8s infinite !important;
            animation: mbLogoSweepB 4.2s cubic-bezier(.38, 0, .5, 1) .8s infinite !important;
  }
}


/* ═════════════════════════════════════════════════════════════════════════
   §36  ANNOUNCEMENT BAR — REMOVED ON EVERY DEVICE.

   app.js renders it into #announce-root on every page load. Hiding both the
   bar and its container means no empty strip is left behind and the header
   sits flush at the top of the page. */
#announce-root,
.announce{ display: none !important; }


/* ═════════════════════════════════════════════════════════════════════════
   §34  PHONE — THE HERO POUCH DOES NOT ANIMATE.

   On the home hero, app.js gives the image column three separate pieces of
   motion, and on a phone all three land in the first second of the visit:

     1. `.sr-right` + a 120ms timer adding `.sr-visible`  (slide in from right)
     2. `.hero-cin-right` + `.cin-active`                 (cinematic entrance)
     3. a float loop on `.hero-img-wrap`                   (gentle bobbing)

   Stacked on a phone that reads as the product jittering the moment the page
   opens. All three are neutralised below, so the pouch is simply THERE, fully
   visible, from the first paint. Desktop keeps every one of them.

   `opacity` and `transform` are pinned rather than the classes removed, so
   this stays purely additive — app.js can add whatever classes it likes and
   the result on a phone is unchanged. */
@media (max-width: 767px){
  #hero-img-col,
  #hero-img-col.sr,
  #hero-img-col.sr-right,
  #hero-img-col.sr-right:not(.sr-visible),
  #hero-img-col.hero-cin-right,
  #hero-img-col.hero-cin-right:not(.cin-active),
  .hero-home .hero-img,
  .hero-home .hero-img-wrap,
  .hero-home .mb-hero-img{
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
    animation: none !important;
    will-change: auto !important;
  }
}


/* ═════════════════════════════════════════════════════════════════════════
   §33  ROUTE ENTRANCE — the fix for "the title just flashes on Windows".

   Symptom: opening a subpage flashed the headline and the product images
   instead of animating them. Two independent causes:

   1. FLASH = flash of unstyled content. app.js writes the new page's markup
      into #main; the browser could paint it fully visible before the reveal
      scaffolding (opacity:0 + transform) was attached. Visible → hidden →
      visible reads as a flash. site-patch.js §22 now hides #main
      synchronously the instant navigation starts, so that first visible
      paint never happens.

   2. NO MOTION = a transition with no start state. Every reveal on this site
      is a CSS *transition* switched on by an IntersectionObserver. For
      content already in the viewport when the route renders, the observer
      fires on the very next frame: the hidden state and the visible state
      land in the same style flush, there is nothing for the browser to
      interpolate, and the element pops. macOS got away with it because its
      observer timing happened to straddle a frame; Windows did not.

   The cure for (2) is to stop using a transition. A keyframe *animation*
   always plays from its first frame the moment the class lands — no start
   state to miss, no observer, no race. It runs on #main itself, which
   carries none of the `transform: !important` overrides that would
   otherwise cancel it, and it is deliberately NOT disabled under
   prefers-reduced-motion: Windows commonly reports that preference, and
   this is a single short, non-looping, sub-20px movement — the same
   judgement already applied to the roadmap bars and the theme bulb.
   ═════════════════════════════════════════════════════════════════════════ */

/* Hidden while the new route is being written and patched.
   The animation is a SAFETY NET, not the effect: if JavaScript ever failed
   to clear the attribute, the page would reveal itself anyway after 0.9s
   instead of staying blank. In normal operation §22 removes the attribute
   after ~60-150ms and `.mb-page-in` below takes over long before this
   keyframe reaches its fade. */
#main[data-mb-enter]{
  animation: mbHoldHidden .9s linear both;
}
@keyframes mbHoldHidden{
  0%, 70% { opacity: 0; }
  100%    { opacity: 1; }
}

/* The actual entrance — one calm fade + rise for the whole subpage. */
#main.mb-page-in{
  animation: mbPageIn .78s var(--ease-premium) both;
}
@keyframes mbPageIn{
  from{ opacity: 0; transform: translate3d(0, 16px, 0); }
  to  { opacity: 1; transform: translate3d(0, 0, 0); }
}

/* Genuine reduced-motion users still get the fade, just without the rise. */
@media (prefers-reduced-motion: reduce){
  @keyframes mbPageIn{
    from{ opacity: 0; transform: none; }
    to  { opacity: 1; transform: none; }
  }
}

/* NOTE — a permanent `backface-visibility: hidden` used to sit on #main here
   to keep a stable compositor layer. It has been removed: see §41. Holding
   the whole page on a GPU layer switches text from subpixel antialiasing to
   grayscale, which is what made body copy look soft and slightly crooked. */
