/* ===== assets/css/components/hero.css ===== */
/* =============================================================================
   hero.css — Varedat.io · 02 Hero (dark cinematic globe, v3)
   Loaded after components.css. WP mapping: assets/css/components/hero.css

   TUNING: every composition/colour knob is a custom property on .hero (below)
   and can be overridden per breakpoint. The canvas renderer reads the --globe-*
   and --arrow-* colours; it never contains hex values.

   NOTE ON DIRECTION: the client's reference composition is physical —
   copy LEFT, globe RIGHT — so this section lays out with `direction: ltr`
   at the grid level and restores `direction: rtl` inside the copy block.
   ============================================================================= */

.hero {
  /* ---- environment ---- */
  --hero-bg: #050505;
  --hero-dust: rgba(122, 74, 34, 0.22);        /* warm haze upper-left, like the reference */
  --hero-text: #ffffff;
  --hero-text-muted: #b6bcc4;

  /* ---- globe placement ----
     --globe-size is the DISC diameter. Per the client's reference (2026-08-25) the Earth is
     MASSIVE, anchored to the right and intentionally cropped (right + bottom, top rim just
     inside) — the "complete sphere inside 100vh" rule is superseded. Desktop sizing derives
     from the viewport HEIGHT so the sphere always dominates: min(112vh, 84vw).
     The canvas box is larger by --globe-overflow (total fraction) so halos can spill. */
  --globe-size: 132vw;
  --globe-overflow: 0.6;                        /* must equal `overflow` in globe.config.js (room for high arcs + outer haze) */
  --globe-box: calc(var(--globe-size) * (1 + var(--globe-overflow)));
  --globe-margin: calc(var(--globe-size) * var(--globe-overflow) / 2);
  --globe-right: -23vw;  /* mobile reference: centre at roughly 57vw, so the right side stays cropped.
                                       Fraction of the diameter beyond the RIGHT VIEWPORT edge; desktop: how far the disc extends beyond the right edge (UC reference ≈ −0.6; kept milder so Tehran + routes stay in view) */
  /* Stable viewport: mobile browser chrome must not move the globe while
     scrolling. The warm rim remains just under the header. */
  --globe-top: 12svh;

  /* ---- globe material ---- */
  --globe-core: #020306;
  /* atmosphere palette sampled from the reference screenshot; one continuous rim: top → sides → bottom */
  --atmo-cream: #ffd6b8;      /* brightest top rim */
  --atmo-salmon: #ffa478;     /* upper rim body */
  --atmo-orange: #ff7a3a;     /* saturated orange */
  --atmo-burnt: #7a2a0e;      /* dark burnt orange */
  --atmo-ember: #1a0c08;      /* near-black warm (side transition) */
  --atmo-violet: #25185e;     /* subtle violet/navy transition */
  --atmo-navy: #0b1f8a;       /* deep navy */
  --atmo-royal: #1f4df5;      /* saturated royal blue */
  --atmo-electric: #4d8cff;   /* bright electric blue at the bottom rim — blue, not cyan */
  --globe-orange: #ff5a14;
  --globe-amber: #ff9a45;
  --globe-yellow: #ffd67a;
  --globe-blue-deep: #071a6a;
  --globe-blue: #1b49f0;
  --globe-blue-light: #5c9dff;
  --globe-dot: #ffffff;
  --globe-dot-warm: #ffc9a2;
  --globe-dot-cool: #b3cdff;

  /* ---- network ---- */
  /* ONE arrow palette for every route (v11): transparent → burnt → orange → amber → gold → yellow head */
  --arrow-burnt: #b83600;     /* deep burnt tail, fading to transparent (v18: darker so the body reads as a progression) */
  --arrow-orange: #ff5f00;    /* strong saturated orange body */
  --arrow-amber: #ffa018;     /* amber — clearly warmer than the body */
  --arrow-gold: #ffc83c;      /* gold toward the front */
  --arrow-yellow: #fff0a6;    /* HEAD — warm bright yellow, the brightest point */
  --globe-marker: #ff7a2a;
  --globe-label-bg: #141416;
  --globe-label-fg: #ffffff;

  /* section-scoped token overrides so shared components render on dark */
  --color-text: var(--hero-text);
  --color-text-muted: var(--hero-text-muted);
  --color-line: rgba(255, 255, 255, 0.16);
  --color-focus: #ffffff;
  --color-accent: var(--globe-amber);

  position: relative;
  isolation: isolate;
  overflow: hidden;                             /* the disc is meant to leave the viewport */
  padding-block: var(--space-7) 0;
  background: var(--hero-bg);
  color: var(--hero-text);
}

