/* ============================================================
   HERO 3D — "THE RITUAL JAR", scroll journey (look + text choreography)
   Scene/timing logic lives in components/hero3d/hero3d.js. This file owns:
   the pinned stage, the bloom that follows the jar, the poster fallback,
   the scroll cue, the CTA-dive flash, and the "text comes out of the
   cream" reveal.

   Layout: #hero3d is position:sticky (NOT fixed -- see the v4.4 Android
   note in DESIGN-EVOLUTION.md; sticky lives inside the normal flow and
   scrolls with the document like everything else). It is pulled out of the
   flow with a negative bottom margin, so .hero-visual acts as the
   first-screen spacer and the copy scrolls up underneath the pinned jar.
   Follows the app rules: opacity/transform only, calm settle easing, every
   effect off under reduced-motion.
   ============================================================ */

/* first screen = the big closed jar; the copy starts one screen down */
#s0 .hero-visual {
  width: 100%; margin: 0;
  height: calc(100vh - var(--header-h)); height: calc(100svh - var(--header-h));
}

.hero3d {
  container-type: size;                                      /* lets the placeholder size itself from the stage (cqw/cqh) */
  position: sticky; top: var(--header-h); z-index: 3;
  width: calc(100% + 48px); margin: -18px -24px 0;          /* full-bleed inside the 24px body padding; -18px cancels .s0-body's top padding */
  height: calc(100vh - var(--header-h)); height: calc(100svh - var(--header-h));
  margin-bottom: calc(-1 * (100vh - var(--header-h)));
  margin-bottom: calc(-1 * (100svh - var(--header-h)));
  pointer-events: none;                                      /* the page scrolls, taps reach the copy */
}

/* Warm stage light that rides with the jar (LIGHT-01: lit, not a flat void) */
.hero3d-bloom {
  position: absolute; inset: 0; pointer-events: none; opacity: 0;
  /* the glow follows the jar with a transform written by hero3d.js (compositor-only, no repaint) */
  background: radial-gradient(50% 30% at 50% 44%, rgba(221,184,104,.30), rgba(196,154,74,.10) 55%, transparent 74%);
  transition: opacity 1.2s var(--ease-out);
  will-change: transform;
}
.hero3d-bloom { opacity: 1; }
/* the static warm floor light (it used to be the bloom's second gradient) */
.hero3d::before {
  content: ''; position: absolute; inset: 0; pointer-events: none; opacity: 0;
  background: radial-gradient(70% 34% at 50% 100%, rgba(120,86,40,.16), transparent 70%);
  transition: opacity 2.2s var(--ease-out) .2s;
}
.hero3d::before { opacity: 1; }

.hero3d-canvas-slot { position: absolute; inset: 0; opacity: 0; transition: opacity 1s var(--ease-out); }
.hero3d-canvas-slot canvas { display: block; width: 100%; height: 100%; }
.hero3d.h3d-started .hero3d-canvas-slot { opacity: 1; }

/* Scroll cue: an icon only (no copy to translate). Fades out as soon as you scroll. */
.hero3d-cue {
  position: absolute; left: 50%; bottom: 22px; width: 26px; height: 40px; margin-left: -13px;
  border: 1.5px solid rgba(221,184,104,.55); border-radius: 14px;
  opacity: 0; transition: opacity .8s var(--ease-out) 1.6s;
}
.hero3d.h3d-started .hero3d-cue { opacity: 1; transition: opacity .25s linear; animation: h3dCueIn 1s var(--ease-out) 1.6s both; }
.hero3d-cue i {
  position: absolute; left: 50%; top: 8px; width: 4px; height: 8px; margin-left: -2px; border-radius: 2px;
  background: var(--gold-bright); box-shadow: 0 0 8px rgba(221,184,104,.8);
  animation: h3dCueDot 2s ease-in-out infinite;
}
@keyframes h3dCueIn  { from { transform: translateY(8px); } to { transform: none; } }
@keyframes h3dCueDot { 0% { transform: translateY(0); opacity: 1; } 70% { transform: translateY(14px); opacity: 0; } 100% { transform: translateY(0); opacity: 0; } }

