/* ────────────────────────────────────────────────────────────
   Variant C - "Dispatch"
   Treats the page like a field dispatch / archival document.
   Drill image is the hero - type is the caption. Modest GT
   America headline mid-page, supporting deck below. A small
   Plex-Mono dispatch header in the upper-left labels the
   document; a brass dot is the only color note.

   PAGE LOAD CHOREOGRAPHY (2.0s total - see motion brief §5):
     Beat 1  0–800ms     bg fade-in, hero image fade + scale 1.04→1.00
     Beat 2  400–1000ms  dispatch header lines (stagger-base 80ms)
     Beat 3  500–1100ms  nav wordmark + links (stagger-base 80ms) + CTA
     Beat 4  700–1600ms  H1 lines Pattern A weight-reveal (100ms stagger)
     Beat 5  1100–1700ms subhead paragraph
     Beat 6  1400–2000ms primary CTA, manifesto link, telemetry
   Easings: all ease-out-expo per the brief.
   ──────────────────────────────────────────────────────────── */

const HERO_EASE = "cubic-bezier(0.16, 1, 0.3, 1)";

function HeroDispatch() {
  // Ref to the bg video so we can imperatively coax iOS Safari
  // into playing it. See the effect further down for why.
  const heroVideoRef = React.useRef(null);

  // Source selection: desktop / landscape gets the wide file,
  // mobile portrait gets a vertical-cropped file shot for the
  // narrower aspect ratio. Both are now served locally (Cloudflare
  // Pages handles byte-range requests correctly, so the old
  // "iOS Safari needs the Netlify CDN" workaround is no longer
  // strictly required - but we preserve the URL below in case
  // real-device testing surfaces a regression and we need to
  // revert mobile to it).
  //
  // Width-only used to be enough, but adding `orientation: portrait`
  // prevents a phone held in landscape from getting the
  // portrait-cropped file (which would letterbox). It also keeps
  // narrow desktop windows on the wide video, which is correct
  // since their viewport is still landscape-proportioned.
  //
  // Evaluated once at mount. The video does NOT swap if the user
  // rotates mid-session - reloading an MP4 on rotation would feel
  // glitchy, and most users don't expect a background to swap
  // when they turn their phone.
  //
  // Revert path (mobile only): swap the mobile branch below back to
  //   "https://flourishing-churros-4a6773.netlify.app/Axion_BG_Video_Original.mp4"
  // That CDN is the previously-known-reliable iOS path.
  const heroVideoSrc = React.useMemo(() => {
    const isMobilePortrait =
      typeof window !== "undefined" &&
      window.matchMedia &&
      window.matchMedia("(max-width: 768px) and (orientation: portrait)").matches;
    return isMobilePortrait
      ? "assets/imagery/Axion_BG_Video_2_Mobile.mp4"
      : "assets/imagery/Axion_BG_Video_2.mp4";
  }, []);

  // iOS Safari inline-autoplay survival kit.
  //
  // Symptoms: video plays fine in Chrome DevTools' iPhone emulator
  // and on desktop, but a real iPhone shows a blank/black region.
  //
  // Causes, in order of how often they bite:
  //   1. React renders `muted` as a *property* but not always as an
  //      *attribute*. iOS Safari's autoplay policy checks the
  //      attribute on the DOM node before the source loads - no
  //      attribute, no inline autoplay, full stop.
  //   2. Older iOS releases only honor the legacy
  //      `webkit-playsinline` attribute; `playsinline` alone is
  //      ignored.
  //   3. Low Power Mode disables autoplay outright regardless of
  //      attributes. The video will only start after a real user
  //      gesture (tap/scroll).
  //
  // Fixes applied below:
  //   - Force-set the attributes imperatively before calling load().
  //   - Set the JS `muted` property too (belt + suspenders).
  //   - Call play(); if the promise rejects (Low Power Mode), arm
  //     a one-shot listener that retries on the first touch/scroll.
  React.useEffect(() => {
    const v = heroVideoRef.current;
    if (!v) return;
    v.muted = true;
    v.defaultMuted = true;
    v.setAttribute("muted", "");
    v.setAttribute("playsinline", "");
    v.setAttribute("webkit-playsinline", "");
    v.setAttribute("autoplay", "");

    const tryPlay = () => {
      const p = v.play();
      if (p && typeof p.catch === "function") {
        p.catch(() => {
          // Autoplay was blocked (almost always Low Power Mode).
          // Wait for any user gesture and retry once.
          const retry = () => {
            v.play().catch(() => {});
            window.removeEventListener("touchstart", retry);
            window.removeEventListener("touchend", retry);
            window.removeEventListener("click", retry);
            window.removeEventListener("scroll", retry);
          };
          window.addEventListener("touchstart", retry, { once: true, passive: true });
          window.addEventListener("touchend", retry, { once: true, passive: true });
          window.addEventListener("click", retry, { once: true });
          window.addEventListener("scroll", retry, { once: true, passive: true });
        });
      }
    };

    // readyState >= 2 means we have current frame data; otherwise
    // wait for loadedmetadata so play() has something to chew on.
    if (v.readyState >= 2) {
      tryPlay();
    } else {
      v.addEventListener("loadedmetadata", tryPlay, { once: true });
    }
  }, []);

  // Inject the keyframes once. We drive the choreography with CSS
  // animations + per-element animation-delay so the GPU handles it
  // and there's no React re-render storm during the load sequence.
  React.useEffect(() => {
    if (document.getElementById("__hero-choreo-css")) return;
    const s = document.createElement("style");
    s.id = "__hero-choreo-css";
    s.textContent = `
      /* Beat 1 - background fade. Sits on the body backdrop layer
         so it can't fight other transforms. */
      @keyframes heroBgIn {
        from { opacity: 0; }
        to   { opacity: 1; }
      }
      /* Beat 1 - hero image arrives slightly oversized and settles
         to 1.00. The scale-down is the "weight." */
      @keyframes heroImgIn {
        from { opacity: 0; transform: scale(1.04); }
        to   { opacity: 1; transform: scale(1.00); }
      }
      /* Beats 2/3/5 - generic fade + small y-rise. */
      @keyframes heroRiseSm { /* y +8 → 0 */
        from { opacity: 0; transform: translateY(8px); }
        to   { opacity: 1; transform: translateY(0); }
      }
      /* Same shape as heroRiseSm, but ends at 0.7 - for nav links
         which sit at 70% rest opacity per motion brief §10.
         A separate keyframe is necessary because animation-fill-mode:
         both keeps the terminal frame on the element after the
         animation finishes, which would otherwise override the
         .ax-nav-link { opacity: 0.7 } resting rule. */
      @keyframes heroRiseSmNav { /* y +8 → 0, ending at 70% */
        from { opacity: 0;   transform: translateY(8px); }
        to   { opacity: 0.7; transform: translateY(0); }
      }
      @keyframes heroRiseMd { /* y +12 → 0 */
        from { opacity: 0; transform: translateY(12px); }
        to   { opacity: 1; transform: translateY(0); }
      }
      @keyframes heroRiseLg { /* y +16 → 0 - for the subhead */
        from { opacity: 0; transform: translateY(16px); }
        to   { opacity: 1; transform: translateY(0); }
      }
      @keyframes heroFade {
        from { opacity: 0; }
        to   { opacity: 1; }
      }
      /* CTA buttons - fade + slight scale-up (0.96 → 1.00). */
      @keyframes heroCtaIn {
        from { opacity: 0; transform: scale(0.96); }
        to   { opacity: 1; transform: scale(1.00); }
      }
      /* H1 line - Pattern A weight-reveal. Each line lives inside
         a .hero-h1-line-mask (overflow:hidden) and the inner span
         translates from 100% → 0. The mask IS the reveal - no
         clip-path, no opacity. motion-slow (900ms), ease-out-expo. */
      @keyframes heroLineIn {
        from { transform: translateY(100%); }
        to   { transform: translateY(0); }
      }

      /* Hide the choreographed elements before their animation runs.
         animation-fill-mode: both keeps them at the start frame until
         their delay elapses, and at the end frame after. */
      .hero-anim {
        opacity: 0;
        animation-fill-mode: both;
        animation-timing-function: ${HERO_EASE};
        will-change: opacity, transform;
      }

      /* Each H1 line sits in its own overflow-hidden parent so the
         line cleanly rises into frame even when wrapped. Pattern A. */
      .hero-h1-line-mask {
        display: block;
        overflow: hidden;
        /* Padding so descenders (g, p, y) don't clip against the
           mask's bottom edge. Negative margin compensates so layout
           is unchanged. */
        padding: 0.04em 0 0.16em;
        margin: -0.04em 0 -0.16em;
      }
      .hero-h1-line {
        display: block;
        transform: translateY(100%);
        /* motion-slow (900ms) - hero text reveal carries narrative weight. */
        animation: heroLineIn var(--motion-slow) ${HERO_EASE} both;
        will-change: transform;
      }

      /* Honor reduced motion - skip every choreographed transform
         and just snap to the end state. */
      @media (prefers-reduced-motion: reduce) {
        .hero-anim, .hero-h1-line {
          animation: none !important;
          opacity: 1 !important;
          transform: none !important;
        }
        .ax-manifesto-link::before,
        .ax-manifesto-link::after {
          transition: none !important;
        }
      }

      /* Manifesto link underline - split into two stacked pseudo-
         elements so we can choreograph a "wipe out to the right,
         then redraw from the left" on hover. At rest only ::after
         is visible (origin-left, scaleX 1). On hover ::after wipes
         OUT to the right (origin shifts to right, scales to 0),
         then ::before wipes IN from the left (origin-left, scales
         to 1) on a 180ms delay. Together they read as a single
         brass underline being redrawn left-to-right. 320ms each /
         ease-out-quart per the Axion motion system. */
      .ax-manifesto-link {
        position: relative;
      }
      .ax-manifesto-link::before,
      .ax-manifesto-link::after {
        content: "";
        position: absolute;
        left: 0;
        right: 0;
        bottom: 0;
        height: 1px;
        background: #D4A24C;
        pointer-events: none;
      }
      /* The "incoming" underline - sits behind, hidden at rest. */
      .ax-manifesto-link::before {
        transform: scaleX(0);
        transform-origin: left center;
        transition: transform 320ms cubic-bezier(0.25, 1, 0.5, 1) 180ms;
      }
      /* The "resting" underline - visible at rest, wipes out on hover. */
      .ax-manifesto-link::after {
        transform: scaleX(1);
        transform-origin: left center;
        transition: transform 180ms cubic-bezier(0.5, 0, 0.75, 0) 0ms;
      }
      .ax-manifesto-link:hover::after,
      .ax-manifesto-link:focus-visible::after {
        transform: scaleX(0);
        transform-origin: right center;
      }
      .ax-manifesto-link:hover::before,
      .ax-manifesto-link:focus-visible::before {
        transform: scaleX(1);
      }
      /* When the cursor leaves, snap ::after back without
         re-running the wipe-out (we do this by skipping the
         transition for the back-trip). The ::before fades back to
         scaleX(0) from the right so the redrawn underline doesn't
         appear to wipe out from the left side. */
      .ax-manifesto-link:not(:hover):not(:focus-visible)::after {
        transition: transform 0ms;
      }
      .ax-manifesto-link:not(:hover):not(:focus-visible)::before {
        transform-origin: right center;
        transition: transform 180ms cubic-bezier(0.5, 0, 0.75, 0);
      }
    `;
    document.head.appendChild(s);
  }, []);

  // Helper builders for the per-element animation strings.
  // Keeps the JSX below readable.
  const anim = (name, dur, delay) => ({
    animation: `${name} ${dur}ms ${HERO_EASE} ${delay}ms both`
  });

  // Once each entrance animation finishes, peel off the `hero-anim`
  // class so the element's regular CSS cascade resumes ownership of
  // opacity/transform. Without this, animation-fill-mode: both keeps
  // the keyframe's terminal frame applied at "animation origin" -
  // higher than author rules - so e.g. `.ax-nav-link:hover { opacity:
  // 1 }` would lose to the animation's `to { opacity: 0.7 }`.
  React.useEffect(() => {
    const handler = (e) => {
      const el = e.target;
      if (el && el.classList && el.classList.contains("hero-anim")) {
        el.classList.remove("hero-anim");
        // Belt-and-suspenders: zero out the inline animation string
        // too, so re-renders don't re-apply it. Browsers stop the
        // animation on class removal, but the inline `animation`
        // style would still show up if the element re-mounts.
        el.style.animation = "none";
        // We also need to PIN the resting state, otherwise opacity
        // would jump from 0.7 (animation-owned) to whatever the
        // base CSS says (e.g. 0.7 for .ax-nav-link or 1 elsewhere).
        // For nav links the cascade gives us 0.7 already; for others
        // we want full opacity. Since we can't know the intent here,
        // we lean on each component's own CSS to declare its rest
        // state - which they all do.
      }
    };
    document.addEventListener("animationend", handler, true);
    return () => document.removeEventListener("animationend", handler, true);
  }, []);

  // Dispatch header lines - Beat 2.
  const dispatchLines = ["Dispatch No. 01", "Field ops · MMXXVI", "AX-LDT // System brief"];
  // Nav links text - for stagger.
  const navLinkLabels = ["Problem", "Platform", "Impact", "Team"];
  // H1 lines - split for the per-line clip-path reveal.
  const h1Lines = [
  <>Geothermal has a drilling</>,
  <>cost problem. <span style={{ color: "#D4A24C", fontWeight: 700 }}>Axion</span> is</>,
  <>solving it.</>];

  // Telemetry footer lines - Beat 6.
  const telemetry = [
  { text: "Plate I.", color: "#EDEAE2" },
  { text: "AX-LDT-24000 · 24.0 kW", color: "#A8A8A2" },
  { text: "Field-07, capture window 03", color: "#A8A8A2" }];


  return (
    <div style={{
      position: "relative",
      width: "100%",
      height: "100%",
      background: "#15171A",
      color: "#EDEAE2",
      fontFamily: "var(--font-body)",
      overflow: "hidden",
      isolation: "isolate"
    }}>
      {/* ── Beat 1 - Background video + gradients fade in together. */}
      <div
        aria-hidden="true"
        className="hero-anim"
        style={{
          position: "absolute", inset: 0, zIndex: 0, overflow: "hidden",
          ...anim("heroBgIn", 900, 0)
        }}>
        {/* Hero image / video - gets its own scale-settle so it feels
                        like a camera move arriving on the subject. */}
        <div
          className="hero-anim"
          style={{
            position: "absolute", inset: 0,
            ...anim("heroImgIn", 1200, 0), /* motion-deliberate - the hero's first impression */
            transformOrigin: "center"
          }}>
          <video
            ref={heroVideoRef}
            className="hero-bg-video"
            src={heroVideoSrc}
            autoPlay
            muted
            loop
            playsInline
            preload="auto"
            disableRemotePlayback
            style={{
              position: "absolute",
              inset: 0,
              width: "100%",
              height: "100%",
              objectFit: "cover",
              objectPosition: "right center",
              display: "block"
            }} />
        </div>
        {/* Base darken - much lighter now so the imagery reads
                        clearly. The original 0.40 multiply was crushing the
                        video; 0.12 keeps a hint of moodiness without obscuring
                        the subject. */}
        <div style={{ position: "absolute", inset: 0, background: "rgba(15,16,18,0.12)", mixBlendMode: "multiply" }} />
        {/* "Descent" gradient. The middle of the hero is essentially
                        clear (we want the imagery to breathe), while the bottom
                        of the hero ramps hard into graphite - reading as the
                        surface giving way to depth. Top has a subtle vignette
                        only so the wordmark/nav row sits cleanly. */}
        <div style={{
          position: "absolute", inset: 0,
          background: "linear-gradient(180deg, rgba(1,0,4,0.20) 0%, rgba(1,0,4,0.04) 18%, rgba(1,0,4,0) 38%, rgba(1,0,4,0) 72%, rgba(15,16,18,0.45) 87%, rgba(15,16,18,0.85) 96%, #15171A 100%)"
        }} />
      </div>

      {/* Asymmetric corner crops - register marks at all four corners.
                      Fade with the background (Beat 1). */}
      {/* Top brackets sit higher than the nav row (which is padded
                      24px from the edge) so they read as a frame ABOVE the
                      AXION wordmark and the Request-brief CTA, not alongside
                      them. Bottom brackets stay at 12px to mirror. */}
      {[
      { top: 12, left: 12, borders: "borderTop borderLeft" },
      { top: 12, right: 12, borders: "borderTop borderRight" },
      { bottom: 12, left: 12, borders: "borderBottom borderLeft" },
      { bottom: 12, right: 12, borders: "borderBottom borderRight" }].
      map((c, i) =>
      <div key={i} className={`hero-anim ${c.borders.includes("borderTop") ? "hero-bracket-top" : "hero-bracket-bottom"}`} style={{
        position: "absolute",
        ...c,
        width: 14, height: 14,
        borderColor: "rgba(237,234,226,0.32)",
        borderStyle: "solid",
        borderWidth: 0,
        ...(c.borders.includes("borderTop") ? { borderTopWidth: 1 } : {}),
        ...(c.borders.includes("borderBottom") ? { borderBottomWidth: 1 } : {}),
        ...(c.borders.includes("borderLeft") ? { borderLeftWidth: 1 } : {}),
        ...(c.borders.includes("borderRight") ? { borderRightWidth: 1 } : {}),
        zIndex: 5,
        ...anim("heroFade", 900, 0)
      }} />
      )}

      {/* ── Beat 3 - TOP NAV. Wordmark + links stagger left→right at
                      stagger-base (80ms), button arrives 100ms after the last
                      nav item with a 0.96→1.00 scale settle.
                       Compact-on-scroll (motion brief §10): once scrollY > 100px
                      the bar gets a translucent black backing + 12px backdrop
                      blur, vertical padding shrinks 24→16px, and the wordmark
                      drops to 90% scale. 400ms / ease-out-quart, both ways. The
                      state is held until the user is back at the top - there's
                      no scroll-up un-compact. */
      }
      <NavBar dispatchHero>
        {/* Wordmark - fade only. The wrapper class `ax-nav-mark`
                        owns the compact 90% scale so the entrance animation
                        below is free to write `transform: translateY(...)`
                        without colliding. */}
        <div className="hero-anim ax-nav-mark" style={anim("heroFade", 600, 500)}>
          <AxionMark height={20} />
        </div>

        {/* Nav links - hidden per design direction. The header now
                        carries only the wordmark (left) and the Request Technical
                        Brief CTA (right). */}

        <div style={{ flex: 1 }} />

        {/* Top-right CTA - last nav item is at delay 500 + 3*60 = 680,
                        so the button arrives at 680 + 100 = 780ms. */}
        <div className="hero-anim hero-nav-cta" style={anim("heroCtaIn", 600, 780)}>
          <BrassCTA
            style={{ fontSize: 13 }}
            href="mailto:investors@axionldt.com?subject=Technical%20Brief%20request"
          >Request Technical Brief</BrassCTA>
        </div>
      </NavBar>

      {/* ── Beat 2 - DISPATCH HEADER. Three lines, stagger-base,
                      starting at 400ms. */}
      <div className="hero-dispatch-block" style={{
        position: "absolute",
        top: 110,
        left: 56,
        zIndex: 4,
        fontFamily: "var(--font-mono)",
        fontSize: 11,
        color: "#A8A8A2",
        letterSpacing: "0.20em",
        textTransform: "uppercase",
        lineHeight: 1.9,
        borderLeft: "1px solid #D4A24C",
        paddingLeft: 14, borderLeftStyle: "solid", borderLeftColor: "rgb(212, 162, 76)", borderWidth: "0px 0px 0px 1px", borderColor: "rgb(212, 162, 76)"
      }}>
        {dispatchLines.map((line, i) =>
        <div
          key={line}
          className="hero-anim"
          style={anim("heroRiseMd", 600, 400 + i * (window.ax ? window.ax.stagger("base") : 80))}>
            {line}
          </div>
        )}
      </div>

      {/* ── CENTERED MANIFESTO - H1 (Beat 4), subhead (Beat 5),
                      CTAs (Beat 6) all live in this block but fire on
                      independent schedules. */}
      <div className="hero-manifesto-block" style={{
        position: "absolute",
        left: 56,
        right: "auto",
        bottom: 140,
        zIndex: 4,
        maxWidth: 720
      }}>
        {/* Beat 4 - H1 lines (Pattern A: weight reveal). Each line
                        sits in its own overflow:hidden mask; the inner span
                        rises from translateY(100%) to 0. 100ms stagger between
                        lines per the brief - these read as one sentence but the
                        mask reveal is what gives the type weight. */}
        <h1 className="hero-h1-desktop" style={{
          fontFamily: '"GT America", system-ui, sans-serif',
          lineHeight: 1.04,
          letterSpacing: "-0.018em",
          color: "#EDEAE2",
          margin: 0,
          textWrap: "balance", fontWeight: 700, maxWidth: 1116, width: "100%", fontSize: "var(--fs-fluid-h1)"
        }}>
          {h1Lines.map((line, i) =>
          <span key={i} className="hero-h1-line-mask">
              <span
              className="hero-h1-line"
              style={{ animationDelay: 700 + i * 100 + "ms" }}>
                {line}
              </span>
            </span>
          )}
        </h1>
        <h1 className="hero-h1-mobile" style={{
          fontFamily: '"GT America", system-ui, sans-serif',
          lineHeight: 1.06,
          letterSpacing: "-0.018em",
          color: "#EDEAE2",
          margin: 0,
          textWrap: "balance",
          fontWeight: 700,
          fontSize: "clamp(1.75rem, 1.16rem + 2.51vw, 2.5rem)",
          display: "none"
        }}>
          Geothermal has a <span style={{ color: "#D4A24C" }}>drilling</span> cost problem.
        </h1>

        {/* Beat 5 - subhead. 1100ms / 600ms / +16px y-rise. */}
        <p
          className="hero-anim hero-subtext-desktop"
          style={{
            fontSize: "var(--fs-fluid-body-lg)",

            color: "#A8A8A2",
            maxWidth: 640,

            margin: "28px 0 0",
            textWrap: "pretty", fontFamily: "var(--font-body)",
            ...anim("heroRiseLg", 600, 1100), lineHeight: "1.5", width: "100%"
          }}>The first encapsulated laser system engineered for superhot rock — turning geothermal from regional curiosity into globally clean baseload.


        </p>
        <p
          className="hero-subtext-mobile"
          style={{
            fontSize: "15px",
            color: "#A8A8A2",
            maxWidth: 480,
            width: "100%",
            margin: "16px 0 0",
            textWrap: "pretty",
            fontFamily: "var(--font-body)",
            lineHeight: "1.5",
            display: "none"
          }}>
          Axion is solving it with an encapsulated laser drilling system engineered for superhot rock.
        </p>

        {/* Beat 6 - manifesto link only. The primary "Request
                        technical brief" CTA was removed; the top-right nav CTA
                        already covers that intent. The link now sits left,
                        aligned under the headline + subhead.
                         Color: #EDEAE2 copy / #D4A24C brass underline.
                        Hover: scale 1.00 → 1.02, 300ms ease-out-quart - both
                        the text and the underline grow together because the
                        <a> itself is the transform target. */
        }
        <div style={{ display: "flex", gap: 12, marginTop: 32, alignItems: "center" }}>
          <a
            href="mailto:investors@axionldt.com?subject=Technical%20Brief%20request"
            className="hero-anim ax-manifesto-link"
            style={{
              fontFamily: "var(--font-body)",
              fontSize: 14, fontWeight: 500,
              color: "#EDEAE2",
              textDecoration: "none",
              /* Generous vertical padding so the tap target clears 44px
                 on mobile without shifting the visual position of the
                 text — the link still reads as an inline manifesto link. */
              padding: "12px 0 8px",
              letterSpacing: "-0.005em",
              display: "inline-block",
              ...anim("heroRiseSm", 600, 1400)
            }}>Request Technical Brief

          </a>
        </div>
      </div>

      {/* ── Beat 6 - TELEMETRY footer. Each line fades in only,
                      stagger-base, starting at 1400ms. */}
      <div className="hero-telemetry-block" style={{
        position: "absolute",
        bottom: 56,
        right: 56,
        zIndex: 4,
        textAlign: "right",
        fontFamily: "var(--font-mono)",
        fontSize: 11,
        color: "#A8A8A2",
        letterSpacing: "0.16em",
        textTransform: "uppercase",
        lineHeight: 1.7
      }}>
        {telemetry.map((line, i) =>
        <div
          key={line.text}
          className="hero-anim"
          style={{
            color: line.color,
            ...anim("heroFade", 600, 1400 + i * (window.ax ? window.ax.stagger("base") : 80))
          }}>
            {line.text}
          </div>
        )}
        <div
          className="hero-anim"
          style={{
            display: "flex",
            justifyContent: "flex-end",
            alignItems: "center",
            gap: 8,
            marginTop: 6,
            ...anim("heroFade", 600, 1400 + telemetry.length * 60)
          }}>
          <span>Operational</span>
          <span style={{ width: 6, height: 6, background: "#D4A24C", display: "inline-block" }} />
        </div>
      </div>

      {/* Mobile stack - switch the absolute-positioned blocks to
                static flow below 768px and let the hero grow to content. */}
      <div className="ax-plate-caption hero-plate-mobile" style={{
        position: "absolute",
        bottom: 64,
        left: "var(--sp-section-x)",
        textAlign: "left",
        fontFamily: "var(--font-mono)",
        fontSize: 10,
        color: "rgba(237, 234, 226, 0.30)",
        letterSpacing: "0.16em",
        textTransform: "uppercase",
        lineHeight: 1.7,
        zIndex: 4,
        display: "none"
      }}>
          <div>Plate I.</div>
        </div>
      <style>{`
        @media (max-width: 768px) {
          .hero-dispatch-block,
          .hero-manifesto-block,
          .hero-telemetry-block {
            position: relative !important;
            z-index: 4 !important;
            top: auto !important;
            left: auto !important;
            right: auto !important;
            bottom: auto !important;
          }
          .hero-dispatch-block {
            padding: calc(var(--sp-fluid-xl) + 64px) var(--sp-section-x) 0 !important;
          }
          .hero-manifesto-block {
            position: absolute !important;
            bottom: 100px !important;
            left: 0 !important;
            right: 0 !important;
            top: auto !important;
            max-width: 100% !important;
            padding: 0 var(--sp-section-x) !important;
            z-index: 4 !important;
          }
          .hero-telemetry-block {
            text-align: left !important;
            padding: var(--sp-fluid-base) var(--sp-section-x) var(--sp-fluid-tight) !important;
          }
          .hero-telemetry-block > div:last-child {
            justify-content: flex-start !important;
          }
          .hero-nav-cta {
            display: none !important;
          }
          .hero-anim,
          .hero-h1-line {
            animation: none !important;
            opacity: 1 !important;
            transform: none !important;
          }
          /* Hero content swap on mobile */
          .hero-h1-desktop,
          .hero-subtext-desktop {
            display: none !important;
          }
          .hero-h1-mobile,
          .hero-subtext-mobile,
          .hero-plate-mobile {
            display: block !important;
          }
          .hero-bg-video {
            object-position: center center !important;
          }
        }
      `}</style>
    </div>);

}

window.HeroDispatch = HeroDispatch;