/* Layer 0–1: black field + restrained warm dust (two radial gradients + fine noise, no big blur discs) */
.hero::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(70% 55% at 12% 18%, var(--hero-dust), transparent 70%),
    radial-gradient(40% 35% at 30% 60%, rgba(60, 40, 24, 0.14), transparent 70%);
}

.hero__noise {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: 0.045;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/></filter><rect width='160' height='160' filter='url(%23n)'/></svg>");
  background-size: 160px 160px;
  mix-blend-mode: screen;
}

.hero__inner {
  /* STATIC, for the same reason it is static at 64em: .hero__globe is absolute,
     so a positioned .hero__inner becomes its containing block and the disc
     resolves against the padded container instead of the viewport. Measured at
     390x844 the canvas landed at y=323 when the CSS asked for y=-38. z-index
     still applies at 64em because it is a grid item there. */
  position: static;
  z-index: 2;
  display: grid;
  /* v42 — THE MOBILE HERO WAS RUNNING OFF THE SCREEN.
     Measured at 390x844: .hero__content sat at left 20 / right 596 in a 390 px window, so a
     third of the H1, the lead and the source chips were simply outside it.
     Cause: this is a grid, and with no template the single implicit column is `auto` — which
     means max-content. On mobile .hero__globe is in flow at --globe-box (150vw x 1.6 = 936 px
     by design, because the disc is meant to bleed past the edges), so the column sized itself
     to the globe and the container went with it. minmax(0, 1fr) pins the column to the
     container and lets the globe overflow it, which is what it was always supposed to do.
     The 12-column rule at 64em overrides this, so desktop is untouched. */
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-6);
  direction: ltr;                               /* physical layout: copy left, globe right */
}

/* ---------- Copy (layer 6) ---------- */
.hero__content {
  position: relative;
  z-index: 6;
  direction: rtl;
  display: grid;
  gap: var(--space-5);
  justify-items: start;
  max-width: 36rem;
}

.hero__title { max-width: 12ch; color: var(--hero-text); }
.hero__title .line { display: block; }
/* v50: BLUE (client). The gradient stays — it is what gives the line its depth against the
   black — but it is built from the interface accent now instead of the globe's warm palette.
   The two lighter stops carry against #050505; the flat token would not. */