/* Static fallback (no WebGL / model failed / context lost): a normal in-flow poster, no pinning */
.hero3d-poster {
  /* Shown INSTANTLY: a small transparent picture of the live scene's first frame (same camera + lights), so a
     refresh never shows an empty hero. It is sized from the stage (the live jar scales with the stage's smaller
     side), so it lines up with the live jar at any screen size, and it dissolves into it once the first live
     frame is drawn. */
  position: absolute; left: 50%; top: 44%; width: min(96%, 470px); height: auto;
  width: min(100cqw, 100cqh); aspect-ratio: 1;
  transform: translate(-50%, -50%); opacity: 1; pointer-events: none;
  transition: opacity 1.1s var(--ease-out);
  animation: h3dPosterIn .7s var(--ease-out) both, h3dPosterBreath 3.2s ease-in-out .7s infinite;   /* a slow gold "breath" while the live jar loads (no spinner) */
}
@keyframes h3dPosterIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes h3dPosterBreath { 0%,100% { filter: brightness(1); } 50% { filter: brightness(1.12); } }
.hero3d.h3d-live .hero3d-poster { opacity: 0; animation: none; }
.hero3d.h3d-fallback { position: absolute; top: 0; left: -24px; right: -24px; margin: 0; width: auto; height: max(56svh, 340px); }
.hero3d.h3d-fallback .hero3d-poster { top: 50%; }
.hero3d.h3d-fallback .hero3d-poster { opacity: 1; animation: none; }
.hero3d.h3d-fallback .hero3d-canvas-slot, .hero3d.h3d-fallback .hero3d-cue { display: none; }
.hero3d.h3d-fallback .hero3d-bloom, .hero3d.h3d-fallback::before { opacity: 1; }
html:has(.hero3d.h3d-fallback) #s0 .hero-visual { height: max(56svh, 340px); }

/* CTA dive: the frame floods with warm cream light as the camera enters the jar */
.hero3d-flash {
  position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(60% 40% at 50% 44%, rgba(255,236,190,.95), rgba(221,184,104,.55) 45%, transparent 78%);
  opacity: calc((var(--h3d-dive, 0) - .5) * 2);
}

/* ---- journey layout tweaks ----
   The jar ends pinned in the space ABOVE the copy, so the copy stack is
   tightened a little (only while the journey is live) to leave that space
   generous on a phone. Content, keys and order are untouched. */
html.h3d-journey #s0 .s0-body { padding-bottom: 14px; }
html.h3d-journey #s0 .specimen-tag { margin-bottom: 12px; }
html.h3d-journey #s0 .hero-headline { font-size: clamp(30px, 8.4vw, 50px); margin-bottom: 14px; }
html.h3d-journey #s0 .hero-headline .line-strike { margin-bottom: 6px; }
html.h3d-journey #s0 .hero-sub { margin-bottom: 14px; line-height: 1.62; }
html.h3d-journey #s0 .hero-trust { padding-top: 12px; padding-bottom: 12px; margin-bottom: 16px; }
html.h3d-journey #s0 .s0-footer { padding-top: 10px; padding-bottom: calc(10px + var(--sab)); }
/* the floating Facebook/Website dock normally rides the old hero image's lower edge;
   on the journey it moves to the top-right, beside the pinned jar, clear of the CTA */
body:has(#s0.active) .global-fab-dock { top: calc(var(--header-h) + 78px); }

/* ---- the welcome copy comes out of the cream ----
   JS (emerge()) adds .h3d-out to each block once the lid is open and the block
   scrolls into view, after setting --h3d-dx / --h3d-dy = the offset from that
   block's own place back up to the jar's mouth. So each block travels the short
   way from the cream INTO its place, staggered. Line-level, so it survives
   EN/AR text swaps. Only applied while the journey is live (.h3d-journey);
   reduced-motion / no-WebGL keep the page fully visible. */
html.h3d-pending #s0 .specimen-tag,
html.h3d-pending #s0 .hero-headline .line-strike,
html.h3d-pending #s0 .hero-headline .line-truth,
html.h3d-pending #s0 .hero-sub,
html.h3d-pending #s0 .hero-trust,
html.h3d-pending #s0 .hero-cta-group,
html.h3d-pending #s0 .s0-footer,
html.h3d-journey #s0 .specimen-tag:not(.h3d-out),
html.h3d-journey #s0 .hero-headline .line-strike:not(.h3d-out),
html.h3d-journey #s0 .hero-headline .line-truth:not(.h3d-out),
html.h3d-journey #s0 .hero-sub:not(.h3d-out),
html.h3d-journey #s0 .hero-trust:not(.h3d-out),
html.h3d-journey #s0 .hero-cta-group:not(.h3d-out),
html.h3d-journey #s0 .s0-footer:not(.h3d-out) {
  opacity: 0; transform: translate(var(--h3d-dx, 0px), var(--h3d-dy, 20px)) scale(.55);
  animation: none;
}
/* (the motion itself is driven per frame by hero3d.js as inline transform/opacity/filter;
   .h3d-out just marks a block as fully in place) */
html.h3d-journey #s0 .h3d-out { animation: none; }
/* the strike-through line draws itself once it has landed */
html.h3d-journey #s0 .hero-headline .line-strike.h3d-out { text-decoration-color: transparent; animation: h3dStrike .7s ease .25s forwards; }
@keyframes h3dStrike { from { text-decoration-color: transparent; } to { text-decoration-color: var(--rose); } }

/* CTA: a slow gold breath once it has arrived, so the eye is led to it */
html.h3d-journey #s0 .hero-cta-group.h3d-out .btn-start { animation: h3dKey 3.4s ease-in-out 1.6s infinite; }
@keyframes h3dKey {
  0%,100% { box-shadow: var(--edge-light-strong), 0 0 0 1px rgba(196,154,74,.30), 0 10px 26px rgba(8,7,5,.42), 0 2px 10px rgba(196,154,74,.20); }
  50%     { box-shadow: var(--edge-light-strong), 0 0 0 1px rgba(221,184,104,.55), 0 10px 26px rgba(8,7,5,.42), 0 0 34px rgba(221,184,104,.42); }
}

@media (prefers-reduced-motion: reduce) {
  html.h3d-pending #s0 .specimen-tag,
  html.h3d-pending #s0 .hero-headline .line-strike,
  html.h3d-pending #s0 .hero-headline .line-truth,
  html.h3d-pending #s0 .hero-sub,
  html.h3d-pending #s0 .hero-trust,
  html.h3d-pending #s0 .hero-cta-group,
  html.h3d-pending #s0 .s0-footer { opacity: 1; transform: none; filter: none; }
  html.h3d-in #s0 * { animation: none !important; }
  .hero3d-bloom, .hero3d-canvas-slot { transition: none; }
  .hero3d-cue { display: none; }
}

/* Weaker tiers: no blur filter on text (a full-line blur is a real cost on low-end GPUs) */
html[data-quality="low"] #s0 .specimen-tag,
html[data-quality="low"] #s0 .hero-headline .line-strike,
html[data-quality="low"] #s0 .hero-headline .line-truth,
html[data-quality="low"] #s0 .hero-sub,
html[data-quality="low"] #s0 .hero-trust,
html[data-quality="low"] #s0 .hero-cta-group,
html[data-quality="low"] #s0 .s0-footer { filter: none !important; }


/* ---- grab-to-rotate: a small hit area that rides over the jar once the intro is done ----
   The stage is pointer-events:none (so the page scrolls and the copy stays tappable); only
   this area takes the pointer. touch-action: pan-y lets a vertical swipe still scroll the page. */