.hero__title .line--accent {
  color: transparent;
  background: linear-gradient(90deg, #a9abff, #6f73f5 55%, #4f52e0);
  -webkit-background-clip: text;
  background-clip: text;
}

.hero .lead { color: var(--hero-text-muted); }

.hero .eyebrow { color: var(--hero-text-muted); }
.hero .eyebrow::before { background: var(--globe-amber); }

.hero__actions { display: flex; flex-wrap: wrap; gap: var(--space-3); margin-block-start: var(--space-2); }

/* white pill primary CTA on dark (as in the reference) */
.hero .btn--primary {
  --btn-bg: #ffffff;
  --btn-fg: #0f1419;
  --btn-border: #ffffff;
}
.hero .btn--primary:hover,
.hero .btn--primary:focus-visible { --btn-bg: var(--globe-yellow); --btn-border: var(--globe-yellow); }
.hero .btn--secondary {
  --btn-bg: transparent;
  --btn-fg: #ffffff;
  --btn-border: rgba(255, 255, 255, 0.34);
}
.hero .btn--secondary:hover,
.hero .btn--secondary:focus-visible { --btn-border: #ffffff; --btn-bg: rgba(255, 255, 255, 0.06); }

/* Sourcing line — textual equivalent of the globe's network layer; boxed like the map labels */
.hero__sources {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--space-3) var(--space-4);
  margin-block-start: var(--space-3);
  font-size: var(--text-small);
  color: var(--hero-text-muted);
}
.hero__sources-label {
  font-family: var(--font-mono); font-size: var(--text-label); letter-spacing: 0.08em; text-transform: uppercase;
}
.source-chip {
  display: inline-flex; align-items: center; gap: var(--space-2);
  padding: 0.4rem 0.75rem;
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: var(--radius-sm);
  background: var(--globe-label-bg);
  color: var(--globe-label-fg);
}
.source-chip__code {
  font-family: var(--font-mono); font-size: 0.6875rem; font-weight: 500; letter-spacing: 0.1em;
  color: var(--globe-marker); direction: ltr;
}

/* ---------- Globe (layers 2–5) ---------- */
/* Mobile: matched to the client's 494x1108 reference recording, 2026-09-07.
   The disc is about 132vw wide, shifted right, and the copy sits at the
   BOTTOM, over the blue lower limb, which is bright enough to carry white text
   without a scrim over it. That is why the earlier right-anchored version was
   reference's lower blue limb. The right crop is intentionally stronger than
   the left; a symmetrically centred 178vw disc was both too large and misplaced.

   Touch-draggable; vertical-first gestures scroll the page. */
.hero {
  min-block-size: 100vh;
  min-block-size: 100svh;
  display: grid;
  align-items: end;
  padding-block-end: var(--space-7);
}
.hero__globe {
  position: absolute;
  inset-inline-start: calc(var(--globe-right) - var(--globe-margin));
  inset-block-start: calc(var(--globe-top) - var(--globe-margin));
  margin: 0;
  z-index: 1;
  inline-size: var(--globe-box);
  block-size: var(--globe-box);
  pointer-events: auto;
}
@media (max-width: 63.99em) {
  /* Mobile browser chrome changes the visual viewport while scrolling. Never
     let an old GSAP inline transform/opacity or a breakpoint refresh move the
     oversized globe out of its measured position. */
  .hero__globe { transform: none !important; opacity: 1 !important; }
}
/* Copy remains directly on the Hero surface; no separate panel or wash. */
.hero__content::before {
	content: none;
}
/* Below the copy the section resolves to the page background, so the disc is
   never cut by a hard edge. */
.hero::after {
  block-size: clamp(4rem, 18vh, 11rem);
}
/* very short / landscape phones: the copy has to stay dominant, so the disc
   gets smaller rather than swallowing the frame */
@media (max-height: 30em) and (max-width: 63.99em) {
  .hero { --globe-size: min(96svh, 70vw); }
}
.hero__globe canvas {
  display: block; inline-size: 100%; block-size: 100%;
  touch-action: pan-y;                          /* page scroll wins for vertical-first gestures */
  user-select: none; -webkit-user-select: none; -webkit-touch-callout: none;
}
.hero__globe canvas.is-grabbable { cursor: grab; }
.hero__globe canvas.is-grabbing { cursor: grabbing; }

/* section bottom dissolves into black so the cropped disc reads intentional */
.hero::after {
  content: "";
  position: absolute; inset-inline: 0; inset-block-end: 0;
  block-size: clamp(3rem, 12vh, 8rem);
  z-index: 4; pointer-events: none;
  background: linear-gradient(to top, var(--hero-bg) 0%, rgba(5, 5, 5, 0) 100%);
}

/* ---------- Tablet (≥ 640px) ---------- */
@media (min-width: 40em) {
  .hero { --globe-size: 112vw; --globe-right: -13vw; --globe-top: 10svh; }
}

@media (min-width: 48em) {
  .hero { padding-block-start: var(--space-8); }
  .hero__title { max-width: 13ch; }
}

/* ---------- Desktop (≥ 1024px): 100vh black field, copy left, MASSIVE cropped Earth on the right ---------- */
@media (min-width: 64em) {
  .hero {
    /* v28 — MEASURED off the client's own United Carriers screenshot (2876×1310 content box),
       circle fitted to the coloured limb: 196 of 205 boundary points inlier, rms 3.5 px.
         diameter 1300 px = 0.992 × viewport HEIGHT   (0.452 × width at that 2.20 aspect)
         disc top 0.005 H, bottom 0.998 H             → vertically centred, full-bleed
         34.1 % of the diameter beyond the right edge
       So the v19 reading was right and v27's small top-right disc was wrong; this restores the
       height-derived diameter and sets the crop to the measured 0.34 rather than the eyeballed
       0.38. Sizing from height, not width, is what keeps it stable across window aspects. */
    --globe-size: min(100vh, 84vw);
    --globe-size: min(100dvh, 84vw);
    --globe-right: calc(-0.34 * var(--globe-size));
    --globe-top: calc((100dvh - var(--globe-size)) / 2);
    min-block-size: 100vh;
    min-block-size: 100dvh;
    margin-block-start: calc(-1 * var(--header-height));   /* the header floats over the black field */
    padding-block: calc(var(--header-height) + var(--space-7)) var(--space-8);
    display: grid;
    align-items: center;
  }
  .hero__inner {
    /* v28: STATIC on purpose. .hero__globe is position:absolute, so its containing block was
       .hero__inner — which sits inside .container and therefore carries the page gutter. The
       disc was landing one gutter-width (64 px at 1440) short of where --globe-right asked for,
       which is what the v19 −0.38 coefficient was silently compensating for. With this static,
       the globe resolves against .hero (position:relative, line 1124) and --globe-right means
       what it says: a fraction of the diameter beyond the VIEWPORT edge, gutter-independent.
       z-index still applies here because .hero__inner is a grid item. */
    position: static;
    grid-template-columns: repeat(12, minmax(0, 1fr));
    column-gap: var(--space-6);
    align-items: center;
  }
  .hero__content { grid-column: 1 / span 6; }
  .hero__globe {
    position: absolute;
    right: calc(var(--globe-right) - var(--globe-margin));
    top: calc(var(--globe-top) - var(--globe-margin));
    margin: 0;
    inline-size: var(--globe-box);
    block-size: var(--globe-box);
    pointer-events: auto;
  }
}

@media (min-width: 80em) {
  .hero { --globe-size: min(100dvh, 80vw); --globe-right: calc(-0.34 * var(--globe-size)); }
  .hero__content { max-width: 36rem; }
}

@media (min-width: 96em) {
  .hero { --globe-size: min(100dvh, 72vw); --globe-right: calc(-0.34 * var(--globe-size)); }
}

/* ---------- Preview variant: no header (hero-preview.html only; production keeps the header) ---------- */
.preview-no-header .hero { margin-block-start: 0; }
@media (min-width: 64em) {
  .preview-no-header .hero { padding-block-start: var(--space-7); }
  .preview-no-header .hero__globe { inset-block-start: calc(var(--globe-top) - var(--globe-margin)); }
}

/* ---------- Header over the dark hero ---------- */
.site-header.is-dark {
  --color-ink: #ffffff;
  --color-text-muted: #b6bcc4;
  color: #ffffff;
}
.site-header.is-dark .brand,
.site-header.is-dark .nav-toggle { color: #ffffff; }
.site-header.is-dark .nav__link { color: #ffffff; }
.site-header.is-dark .nav__link:hover { background: rgba(255, 255, 255, 0.1); color: #ffffff; }
.site-header.is-dark .site-header__cta {
  --btn-bg: #ffffff; --btn-fg: #0f1419; --btn-border: #ffffff;
}
.site-header.is-dark .site-header__cta:hover,
.site-header.is-dark .site-header__cta:focus-visible { --btn-bg: #ffd67a; --btn-border: #ffd67a; }
.site-header.is-dark.is-scrolled {
  background: rgba(5, 5, 5, 0.72);
  border-block-end-color: rgba(255, 255, 255, 0.08);
}
/* the off-canvas menu keeps its light surface on mobile regardless */
@media (max-width: 63.99em) {
  .site-header.is-dark .nav { --color-ink: #0f1419; color: #0f1419; }
  .site-header.is-dark .nav__link { color: #0f1419; }
  .site-header.is-dark .nav-toggle[aria-expanded="true"] { color: #0f1419; }
}

/* ---------- Reduced motion: nothing to disable here; the renderer handles it ---------- */



/* ---------- v30: the hero copy entrance is CSS, not GSAP ----------
   GSAP's ticker is requestAnimationFrame, and this page stops producing frames
   while the journey's assets are decoded — that stall is what v28 §D.3 recorded
   and what the 4 s failsafe patched over. A patched-over stall is still a stall:
   the copy was arriving late on every cold load. CSS keyframes on opacity and
   transform are composited and do not need the main thread at all, so the hero
   now animates on time no matter what the parser is doing behind it.
   `backwards` and NOT `both`: the pre-delay state is the hidden one, and after
   the run the element returns to its ordinary visible state — so if animations
   are unsupported or disabled the copy is simply there. It can never be left
   invisible by this rule, which is what the old autoAlpha approach could do.
   Numbers are animations.js's: 24 px, 0.8 s, 0.08 s stagger, power3.out. */
@keyframes hero-in {
  from { opacity: 0; transform: translate3d(0, 24px, 0); }
  to   { opacity: 1; transform: none; }
}
/* Held while the entry loader is up, released when it lifts. Without this the
   whole 0.8s runs behind a black overlay and the reader never sees it -- which is
   exactly what happened when the loader was added. `backwards` keeps the copy at
   the animation's first frame while paused, so nothing flashes in at full opacity
   first. */
html.is-loading .hero [data-hero] { animation-play-state: paused; }
.hero [data-hero] {
  animation: hero-in 0.8s cubic-bezier(0.215, 0.61, 0.355, 1) backwards;
}
.hero [data-hero="eyebrow"]              { animation-delay: 0.05s; }
.hero [data-hero="line"]:nth-of-type(1)  { animation-delay: 0.15s; }
.hero [data-hero="line"]:nth-of-type(2)  { animation-delay: 0.23s; }
.hero [data-hero="lead"]                 { animation-delay: 0.35s; }
.hero [data-hero="actions"]              { animation-delay: 0.43s; }
.hero [data-hero="sources"]              { animation-delay: 0.43s; }
@media (prefers-reduced-motion: reduce) {
  .hero [data-hero] { animation: none; }
}