.hero3d-grab {
  position: absolute; left: 0; top: 0; width: 0; height: 0;
  pointer-events: none; touch-action: pan-y; cursor: grab; -webkit-tap-highlight-color: transparent;
}
.hero3d-grab.is-on { pointer-events: auto; }
.hero3d-grab.is-grabbing { cursor: grabbing; }
.hero3d.h3d-fallback .hero3d-grab { display: none; }


/* ---- perf: while the live jar is on screen, the glass header / signature bar don't blur the canvas under them ----
   (backdrop-filter re-blurs its backdrop every frame the canvas changes; the header is already ~96% opaque, so the
   look is unchanged). Only on the hero screen; every other screen keeps its glass. */
html.h3d-journey #app:has(> #s0.active) .app-header,
html.h3d-journey #app:has(> #s0.active) .sudegy-sig {
  -webkit-backdrop-filter: none !important; backdrop-filter: none !important;
}


/* ---- loading line: a thin gold line under the placeholder jar (same look as the splash's progress line) ----
   hero3d.js sets the fill from real progress (libraries -> model bytes -> scene ready). It fades away
   with the placeholder once the live jar is on screen. No copy, so nothing to translate. */
.hero3d-loadbar {
  position: absolute; left: 50%; top: min(calc(44% + min(100cqw, 100cqh) * 0.3), calc(100% - 96px)); width: min(58vw, 190px); height: 3px;
  margin-left: calc(min(58vw, 190px) / -2); border-radius: 999px; overflow: hidden;
  background: var(--hairline-strong); opacity: 0; pointer-events: none;
  animation: h3dLoadIn .5s var(--ease-out) .25s forwards;
  transition: opacity .6s var(--ease-out);
}
.hero3d-loadbar i {
  display: block; height: 100%; width: 100%; border-radius: 999px;
  background: linear-gradient(90deg, var(--gold), var(--gold-bright));
  box-shadow: 0 0 12px rgba(221,184,104,.55);
  transform-origin: left center; transform: scaleX(.05);
  transition: transform .7s var(--ease-glide);
}
html[dir="rtl"] .hero3d-loadbar i { transform-origin: right center; }   /* fills toward the left, like the splash line */
@keyframes h3dLoadIn { to { opacity: 1; } }
.hero3d.h3d-live .hero3d-loadbar { opacity: 0 !important; animation: none; }
.hero3d.h3d-fallback .hero3d-loadbar { display: none; }
@media (prefers-reduced-motion: reduce) { .hero3d-loadbar, .hero3d-loadbar i { animation: none; transition: none; } .hero3d-loadbar { opacity: 1; } }


/* ---- after the intro sequence: the jar is fixed in the viewport, BEHIND the copy ----
   (hero3d.js adds .h3d-static once the jar has landed.) It no longer moves with the scroll, so there is nothing for
   it to trail behind on a fast swipe. Up at the top it is interactive (the grab area is on); scrolled down it dims and
   stays in the background, and the copy scrolls over it. */
.hero3d { transition: opacity .3s linear; }
.hero3d.h3d-static { z-index: -1; }
#s0 .hero-visual { pointer-events: none; }
/* Legibility over the dimmed jar: a soft dark halo on the SOLID-colour copy only. It must NOT go on the gold-gradient headline
   (.line-truth): a text-shadow on background-clip:text paints over the gradient and dims the letters. The jar's own dim (0.58)
   is what keeps the headline readable. */
html.h3d-static #s0 .hero-headline .line-strike,
html.h3d-static #s0 .hero-sub,
html.h3d-static #s0 .hero-trust { text-shadow: 0 2px 14px rgba(21,19,15,.85); }

/* Discover your formula: the copy fades away so the jar behind it is what you dive into */
html.h3d-diving #s0 .specimen-tag,
html.h3d-diving #s0 .hero-headline .line-strike,
html.h3d-diving #s0 .hero-headline .line-truth,
html.h3d-diving #s0 .hero-sub,
html.h3d-diving #s0 .hero-trust,
html.h3d-diving #s0 .hero-cta-group,
html.h3d-diving #s0 .s0-footer { opacity: 0 !important; transition: opacity .45s ease !important; }
