// V4 — Cinematic Proof Document (refined)
// • Single artboard (no comparison canvas).
// • Ambient background motion: drifting stars, orbiting sun, scripture glyphs.
// • Section fade-ins via IntersectionObserver.
// • Count-up on pillar numbers.
// • Scrubber rebuilt — piecewise time compression so 1780/1947/2027 don't
//   collide; titles surface only in the readout, never on the rail.
// • Fig.1 polished — tooltip no longer clips, hover-isolate sharper.

const V4 = (() => {
  const C = window.SIG_CONTENT;
  const { useState, useEffect, useRef, useMemo, useLayoutEffect } = React;

  const v4 = {
    bg:      '#0a0907',
    deep:    '#050403',
    paper:   '#ece4cf',
    paperD:  'rgba(236,228,207,0.62)',
    paperM:  'rgba(236,228,207,0.40)',
    paperVD: 'rgba(236,228,207,0.18)',
    rule:    'rgba(236,228,207,0.14)',
    ruleS:   'rgba(236,228,207,0.08)',
    brass:   '#cfa44a',
    brassD:  '#8b6c2c',
    brassL:  '#e9c674',
    ember:   '#c8462b',
    ink:     '#0a0907',

    serif:   '"Cormorant Garamond", "EB Garamond", serif',
    inscr:   '"Cinzel", "Trajan Pro", serif',
    sans:    '"IBM Plex Sans", "Söhne", "Helvetica Neue", system-ui, sans-serif',
    mono:    '"JetBrains Mono", "IBM Plex Mono", ui-monospace, monospace',
  };

  // ============================================================
  // CSS
  // ============================================================
  const css = `
    .v4{font-family:${v4.sans};background:${v4.bg};color:${v4.paper};
      -webkit-font-smoothing:antialiased;line-height:1.55;
      max-width:1480px;margin:0 auto;position:relative;overflow:hidden;}
    .v4 *{box-sizing:border-box;}
    .v4 a{color:inherit;text-decoration:none;cursor:pointer;}
    .v4 h1,.v4 h2,.v4 h3{margin:0;font-weight:400;letter-spacing:-0.015em;}
    .v4 .mono{font-family:${v4.mono};font-size:11px;letter-spacing:0.18em;text-transform:uppercase;}
    .v4 .num{font-family:${v4.serif};font-feature-settings:"lnum","tnum";}
    .v4 .brass{color:${v4.brass};}
    .v4 .muted{color:${v4.paperM};}

    /* ============ AMBIENT BACKGROUND (page-wide) ============ */
    .v4 .ambient{position:absolute;inset:0;pointer-events:none;z-index:0;overflow:hidden;}
    .v4 .ambient .glyphs{position:absolute;inset:0;color:${v4.brass};opacity:0.05;font-family:${v4.serif};font-style:italic;}
    .v4 .ambient .glyph{position:absolute;font-size:120px;animation:v4-drift 60s linear infinite;}

    @keyframes v4-drift {
      0% { transform: translateY(0) rotate(0deg); opacity:0; }
      10%{ opacity:0.35; }
      90%{ opacity:0.35; }
      100% { transform: translateY(-120vh) rotate(-3deg); opacity:0; }
    }

    /* ============ TOPBAR ============ */
    .v4 .topbar{position:relative;z-index:5;display:flex;align-items:center;justify-content:space-between;
      padding:18px 44px;border-bottom:1px solid ${v4.rule};background:rgba(5,4,3,0.85);backdrop-filter:blur(8px);}
    .v4 .wm{display:flex;align-items:center;gap:14px;}
    .v4 .wm .seal{width:32px;height:32px;border:1px solid ${v4.brass};border-radius:50%;display:flex;align-items:center;justify-content:center;color:${v4.brass};font-family:${v4.inscr};font-size:14px;
      animation:v4-rotate 60s linear infinite;}
    @keyframes v4-rotate{to{transform:rotate(360deg);}}
    .v4 .wm .name{font-family:${v4.inscr};font-size:12px;letter-spacing:0.28em;color:${v4.paper};}
    .v4 .wm .sub{font-family:${v4.serif};font-style:italic;font-size:12px;color:${v4.paperM};margin-left:6px;}
    .v4 .topnav{display:flex;gap:24px;font-size:12px;letter-spacing:0.04em;color:${v4.paperD};align-items:center;}
    .v4 .topnav a{position:relative;transition:color .2s;}
    .v4 .topnav a:not(.pill):hover{color:${v4.paper};}
    .v4 .topnav a:not(.pill)::after{content:"";position:absolute;left:0;right:0;bottom:-4px;height:1px;background:${v4.brass};transform:scaleX(0);transform-origin:center;transition:transform .25s ease;}
    .v4 .topnav a:not(.pill):hover::after{transform:scaleX(1);}
    .v4 .topnav .pill{padding:8px 16px;border:1px solid ${v4.brass};color:${v4.brass};transition:all .2s;}
    .v4 .topnav .pill:hover{background:${v4.brass};color:${v4.ink};}

    /* ============ STAGE / HERO ============ */
    .v4 .stage{position:relative;min-height:760px;overflow:hidden;border-bottom:1px solid ${v4.rule};
      background:${v4.deep};}
    /* Hero photographic backdrop — narrow gate. Sits below SVG gate so SVG light slit aligns. */
    .v4 .stage::before{content:"";position:absolute;inset:0;
      background-image:url('${C.hero.bg_image || ""}');
      background-size:cover;background-position:center;
      opacity:0.34;filter:contrast(1.04) saturate(1.05);z-index:0;}
    .v4 .stage::after{content:"";position:absolute;inset:0;background:
      linear-gradient(180deg, rgba(5,4,3,0.45) 0%, rgba(5,4,3,0.20) 35%, rgba(5,4,3,0.92) 100%);
      z-index:1;pointer-events:none;}
    .v4 .stage .vignette{position:absolute;inset:0;z-index:2;background:
      radial-gradient(ellipse 90% 70% at 50% 35%, rgba(207,164,74,0.18), transparent 60%),
      radial-gradient(circle at 80% 95%, rgba(200,70,43,0.10), transparent 55%);
      pointer-events:none;}
    .v4 .stage .noise{position:absolute;inset:0;opacity:0.10;mix-blend-mode:overlay;pointer-events:none;
      background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='220' height='220'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2'/></filter><rect width='220' height='220' filter='url(%23n)'/></svg>");}

    /* Starfield canvas */
    .v4 .stage .stars{position:absolute;inset:0;pointer-events:none;}

    /* Sun behind gate — slowly rotating, with rays */
    .v4 .stage .sun{position:absolute;left:50%;top:38%;transform:translate(-50%,-50%);width:540px;height:540px;pointer-events:none;}
    .v4 .stage .sun-disc{transform-origin:center;animation:v4-sun-rotate 200s linear infinite;}
    @keyframes v4-sun-rotate{to{transform:rotate(360deg);}}
    .v4 .stage .sun-pulse{animation:v4-sun-pulse 8s ease-in-out infinite;transform-origin:center;}
    @keyframes v4-sun-pulse{0%,100%{opacity:0.42;}50%{opacity:0.62;}}

    /* Gate silhouette in foreground */
    .v4 .stage .gate{position:absolute;left:50%;bottom:-100px;transform:translateX(-50%);width:560px;height:740px;opacity:0.92;pointer-events:none;}

    .v4 .stage-inner{position:relative;z-index:3;padding:54px 56px 0;}
    .v4 .h-meta{display:grid;grid-template-columns:1fr auto 1fr;align-items:center;gap:24px;
      font-family:${v4.mono};font-size:10px;letter-spacing:0.22em;color:${v4.paperM};
      padding-bottom:14px;border-bottom:1px solid ${v4.rule};margin-bottom:48px;}
    .v4 .h-meta b{color:${v4.paper};font-weight:500;}
    .v4 .h-meta .stamp{color:${v4.brass};text-align:center;display:inline-flex;align-items:center;gap:10px;}
    .v4 .h-meta .stamp .star{color:${v4.brass};animation:v4-twinkle 3.4s ease-in-out infinite;}
    @keyframes v4-twinkle{0%,100%{opacity:1;}50%{opacity:0.35;}}
    .v4 .h-meta .right{text-align:right;}
    .v4 .eyebrow{font-family:${v4.mono};font-size:11px;letter-spacing:0.32em;color:${v4.brass};
      display:inline-flex;align-items:center;gap:14px;}
    .v4 .eyebrow::before,.v4 .eyebrow::after{content:"";width:36px;height:1px;background:${v4.brass};opacity:0.5;}
    .v4 .h-title{font-family:${v4.serif};font-size:92px;line-height:0.98;letter-spacing:-0.028em;
      margin:20px 0 0;max-width:24ch;font-weight:400;}
    .v4 .h-title em{font-style:italic;color:${v4.brass};}
    .v4 .h-sub{font-family:${v4.serif};font-style:italic;font-size:20px;line-height:1.45;color:${v4.paperD};
      margin:24px 0 0;max-width:54ch;}
    .v4 .hero-ctas{margin-top:32px;display:flex;gap:14px;align-items:center;flex-wrap:wrap;}
    .v4 .hero-cta{font-family:${v4.mono};font-size:11px;letter-spacing:0.2em;text-transform:uppercase;
      padding:14px 22px;border:1px solid ${v4.brass};color:${v4.brass};transition:all .2s;}
    .v4 .hero-cta:hover{background:${v4.brass};color:${v4.ink};}
    .v4 .hero-cta.primary{background:${v4.brass};color:${v4.ink};}
    .v4 .hero-cta.primary:hover{background:${v4.paper};border-color:${v4.paper};}

    /* MESSAGE — theology-first section. Three columns of doctrine before
       any math appears. */
    .v4 .message-grid{margin-top:48px;display:grid;grid-template-columns:1fr 1fr 1fr;gap:0;
      border:1px solid ${v4.rule};background:${v4.deep};}
    .v4 .msg{padding:36px 32px 40px;border-right:1px solid ${v4.rule};position:relative;}
    .v4 .msg:last-child{border-right:none;}
    .v4 .msg .ref{font-family:${v4.mono};font-size:10px;letter-spacing:0.22em;text-transform:uppercase;color:${v4.ember};}
    .v4 .msg h3{font-family:${v4.serif};font-weight:400;font-size:34px;letter-spacing:-0.018em;line-height:1.06;margin-top:18px;}
    .v4 .msg h3 em{font-style:italic;color:${v4.brass};}
    .v4 .msg .quote{font-family:${v4.serif};font-style:italic;font-size:17px;line-height:1.5;color:${v4.paperD};
      margin-top:22px;padding-left:18px;border-left:1px solid ${v4.brass};}
    .v4 .msg p{font-size:14.5px;line-height:1.6;color:${v4.paperD};margin:18px 0 0;}

    /* MESSIAH2027 CALLOUT — reusable strip linking out for full timeline */
    .v4 .m27{margin-top:24px;display:flex;align-items:center;justify-content:space-between;gap:24px;
      padding:20px 28px;border:1px solid ${v4.brass};background:rgba(207,164,74,0.06);}
    .v4 .m27 .label{font-family:${v4.mono};font-size:10px;letter-spacing:0.22em;text-transform:uppercase;color:${v4.brass};}
    .v4 .m27 .body{font-family:${v4.serif};font-style:italic;font-size:18px;color:${v4.paper};flex:1;line-height:1.4;}
    .v4 .m27 .link{font-family:${v4.mono};font-size:11px;letter-spacing:0.2em;text-transform:uppercase;
      color:${v4.brass};border:1px solid ${v4.brass};padding:12px 18px;transition:all .15s;white-space:nowrap;}
    .v4 .m27 .link:hover{background:${v4.brass};color:${v4.ink};}

    /* SCRUBBER */
    .v4 .scrubber{position:relative;z-index:3;padding:42px 56px 64px;
      background:linear-gradient(180deg, transparent, ${v4.bg} 40%);}
    .v4 .scrub-head{display:flex;justify-content:space-between;align-items:baseline;
      font-family:${v4.mono};font-size:11px;letter-spacing:0.22em;color:${v4.paperM};
      margin-bottom:32px;}
    .v4 .scrub-head .lbl{color:${v4.brass};display:inline-flex;align-items:center;gap:10px;}
    .v4 .scrub-head .lbl::before{content:"";width:18px;height:1px;background:${v4.brass};}
    /* Rail container with rail line near the BOTTOM. Bracket connector
       lanes occupy the upper space above the rail. */
    .v4 .scrub-rail{position:relative;height:280px;cursor:ew-resize;user-select:none;}
    .v4 .scrub-line{position:absolute;left:0;right:0;top:200px;height:1px;background:${v4.rule};}
    .v4 .scrub-fill{position:absolute;left:0;top:200px;height:1px;background:linear-gradient(90deg,${v4.brassD},${v4.brass});box-shadow:0 0 16px ${v4.brass};transition:width .08s linear;}
    .v4 .scrub-tick{position:absolute;top:200px;width:1px;height:6px;background:${v4.ruleS};transform:translate(-50%,-50%);}
    .v4 .scrub-tick.major{height:10px;background:${v4.rule};}
    .v4 .scrub-tickL{position:absolute;top:214px;transform:translateX(-50%);
      font-family:${v4.mono};font-size:9px;letter-spacing:0.12em;color:${v4.paperVD};white-space:nowrap;}
    .v4 .scrub-mark{position:absolute;top:200px;transform:translate(-50%,-50%);
      width:8px;height:8px;border:1px solid ${v4.brass};background:${v4.bg};border-radius:50%;
      transition:all .25s;z-index:2;}
    .v4 .scrub-mark.passed{background:${v4.brass};box-shadow:0 0 10px ${v4.brass};}
    .v4 .scrub-mark.anchor{width:14px;height:14px;background:${v4.brass};box-shadow:0 0 18px ${v4.brass};}
    .v4 .scrub-mark.anchor.passed{animation:v4-anchor-pulse 2s ease-in-out infinite;}
    @keyframes v4-anchor-pulse{0%,100%{box-shadow:0 0 18px ${v4.brass};}50%{box-shadow:0 0 28px ${v4.brassL};}}
    .v4 .scrub-anchor-label{position:absolute;top:226px;transform:translateX(-50%);
      font-family:${v4.mono};font-size:10px;letter-spacing:0.18em;color:${v4.paperD};white-space:nowrap;
      transition:color .2s,opacity .2s;}
    .v4 .scrub-anchor-label.lvl1{top:246px;}
    .v4 .scrub-anchor-label.active{color:${v4.brass};}

    /* BRACKETS — U-shaped prophecy connectors, rising up from the rail.
       Sorted by horizontal span so SHORTEST length sits at the LOWEST
       depth (closest to the rail), and longest is tallest. Nested. */
    .v4 .scrub-brackets{position:absolute;left:0;right:0;top:0;height:200px;pointer-events:none;}
    .v4 .b-track{position:absolute;height:1px;background:var(--c);opacity:0.10;}
    .v4 .b-track.l,.v4 .b-track.r{width:1px;background:var(--c);opacity:0.10;}
    .v4 .b-stem{position:absolute;width:1px;background:var(--c);transform:translateX(-50%);
      transition:opacity .3s;opacity:1;}
    .v4 .b-bar{position:absolute;height:1.5px;background:var(--c);border-radius:.75px;
      transition:width 90ms linear,left 90ms linear,opacity .3s;}
    .v4 .b-dot{position:absolute;width:7px;height:7px;border-radius:50%;background:var(--c);color:var(--c);
      transform:translate(-50%,-50%);box-shadow:0 0 10px currentColor;opacity:0;
      transition:opacity .25s;}
    .v4 .b-dot.lit{opacity:1;}
    .v4 .b-label{position:absolute;font-family:${v4.mono};font-size:9px;letter-spacing:0.14em;
      transform:translateX(-50%);white-space:nowrap;color:var(--c);text-transform:uppercase;
      opacity:0;transition:opacity .25s;}
    .v4 .b-label.lit{opacity:0.7;}
    .v4 .scrub-handle{position:absolute;top:200px;transform:translate(-50%,-50%);width:2px;height:36px;
      background:${v4.paper};box-shadow:0 0 22px rgba(236,228,207,0.7);transition:left .08s linear;z-index:3;}
    .v4 .scrub-handle::before,.v4 .scrub-handle::after{content:"";position:absolute;left:50%;transform:translateX(-50%);width:0;height:0;border-left:7px solid transparent;border-right:7px solid transparent;}
    .v4 .scrub-handle::before{top:-10px;border-top:9px solid ${v4.paper};}
    .v4 .scrub-handle::after{bottom:-10px;border-bottom:9px solid ${v4.paper};}
    .v4 .scrub-readout{position:absolute;top:-200px;left:0;transform:translateX(-50%);text-align:center;
      min-width:220px;transition:left .08s linear;z-index:4;}
    .v4 .scrub-readout .y{font-family:${v4.serif};font-size:38px;line-height:1;color:${v4.paper};font-style:italic;}
    .v4 .scrub-readout .ttl{font-family:${v4.serif};font-style:italic;font-size:14px;color:${v4.brass};margin-top:4px;white-space:nowrap;min-height:18px;}
    .v4 .scrub-readout .e{font-family:${v4.mono};font-size:9px;letter-spacing:0.18em;color:${v4.paperM};margin-top:2px;}

    /* FORMULA BAR */
    .v4 .formbar{position:relative;z-index:1;display:grid;grid-template-columns:auto 1fr auto;gap:48px;align-items:center;
      padding:30px 56px;border-bottom:1px solid ${v4.rule};border-top:1px solid ${v4.rule};
      background:${v4.deep};overflow:hidden;}
    .v4 .formbar::before{content:"";position:absolute;left:-50%;top:0;width:200%;height:100%;
      background:linear-gradient(90deg, transparent, rgba(207,164,74,0.05), transparent);
      animation:v4-sheen 18s linear infinite;pointer-events:none;}
    @keyframes v4-sheen{from{transform:translateX(-50%);}to{transform:translateX(50%);}}
    .v4 .formbar .lbl{font-family:${v4.mono};font-size:11px;letter-spacing:0.22em;color:${v4.paperM};
      position:relative;z-index:1;}
    .v4 .formbar .formula{font-family:${v4.serif};font-size:42px;letter-spacing:-0.01em;
      display:flex;align-items:baseline;gap:14px;position:relative;z-index:1;}
    .v4 .formbar .op{color:${v4.brassD};}
    .v4 .formbar .res{color:${v4.brass};}
    .v4 .formbar .meta{font-family:${v4.serif};font-style:italic;font-size:14px;color:${v4.paperM};
      text-align:right;max-width:30ch;position:relative;z-index:1;}

    /* SECTION SCAFFOLD */
    .v4 .section{position:relative;z-index:1;display:grid;grid-template-columns:64px 1fr;
      border-bottom:1px solid ${v4.rule};background:${v4.bg};}
    .v4 .section .gutter{border-right:1px solid ${v4.rule};font-family:${v4.mono};font-size:10px;
      color:${v4.paperVD};padding:32px 0 0;text-align:center;letter-spacing:0.04em;line-height:1.9;
      background:${v4.deep};position:relative;}
    .v4 .section .body{padding:64px 56px 80px;position:relative;}

    /* fade in on scroll */
    .v4 .reveal{opacity:0;transform:translateY(24px);transition:opacity .9s cubic-bezier(.2,.7,.3,1),transform .9s cubic-bezier(.2,.7,.3,1);}
    .v4 .reveal.in{opacity:1;transform:translateY(0);}
    .v4 .reveal.d1{transition-delay:.08s;} .v4 .reveal.d2{transition-delay:.16s;}
    .v4 .reveal.d3{transition-delay:.24s;} .v4 .reveal.d4{transition-delay:.32s;}
    .v4 .reveal.d5{transition-delay:.40s;} .v4 .reveal.d6{transition-delay:.48s;}

    .v4 .sec-num{font-family:${v4.mono};font-size:10px;letter-spacing:0.24em;color:${v4.ember};text-transform:uppercase;
      display:inline-flex;align-items:center;gap:10px;}
    .v4 .sec-num::before{content:"";width:24px;height:1px;background:${v4.ember};}
    .v4 .sec-h{font-family:${v4.serif};font-weight:400;font-size:52px;letter-spacing:-0.02em;line-height:1.05;
      margin:14px 0 18px;max-width:24ch;}
    .v4 .sec-h em{font-style:italic;color:${v4.brass};}
    .v4 .sec-lead{font-family:${v4.serif};font-size:19px;line-height:1.55;color:${v4.paperD};max-width:64ch;}

    /* PILLARS */
    .v4 .steps{margin-top:48px;display:grid;grid-template-columns:1fr 1fr 1fr;gap:0;
      border:1px solid ${v4.rule};background:${v4.deep};}
    .v4 .step{padding:32px 30px 36px;border-right:1px solid ${v4.rule};position:relative;
      transition:background .25s;}
    .v4 .step:last-child{border-right:none;}
    .v4 .step:hover{background:${v4.bg};}
    .v4 .step::after{content:"";position:absolute;left:0;right:0;bottom:0;height:0;background:${v4.brass};
      transition:height .25s;}
    .v4 .step:hover::after{height:2px;box-shadow:0 0 18px ${v4.brass};}
    .v4 .step .h{display:flex;justify-content:space-between;align-items:baseline;
      font-family:${v4.mono};font-size:10px;letter-spacing:0.22em;color:${v4.paperM};}
    .v4 .step .h .role{color:${v4.ember};}
    .v4 .step .bn{font-family:${v4.serif};font-size:124px;line-height:0.95;letter-spacing:-0.035em;
      margin:14px 0 0;color:${v4.paper};font-feature-settings:"lnum","tnum";}
    .v4 .step .unit{font-family:${v4.serif};font-style:italic;font-size:22px;color:${v4.paperD};margin-top:2px;}
    .v4 .step .src{font-family:${v4.mono};font-size:10px;letter-spacing:0.14em;color:${v4.paperM};margin-top:14px;}
    .v4 .step .eq{margin-top:22px;padding:14px 16px;background:${v4.bg};border:1px solid ${v4.rule};
      font-family:${v4.mono};font-size:11.5px;letter-spacing:0.04em;color:${v4.paperD};}
    .v4 .step .eq .arrow{color:${v4.ember};margin:0 6px;}
    .v4 .step .eq .res{color:${v4.brass};}
    .v4 .step p{font-size:14.5px;line-height:1.55;color:${v4.paperD};margin:20px 0 0;}
    .v4 .step .shadow{margin-top:18px;padding-top:18px;border-top:1px dashed ${v4.rule};
      font-family:${v4.serif};font-style:italic;font-size:14px;line-height:1.5;color:${v4.paperM};}

    /* CALCULATOR */
    .v4 .calc-card{margin-top:40px;border:1px solid ${v4.brass};background:${v4.deep};
      box-shadow:0 0 0 1px rgba(207,164,74,0.08), 0 24px 48px rgba(0,0,0,0.6);position:relative;overflow:hidden;}
    .v4 .calc-card::before{content:"";position:absolute;inset:0;background:radial-gradient(ellipse at top right, rgba(207,164,74,0.06), transparent 50%);pointer-events:none;}
    .v4 .calc-head{position:relative;display:flex;justify-content:space-between;align-items:center;
      padding:14px 22px;background:${v4.brass};color:${v4.ink};
      font-family:${v4.mono};font-size:11px;letter-spacing:0.18em;text-transform:uppercase;}
    .v4 .calc-head .led{width:8px;height:8px;background:#1a7a3a;border-radius:50%;box-shadow:0 0 8px #4cd07a;display:inline-block;margin-right:8px;animation:v4-led 2s ease-in-out infinite;}
    @keyframes v4-led{0%,100%{opacity:1;box-shadow:0 0 8px #4cd07a;}50%{opacity:0.5;box-shadow:0 0 14px #4cd07a;}}
    .v4 .calc-body{position:relative;padding:32px 32px 28px;display:grid;grid-template-columns:1.2fr 1px 1fr;gap:32px;}
    .v4 .calc-col label{display:block;font-family:${v4.mono};font-size:10px;letter-spacing:0.18em;text-transform:uppercase;color:${v4.paperM};margin-bottom:8px;}
    .v4 .calc-col .field{display:flex;align-items:baseline;gap:12px;margin-bottom:18px;}
    .v4 .calc-col input{font-family:${v4.mono};font-size:22px;background:${v4.bg};border:1px solid ${v4.rule};padding:10px 14px;width:100%;color:${v4.paper};outline:none;border-radius:0;transition:border-color .2s,box-shadow .2s;}
    .v4 .calc-col input:focus{border-color:${v4.brass};box-shadow:0 0 0 3px rgba(207,164,74,0.15);}
    .v4 .calc-col input.short{width:140px;}
    .v4 .calc-col .field .unit{font-family:${v4.mono};font-size:11px;letter-spacing:0.1em;color:${v4.paperM};}
    .v4 .calc-divider{background:${v4.rule};}
    .v4 .calc-out{display:flex;flex-direction:column;justify-content:space-between;}
    .v4 .calc-out .stage-l{font-family:${v4.mono};font-size:11px;letter-spacing:0.08em;color:${v4.paperM};margin-bottom:8px;}
    .v4 .calc-out .eq{font-family:${v4.serif};font-size:22px;line-height:1.5;margin-bottom:8px;color:${v4.paperD};}
    .v4 .calc-out .eq .v{color:${v4.brass};font-feature-settings:"lnum","tnum";}
    .v4 .calc-out .result-box{margin-top:16px;padding:20px 24px;background:${v4.brass};color:${v4.ink};
      display:flex;justify-content:space-between;align-items:baseline;position:relative;overflow:hidden;}
    .v4 .calc-out .result-box::before{content:"";position:absolute;inset:0;background:radial-gradient(circle at 80% 100%, rgba(255,255,255,0.18), transparent 50%);}
    .v4 .calc-out .result-box .l{position:relative;z-index:1;font-family:${v4.mono};font-size:10px;letter-spacing:0.2em;text-transform:uppercase;color:rgba(10,9,7,0.55);}
    .v4 .calc-out .result-box .y{position:relative;z-index:1;font-family:${v4.serif};font-size:48px;letter-spacing:-0.02em;line-height:1;font-feature-settings:"lnum","tnum";font-style:italic;}
    .v4 .calc-out .result-box .y em{font-style:italic;color:#7a2418;font-style:normal;}
    .v4 .preset-row{padding:14px 22px;border-top:1px solid ${v4.rule};display:flex;align-items:center;gap:10px;flex-wrap:wrap;font-family:${v4.mono};font-size:10px;letter-spacing:0.14em;text-transform:uppercase;color:${v4.paperM};position:relative;}
    .v4 .preset{padding:6px 11px;border:1px solid ${v4.rule};background:transparent;color:${v4.paperD};cursor:pointer;font-size:10px;letter-spacing:0.1em;transition:all .15s;}
    .v4 .preset:hover{border-color:${v4.brass};color:${v4.brass};}
    .v4 .preset.active{background:${v4.brass};color:${v4.ink};border-color:${v4.brass};}

    /* CHEAT SHEET */
    .v4 .cheat-card{margin-top:18px;border:1px solid ${v4.rule};background:${v4.deep};}
    .v4 .cheat-head{display:flex;justify-content:space-between;align-items:center;padding:14px 22px;
      border-bottom:1px solid ${v4.rule};font-family:${v4.mono};font-size:11px;letter-spacing:0.18em;
      text-transform:uppercase;color:${v4.paperM};}
    .v4 .cheat-head .ttl{color:${v4.brass};}
    .v4 .cheat-row{display:grid;grid-template-columns:60px 1.1fr 1.4fr 1.3fr 1fr;gap:18px;
      padding:14px 22px;border-bottom:1px solid ${v4.ruleS};align-items:baseline;
      transition:background .15s;}
    .v4 .cheat-row:last-child{border-bottom:none;}
    .v4 .cheat-row.head{font-family:${v4.mono};font-size:9px;letter-spacing:0.22em;text-transform:uppercase;
      color:${v4.paperVD};padding:10px 22px;background:${v4.bg};border-bottom:1px solid ${v4.rule};}
    .v4 .cheat-row:not(.head):hover{background:${v4.bg};}
    .v4 .cheat-row .idx{font-family:${v4.mono};font-size:10px;letter-spacing:0.16em;color:${v4.paperM};}
    .v4 .cheat-row .span{font-family:${v4.serif};font-size:19px;color:${v4.paper};font-style:italic;line-height:1.1;}
    .v4 .cheat-row.r-primary .span{color:${v4.ember};}
    .v4 .cheat-row.r-deadline .span{color:${v4.brass};}
    .v4 .cheat-row .conv{font-family:${v4.mono};font-size:12px;letter-spacing:0.04em;color:${v4.paperD};}
    .v4 .cheat-row .conv .v{color:${v4.brass};}
    .v4 .cheat-row .conv .lit{color:${v4.paperM};font-style:italic;}
    .v4 .cheat-row .lands{font-family:${v4.mono};font-size:12px;letter-spacing:0.04em;color:${v4.paper};}
    .v4 .cheat-row .lands .land-res{color:${v4.brass};}
    .v4 .cheat-row .src{font-family:${v4.mono};font-size:10px;letter-spacing:0.18em;color:${v4.paperM};text-align:right;}

    /* FIG. 1 */
    .v4 .fig{margin:48px 0 0;border:1px solid ${v4.rule};background:${v4.deep};position:relative;}
    .v4 .fig .head{display:flex;justify-content:space-between;align-items:baseline;padding:18px 28px;
      border-bottom:1px solid ${v4.rule};font-family:${v4.mono};font-size:10px;letter-spacing:0.22em;
      color:${v4.paperM};}
    .v4 .fig .head .ttl{color:${v4.brass};}
    .v4 .fig .head .controls{display:flex;gap:8px;align-items:center;}
    .v4 .fig .head .play{padding:6px 12px;border:1px solid ${v4.brass};color:${v4.brass};
      background:transparent;cursor:pointer;font-family:${v4.mono};font-size:10px;letter-spacing:0.16em;
      text-transform:uppercase;transition:all .15s;}
    .v4 .fig .head .play:hover{background:${v4.brass};color:${v4.ink};}
    .v4 .fig .chart-wrap{position:relative;}
    .v4 .fig svg{display:block;width:100%;height:auto;}
    .v4 .fig .legend{display:flex;gap:24px;padding:16px 28px;border-top:1px solid ${v4.rule};
      font-family:${v4.mono};font-size:10px;letter-spacing:0.14em;text-transform:uppercase;color:${v4.paperM};
      align-items:center;flex-wrap:wrap;}
    .v4 .fig .legend .d{display:flex;align-items:center;gap:8px;}
    .v4 .fig .legend .sw{width:18px;height:2px;}
    .v4 .fig .legend .swc{width:10px;height:10px;border-radius:50%;}

    .v4 .arc-line{stroke-linecap:round;transition:stroke-width .2s, filter .2s, opacity .25s;}
    .v4 .arc-line.draw{stroke-dashoffset:var(--len);animation:v4-draw 1.6s cubic-bezier(.65,.05,.36,1) forwards;animation-delay:var(--d);}
    @keyframes v4-draw{to{stroke-dashoffset:0;}}
    .v4 .arc-dot{opacity:0;animation:v4-fade .4s ease-out forwards;animation-delay:var(--d2);}
    @keyframes v4-fade{to{opacity:1;}}
    .v4 .arc-row{cursor:pointer;}
    .v4 .arc-row:hover .arc-line{stroke-width:3.4;filter:drop-shadow(0 0 12px currentColor);}
    .v4 .arc-row:hover .arc-end{r:6;}
    .v4 .arc-row:hover text{fill:${v4.paper};}
    .v4 .arc-row.dim .arc-line{opacity:0.18;}
    .v4 .arc-row.dim text{opacity:0.3;}
    .v4 .arc-row.dim .arc-dot{opacity:0.3;}

    .v4 .conv-pulse{transform-origin:center;transform-box:fill-box;animation:v4-conv 2.8s ease-out infinite;}
    .v4 .conv-pulse.p2{animation-delay:0.9s;}
    .v4 .conv-pulse.p3{animation-delay:1.8s;}
    @keyframes v4-conv{0%{r:14;opacity:0.8;}100%{r:60;opacity:0;}}
    .v4 .conv-core{animation:v4-core 2s ease-in-out infinite;}
    @keyframes v4-core{0%,100%{opacity:1;}50%{opacity:0.6;}}

    .v4 .fig .tooltip{position:absolute;pointer-events:none;background:${v4.bg};border:1px solid ${v4.brass};
      padding:14px 18px;min-width:240px;transform:translate(-50%, -100%);transition:opacity .15s;
      box-shadow:0 12px 32px rgba(0,0,0,0.6);z-index:5;}
    .v4 .fig .tooltip .t{font-family:${v4.serif};font-style:italic;font-size:18px;color:${v4.paper};margin-bottom:6px;}
    .v4 .fig .tooltip .m{font-family:${v4.mono};font-size:10px;letter-spacing:0.16em;text-transform:uppercase;color:${v4.brass};}
    .v4 .fig .tooltip .a{font-family:${v4.mono};font-size:11px;color:${v4.paperD};margin-top:8px;letter-spacing:0.04em;}
    .v4 .fig .tooltip .src{font-family:${v4.mono};font-size:10px;letter-spacing:0.14em;color:${v4.paperM};margin-top:6px;}

    /* EXHIBIT GRID */
    .v4 .ex-grid{display:grid;grid-template-columns:repeat(5,1fr);gap:1px;background:${v4.rule};
      border:1px solid ${v4.rule};margin-top:40px;}
    .v4 .ex-card{position:relative;background:${v4.deep};padding:26px 22px 24px;min-height:240px;
      display:flex;flex-direction:column;cursor:pointer;
      transition:background .25s,transform .25s;overflow:hidden;}
    .v4 .ex-card::before{content:"";position:absolute;inset:0;background:radial-gradient(ellipse at top right, rgba(207,164,74,0), transparent 50%);transition:background .35s;pointer-events:none;}
    .v4 .ex-card:hover{background:${v4.bg};}
    .v4 .ex-card:hover::before{background:radial-gradient(ellipse at top right, rgba(207,164,74,0.12), transparent 60%);}
    .v4 .ex-card .idx{font-family:${v4.mono};font-size:10px;letter-spacing:0.22em;color:${v4.paperM};}
    .v4 .ex-card .tier{font-family:${v4.mono};font-size:9px;letter-spacing:0.24em;text-transform:uppercase;}
    .v4 .ex-card .tier-primary{color:${v4.ember};}
    .v4 .ex-card .tier-deadline{color:${v4.paper};}
    .v4 .ex-card .tier-confirm{color:${v4.paperM};}
    .v4 .ex-card .bn{font-family:${v4.serif};font-size:60px;line-height:1;margin-top:6px;letter-spacing:-0.02em;color:${v4.paper};font-feature-settings:"lnum","tnum";}
    .v4 .ex-card .un{font-family:${v4.serif};font-style:italic;font-size:14px;color:${v4.paperM};margin-top:2px;}
    .v4 .ex-card .arc{font-family:${v4.mono};font-size:11px;color:${v4.paperD};letter-spacing:0.08em;margin-top:14px;}
    .v4 .ex-card .ttl{font-family:${v4.serif};font-style:italic;font-size:15px;color:${v4.paper};margin-top:8px;}
    .v4 .ex-card .src{font-family:${v4.mono};font-size:10px;letter-spacing:0.16em;color:${v4.paperM};
      padding-top:18px;}

    /* DEADLINE BAND */
    .v4 .deadline{position:relative;z-index:1;padding:120px 56px;text-align:center;overflow:hidden;
      background:${v4.bg};border-top:1px solid ${v4.rule};border-bottom:1px solid ${v4.rule};}
    .v4 .deadline .glow{position:absolute;inset:0;background:radial-gradient(ellipse 70% 50% at 50% 30%, rgba(207,164,74,0.12), transparent 60%);pointer-events:none;}
    .v4 .deadline .orbits{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;pointer-events:none;}
    .v4 .deadline .orbits svg{width:1100px;height:1100px;animation:v4-orbit 90s linear infinite;opacity:0.16;}
    @keyframes v4-orbit{to{transform:rotate(360deg);}}
    .v4 .deadline > *{position:relative;z-index:1;}
    .v4 .deadline .k{font-family:${v4.mono};font-size:11px;letter-spacing:0.32em;color:${v4.ember};
      display:inline-flex;align-items:center;gap:14px;}
    .v4 .deadline .k::before,.v4 .deadline .k::after{content:"";width:36px;height:1px;background:${v4.ember};opacity:0.6;}
    .v4 .deadline h2{font-family:${v4.serif};font-size:84px;letter-spacing:-0.02em;line-height:1;
      margin:28px auto 0;max-width:18ch;font-weight:400;}
    .v4 .deadline h2 em{color:${v4.brass};font-style:italic;}
    .v4 .deadline .meter{margin:56px auto 0;max-width:880px;}
    .v4 .deadline .meter .l{font-family:${v4.mono};font-size:10px;letter-spacing:0.2em;color:${v4.paperM};
      display:flex;justify-content:space-between;text-transform:uppercase;}
    .v4 .deadline .meter .bar{margin-top:14px;height:2px;background:${v4.paperVD};position:relative;overflow:hidden;}
    .v4 .deadline .meter .bar .f{position:absolute;left:0;top:0;bottom:0;background:${v4.brass};
      box-shadow:0 0 12px ${v4.brass};}
    .v4 .deadline .meter .digits{display:flex;align-items:baseline;justify-content:center;gap:36px;margin-top:40px;}
    .v4 .deadline .meter .digits .d{text-align:center;}
    .v4 .deadline .meter .digits .v{font-family:${v4.serif};font-size:104px;line-height:1;letter-spacing:-0.02em;color:${v4.paper};font-feature-settings:"lnum","tnum";}
    .v4 .deadline .meter .digits .lbl{font-family:${v4.mono};font-size:10px;letter-spacing:0.24em;color:${v4.paperM};margin-top:6px;}
    .v4 .deadline .meter .digits .sep{font-family:${v4.serif};font-size:60px;color:${v4.brassD};}
    .v4 .deadline p{font-family:${v4.serif};font-style:italic;font-size:20px;color:${v4.paperD};max-width:50ch;margin:44px auto 0;}
    .v4 .deadline .trumpet-note{margin-top:22px;font-family:${v4.mono};font-size:10px;letter-spacing:0.22em;
      text-transform:uppercase;color:${v4.brass};opacity:0.85;}
    .v4 .deadline .ctas{margin-top:38px;display:flex;gap:12px;justify-content:center;}
    .v4 .btn{font-family:${v4.sans};font-size:12px;letter-spacing:0.18em;text-transform:uppercase;padding:16px 28px;border:1px solid ${v4.brass};background:${v4.brass};color:${v4.ink};cursor:pointer;transition:all .15s;}
    .v4 .btn:hover{background:${v4.paper};border-color:${v4.paper};transform:translateY(-1px);}
    .v4 .btn.ghost{background:transparent;color:${v4.brass};}
    .v4 .btn.ghost:hover{background:${v4.brass};color:${v4.ink};}

    /* TEACHINGS REEL */
    .v4 .reel{display:grid;grid-template-columns:repeat(4,1fr);gap:1px;background:${v4.rule};margin-top:40px;}
    .v4 .rcard{background:${v4.deep};display:flex;flex-direction:column;min-height:280px;cursor:pointer;
      transition:background .25s;position:relative;overflow:hidden;}
    .v4 .rcard:hover{background:${v4.bg};}
    .v4 .rcard .img{height:140px;background:linear-gradient(135deg, rgba(207,164,74,0.16), rgba(10,9,7,0.5)),${v4.bg};
      position:relative;border-bottom:1px solid ${v4.rule};overflow:hidden;}
    .v4 .rcard .img::before{content:"";position:absolute;inset:0;
      background-image:repeating-linear-gradient(45deg, transparent 0 8px, rgba(236,228,207,0.04) 8px 9px);
      transition:transform .8s;}
    .v4 .rcard:hover .img::before{transform:translateX(-12px);}
    .v4 .rcard .img .tag{position:absolute;top:12px;left:12px;font-family:${v4.mono};font-size:9px;letter-spacing:0.22em;color:${v4.paperM};}
    .v4 .rcard .body{padding:22px;flex:1;display:flex;flex-direction:column;justify-content:space-between;}
    .v4 .rcard h3{font-family:${v4.serif};font-size:22px;line-height:1.15;letter-spacing:-0.01em;color:${v4.paper};}
    .v4 .rcard .d{font-size:12px;color:${v4.paperM};margin-top:6px;}
    .v4 .rcard .go{margin-top:18px;font-family:${v4.mono};font-size:10px;letter-spacing:0.22em;color:${v4.brass};}

    /* RESOURCES */
    .v4 .resources{margin-top:40px;display:grid;grid-template-columns:1.6fr 1fr 1fr;gap:14px;}
    .v4 .res{padding:28px 26px;background:${v4.deep};border:1px solid ${v4.rule};min-height:240px;
      display:flex;flex-direction:column;justify-content:space-between;transition:border-color .2s,transform .25s;}
    .v4 .res:hover{border-color:${v4.brass};}
    .v4 .res .k{font-family:${v4.mono};font-size:10px;letter-spacing:0.18em;text-transform:uppercase;color:${v4.paperM};}
    .v4 .res h3{font-family:${v4.serif};font-weight:400;font-size:28px;margin-top:14px;color:${v4.paper};}
    .v4 .res .cnt{font-family:${v4.serif};font-size:64px;color:${v4.brass};line-height:1;font-feature-settings:"lnum","tnum";}
    .v4 .res p{font-size:13px;color:${v4.paperM};margin-top:8px;line-height:1.5;}
    .v4 .res .go{margin-top:18px;font-family:${v4.mono};font-size:11px;letter-spacing:0.16em;text-transform:uppercase;color:${v4.brass};}

    /* FOOTER */
    .v4 footer{padding:88px 56px 52px;background:${v4.deep};border-top:1px solid ${v4.rule};position:relative;}
    .v4 footer .verse{font-family:${v4.serif};font-style:italic;font-size:30px;line-height:1.4;max-width:34ch;color:${v4.paper};}
    .v4 footer .verse .ref{display:block;margin-top:18px;font-family:${v4.mono};font-style:normal;font-size:11px;letter-spacing:0.24em;color:${v4.brass};}
    .v4 footer .meta{margin-top:56px;display:flex;justify-content:space-between;font-size:11px;color:${v4.paperM};letter-spacing:0.08em;}
    .v4 footer .links{display:flex;gap:24px;}
    .v4 footer .links a:hover{color:${v4.brass};}

    /* ============ ORNAMENT DIVIDERS ============ */
    .v4 .ornament-div{display:flex;align-items:center;justify-content:center;gap:24px;
      padding:60px 56px;position:relative;}
    .v4 .ornament-div .line{flex:1;height:1px;background:linear-gradient(90deg,transparent,${v4.rule},transparent);}
    .v4 .ornament-div img{width:60px;height:60px;opacity:0.55;filter:sepia(1) hue-rotate(-10deg) saturate(0.8);}

    /* ============ BOOKS SHELF ============ */
    .v4 .books-section{padding:80px 56px 100px;position:relative;background:${v4.deep};
      border-top:1px solid ${v4.rule};border-bottom:1px solid ${v4.rule};}
    .v4 .books-section .header{max-width:760px;margin:0 auto 56px;text-align:center;}
    .v4 .books-section .sec-num{font-family:${v4.mono};font-size:10px;letter-spacing:0.32em;color:${v4.brass};margin-bottom:16px;}
    .v4 .books-section h2{font-family:${v4.serif};font-size:48px;letter-spacing:-0.015em;color:${v4.paper};margin:0 0 14px;}
    .v4 .books-section h2 em{color:${v4.brass};font-style:italic;}
    .v4 .books-section p{color:${v4.paperD};font-size:16px;line-height:1.6;max-width:56ch;margin:0 auto;}
    .v4 .books-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:32px;max-width:1280px;margin:0 auto;}
    .v4 .book{display:flex;flex-direction:column;gap:14px;text-decoration:none;color:inherit;
      transition:transform .25s;}
    .v4 .book:hover{transform:translateY(-6px);}
    .v4 .book .cover-wrap{aspect-ratio:2/3;background:#15110b;border:1px solid ${v4.rule};overflow:hidden;
      box-shadow:0 12px 28px rgba(0,0,0,0.5);transition:box-shadow .25s,border-color .25s;}
    .v4 .book:hover .cover-wrap{box-shadow:0 18px 36px rgba(0,0,0,0.65),0 0 0 1px ${v4.brass};border-color:${v4.brass};}
    .v4 .book .cover-wrap img{width:100%;height:100%;object-fit:cover;display:block;}
    .v4 .book .title{font-family:${v4.serif};font-size:16px;color:${v4.paper};line-height:1.3;text-align:center;}
    .v4 .book .cta{font-family:${v4.mono};font-size:10px;letter-spacing:0.22em;color:${v4.brass};text-align:center;}

    @media (max-width: 880px) {
      .v4 .books-grid{grid-template-columns:repeat(2,1fr);gap:22px;}
      .v4 .books-section{padding:60px 24px 80px;}
      .v4 .books-section h2{font-size:32px;}
      .v4 .ornament-div{padding:40px 24px;}
    }
  `;

  // ============================================================
  // DEADLINE TARGET — Feast of Trumpets (Yom Teruah) 2027
  // Tishri 1, 5788 — sunset Sept 11 → sunset Sept 12, 2027 by observation.
  // Using Sept 12 2027 as the target moment.
  // ============================================================
  const FEAST_TARGET = new Date('2027-09-12T18:00:00Z');
  const GEN_START    = new Date('1947-01-01T00:00:00Z');
  const GEN_END_FULL = +GEN_START + 80 * 365.25 * 86400000;

  function getCountdown() {
    const now = Date.now();
    const ms = FEAST_TARGET - now;
    const daysLeft = Math.max(0, Math.floor(ms / 86400000));
    const monthsLeft = Math.max(0, Math.floor(daysLeft / 30.44));
    const yearsElapsed = Math.max(0, Math.floor((now - +GEN_START) / (365.25 * 86400000)));
    const pctElapsed = Math.min(100, ((now - +GEN_START) / (GEN_END_FULL - +GEN_START)) * 100);
    return { daysLeft, monthsLeft, yearsElapsed, pctElapsed };
  }
  const COUNTDOWN = getCountdown();

  // ============================================================
  // Hooks
  // ============================================================
  function useInView(opts = { threshold: 0.18 }) {
    const ref = useRef(null);
    // Lazy init: if the document is hidden (preview iframe, prerender) start
    // already-seen so reveal content paints immediately without relying on
    // CSS transitions (which pause while the doc is hidden).
    const [seen, setSeen] = useState(() =>
      typeof document !== 'undefined' && document.hidden ? true : false
    );
    useEffect(() => {
      if (!ref.current) return;
      let fired = false;
      const fire = () => { if (!fired) { fired = true; setSeen(true); } };

      // Primary: IntersectionObserver. Some preview iframes are
      // visibility:hidden and IO callbacks never fire there.
      const io = new IntersectionObserver((entries) => {
        entries.forEach(e => {
          if (e.isIntersecting) { fire(); io.disconnect(); }
        });
      }, opts);
      io.observe(ref.current);

      // Safety net 1: if the document is hidden (preview iframe case), flip
      // immediately so content is visible.
      if (typeof document !== 'undefined' && document.hidden) {
        fire();
      }
      const onVis = () => { if (document.hidden) fire(); };
      document.addEventListener('visibilitychange', onVis);

      // Safety net 2: belt-and-suspenders timeout — guarantees nothing stays
      // invisible past 900ms regardless of IO behavior.
      const t = setTimeout(fire, 900);

      return () => {
        io.disconnect();
        clearTimeout(t);
        document.removeEventListener('visibilitychange', onVis);
      };
    }, []);
    return [ref, seen];
  }

  function useCountUp(target, run, duration = 1400) {
    const [v, setV] = useState(0);
    useEffect(() => {
      if (!run) return;
      // If rAF won't tick (hidden iframe, prerender, etc.) jump to the final.
      if (typeof document !== 'undefined' && document.hidden) {
        setV(target);
        return;
      }
      // Safety net: also force final value at 1.5x duration in case rAF stalls.
      const safety = setTimeout(() => setV(target), duration + 400);
      let raf;
      const start = performance.now();
      const tick = (now) => {
        const p = Math.min(1, (now - start) / duration);
        const eased = 1 - Math.pow(1 - p, 3);
        setV(Math.round(target * eased));
        if (p < 1) raf = requestAnimationFrame(tick);
      };
      raf = requestAnimationFrame(tick);
      return () => { cancelAnimationFrame(raf); clearTimeout(safety); };
    }, [run, target, duration]);
    return v;
  }

  // ============================================================
  // Topbar
  // ============================================================
  function Topbar() {
    return (
      <div className="topbar">
        <div className="wm">
          <div className="seal">✦</div>
          <div>
            <div className="name">{C.brand}</div>
            <span className="sub">est. {C.ministry}</span>
          </div>
        </div>
        <div className="topnav">
          {C.nav.slice(0,6).map(n => (
            <a key={n.label} href={n.href} {...(n.external ? {target:'_blank', rel:'noopener'} : {})}>
              {n.label}{n.external ? ' ↗' : ''}
            </a>
          ))}
          <a className="pill" href="/the-key.html">Free book</a>
        </div>
      </div>
    );
  }

  // ============================================================
  // Background — Stars (canvas), Sun (svg)
  // ============================================================
  function StarField() {
    const ref = useRef(null);
    useEffect(() => {
      const c = ref.current;
      if (!c) return;
      const dpr = window.devicePixelRatio || 1;
      let raf;
      let stars = [];
      let w = 0, h = 0;
      const resize = () => {
        const r = c.getBoundingClientRect();
        w = r.width; h = r.height;
        c.width = w * dpr; c.height = h * dpr;
        c.style.width = w + 'px'; c.style.height = h + 'px';
        const ctx = c.getContext('2d');
        ctx.setTransform(dpr,0,0,dpr,0,0);
        // generate ~140 stars
        const n = Math.max(80, Math.floor((w * h) / 7000));
        stars = Array.from({length:n}, () => ({
          x: Math.random()*w,
          y: Math.random()*h,
          r: Math.random()*1.2 + 0.2,
          tw: Math.random()*Math.PI*2,
          twS: 0.5 + Math.random()*1.5,
          drift: 0.04 + Math.random()*0.08,
          hue: Math.random() < 0.85 ? 'paper' : 'brass',
        }));
      };
      resize();
      window.addEventListener('resize', resize);

      const ctx = c.getContext('2d');
      let last = performance.now();
      const draw = (now) => {
        const dt = (now - last) / 1000; last = now;
        ctx.clearRect(0,0,w,h);
        for (const s of stars) {
          s.y -= s.drift;
          if (s.y < -2) { s.y = h + 2; s.x = Math.random()*w; }
          s.tw += dt * s.twS;
          const a = 0.35 + Math.sin(s.tw) * 0.45;
          const col = s.hue === 'brass'
            ? `rgba(231,198,116,${(a * 0.9).toFixed(3)})`
            : `rgba(236,228,207,${(a * 0.7).toFixed(3)})`;
          ctx.beginPath();
          ctx.arc(s.x, s.y, s.r, 0, Math.PI*2);
          ctx.fillStyle = col;
          ctx.fill();
        }
        raf = requestAnimationFrame(draw);
      };
      raf = requestAnimationFrame(draw);
      return () => { cancelAnimationFrame(raf); window.removeEventListener('resize', resize); };
    }, []);
    return <canvas ref={ref} className="stars"/>;
  }

  function SunBackdrop() {
    // SVG sun with slowly rotating ray spokes and a soft pulsing glow.
    return (
      <svg className="sun" viewBox="0 0 540 540" fill="none">
        <defs>
          <radialGradient id="sunGlow" cx="50%" cy="50%" r="50%">
            <stop offset="0%"  stopColor="#cfa44a" stopOpacity="0.9"/>
            <stop offset="40%" stopColor="#8b6c2c" stopOpacity="0.35"/>
            <stop offset="100%" stopColor="#8b6c2c" stopOpacity="0"/>
          </radialGradient>
          <radialGradient id="sunCore" cx="50%" cy="50%" r="50%">
            <stop offset="0%"  stopColor="#f3d27d"/>
            <stop offset="60%" stopColor="#cfa44a"/>
            <stop offset="100%" stopColor="#8b6c2c" stopOpacity="0"/>
          </radialGradient>
        </defs>
        {/* outer glow halo */}
        <circle cx="270" cy="270" r="260" fill="url(#sunGlow)" className="sun-pulse"/>
        {/* ray spokes — rotating */}
        <g className="sun-disc">
          {Array.from({length:24}).map((_,i) => {
            const a = (i / 24) * Math.PI * 2;
            const r1 = 88, r2 = 168;
            const x1 = 270 + Math.cos(a)*r1, y1 = 270 + Math.sin(a)*r1;
            const x2 = 270 + Math.cos(a)*r2, y2 = 270 + Math.sin(a)*r2;
            return <line key={i} x1={x1} y1={y1} x2={x2} y2={y2}
              stroke="#cfa44a" strokeWidth={i % 3 === 0 ? 1.2 : 0.6}
              opacity={i % 3 === 0 ? 0.55 : 0.28}/>;
          })}
          {/* inner ring */}
          <circle cx="270" cy="270" r="80" fill="none" stroke="#cfa44a" strokeWidth="1" opacity="0.4"/>
          <circle cx="270" cy="270" r="68" fill="url(#sunCore)" opacity="0.6"/>
        </g>
      </svg>
    );
  }

  function AmbientGlyphs() {
    // Slowly drifting Hebrew-shaped glyphs (low opacity) across the whole page.
    const glyphs = ['א','ב','ג','ד','ה','ו','ז','י','ל','מ','נ','ס','ע','פ','ק','ר','ש','ת','✦','✧','⊕'];
    const items = Array.from({length:14}).map((_,i) => ({
      ch: glyphs[i % glyphs.length],
      x: 5 + (i * 7.2) % 95,
      delay: -(i * 5.7) % 60,
      size: 90 + (i % 4) * 30,
    }));
    return (
      <div className="ambient">
        <div className="glyphs">
          {items.map((g, i) => (
            <span key={i} className="glyph" style={{
              left: g.x + '%',
              bottom: '-180px',
              fontSize: g.size + 'px',
              animationDelay: g.delay + 's',
            }}>{g.ch}</span>
          ))}
        </div>
      </div>
    );
  }

  // ============================================================
  // HERO — Stage
  // ============================================================
  function Stage() {
    return (
      <div className="stage">
        <div className="vignette"></div>
        <StarField/>
        <SunBackdrop/>
        <div className="noise"></div>
        <svg className="gate" viewBox="0 0 200 260" fill="none" preserveAspectRatio="xMidYMax meet">
          <defs>
            <linearGradient id="v4gateG" x1="0" y1="0" x2="0" y2="1">
              <stop offset="0" stopColor="#050403" stopOpacity="0"/>
              <stop offset="0.35" stopColor="#050403" stopOpacity="0.5"/>
              <stop offset="1" stopColor="#050403" stopOpacity="1"/>
            </linearGradient>
            <linearGradient id="v4slit" x1="0" y1="0" x2="0" y2="1">
              <stop offset="0"   stopColor="#f3d27d" stopOpacity="0.95"/>
              <stop offset="0.5" stopColor="#cfa44a" stopOpacity="0.7"/>
              <stop offset="1"   stopColor="#cfa44a" stopOpacity="0"/>
            </linearGradient>
          </defs>
          {/* doorframe */}
          <path d="M30 260 V120 Q30 60 100 60 Q170 60 170 120 V260 Z" fill="#15110b"/>
          {/* dark interior */}
          <path d="M52 260 V120 Q52 84 100 84 Q148 84 148 120 V260 Z" fill="#050403"/>
          {/* light slit — animated */}
          <rect x="98.5" y="84" width="3" height="176" fill="url(#v4slit)">
            <animate attributeName="opacity" values="0.7;1;0.7" dur="4s" repeatCount="indefinite"/>
          </rect>
          {/* bottom vignette so it blends */}
          <rect x="0" y="120" width="200" height="140" fill="url(#v4gateG)"/>
        </svg>

        <div className="stage-inner">
          <div className="h-meta">
            <span><b>{C.ministry}</b> · live</span>
            <span className="stamp"><span className="star">✱</span>{C.hero.eyebrow}<span className="star">✱</span></span>
            <span className="right"><b>Generation</b> · year {COUNTDOWN.yearsElapsed} of 80</span>
          </div>
          <div className="eyebrow">A WARNING FROM YAHUSHA THE MESSIAH</div>
          <h1 className="h-title">Ten prophecies.<br/><em>One</em> formula. <em>One</em> year.</h1>
          <p className="h-sub">{C.hero.sub}</p>
          <div className="hero-ctas">
            <a className="hero-cta primary" href={C.hero.cta_primary_href || '#message'}>{C.hero.cta_primary} →</a>
            <a className="hero-cta" href={C.hero.cta_secondary_href || '#'}>{C.hero.cta_secondary} ↓</a>
          </div>
        </div>
      </div>
    );
  }

  // ============================================================
  // SCRUBBER — piecewise compression so right cluster has room
  // ============================================================
  // 0.00 → 0.30 covers -500 BC → 33 AD     (events: -444, 33)
  // 0.30 → 0.55 covers 33 AD → 1500 AD     (event:  538)
  // 0.55 → 1.00 covers 1500 → 2050 AD      (events: 1780, 1947, 2027)
  const SCRUB_MIN = -600, SCRUB_MAX = 2050;
  function yearToPct(y) {
    if (y <= 33)   return ((y - SCRUB_MIN) / (33 - SCRUB_MIN)) * 32;
    if (y <= 1500) return 32 + ((y - 33) / (1500 - 33)) * 23;
    return 55 + ((y - 1500) / (SCRUB_MAX - 1500)) * 45;
  }
  function pctToYear(p) {
    if (p <= 32) return Math.round(SCRUB_MIN + (p/32) * (33 - SCRUB_MIN));
    if (p <= 55) return Math.round(33 + ((p-32)/23) * (1500 - 33));
    return Math.round(1500 + ((p-55)/45) * (SCRUB_MAX - 1500));
  }

  // Prophecies displayed as bracket-shaped connectors on the scrubber. Each
  // bracket rises out of the rail at start year, runs horizontally, and
  // returns to the rail at end year. Sort by horizontal span: shortest sits
  // closest to the rail, longest is tallest — so they nest cleanly and
  // never intersect.
  const PROPHECIES = [
    { id:'2520', label:'2,520 YEARS',     from:-537,  to:1947, tier:'primary'  },
    { id:'80',   label:'80 YEARS',        from:1947,  to:2027, tier:'deadline' },
    { id:'69w',  label:'69 WEEKS',        from:-444,  to:33,   tier:'confirm'  },
    { id:'2300', label:'2,300 DAYS',      from:-457,  to:1809, tier:'confirm'  },
    { id:'1260', label:'1,260 DAYS',      from:538,   to:1780, tier:'confirm'  },
    { id:'1290', label:'1,290 DAYS',      from:538,   to:1809, tier:'confirm'  },
    { id:'1335', label:'1,335 DAYS',      from:538,   to:1854, tier:'confirm'  },
    { id:'430',  label:'430 YEARS',       from:1517,  to:1947, tier:'confirm'  },
    { id:'70',   label:'70 YEARS',        from:1947,  to:2017, tier:'confirm'  },
    { id:'120j', label:'120 JUBILEES',    from:-3887, to:2027, tier:'confirm'  },
  ];

  // Precomputed sorted version: each prophecy gets a lane index based on
  // ascending visual span (% width on the compressed axis). Lane 0 = shortest
  // = shallowest depth.
  const SORTED_PROPHECIES = (() => {
    const withSpan = PROPHECIES.map(p => ({
      ...p,
      _spanPct: Math.abs(yearToPct(Math.min(p.to, SCRUB_MAX)) - yearToPct(Math.max(p.from, SCRUB_MIN))),
    }));
    withSpan.sort((a,b) => a._spanPct - b._spanPct);
    return withSpan.map((p, i) => ({ ...p, lane: i }));
  })();

  // Bracket depth = how far above the rail the horizontal bar sits.
  const BRACKET_BASE   = 14;
  const BRACKET_STEP   = 17;   // 10 lanes * 17 + 14 = 184px max
  const RAIL_Y         = 200;  // rail line top inside .scrub-rail

  function ProphecyBrackets({ year }) {
    const colorFor = t => t === 'primary' ? v4.ember : t === 'deadline' ? v4.paper : v4.brass;
    return (
      <div className="scrub-brackets" aria-hidden="true">
        {SORTED_PROPHECIES.map(p => {
          const fromY = Math.max(p.from, SCRUB_MIN);
          const toY   = Math.min(p.to,   SCRUB_MAX);
          const sX = yearToPct(fromY);
          const eX = yearToPct(toY);
          const depth = BRACKET_BASE + p.lane * BRACKET_STEP;
          const barTop = RAIL_Y - depth;
          const color = colorFor(p.tier);

          const started   = year >= p.from;
          const completed = year >= p.to;

          // Right edge of the live bar: tracks the handle while in-progress,
          // snaps to end-x when completed.
          const handleX = yearToPct(Math.min(Math.max(year, fromY), toY));
          const rightEdge = completed ? eX : (started ? handleX : sX);
          const hLeft  = Math.min(sX, rightEdge);
          const hWidth = Math.max(0, Math.abs(rightEdge - sX));

          return (
            <React.Fragment key={p.id}>
              {/* always-on faint TRACK so the structure of all 10 brackets
                  is visible from the start */}
              <div className="b-track" style={{
                top: barTop + 'px', left: sX + '%', width: (eX - sX) + '%',
                ['--c']: color,
              }}/>
              <div className="b-track l" style={{
                top: barTop + 'px', height: depth + 'px', left: sX + '%',
                ['--c']: color, transform: 'translateX(-50%)',
              }}/>
              <div className="b-track r" style={{
                top: barTop + 'px', height: depth + 'px', left: eX + '%',
                ['--c']: color, transform: 'translateX(-50%)',
              }}/>

              {/* LEFT STEM lights once the handle has reached the start */}
              {started && (
                <div className="b-stem" style={{
                  top: barTop + 'px', height: depth + 'px', left: sX + '%',
                  ['--c']: color,
                }}/>
              )}
              {/* HORIZONTAL extends with the handle while in-progress */}
              {started && (
                <div className="b-bar" style={{
                  top: (barTop - 0.5) + 'px', left: hLeft + '%', width: hWidth + '%',
                  ['--c']: color,
                }}/>
              )}
              {/* RIGHT STEM only after completion */}
              {completed && (
                <div className="b-stem" style={{
                  top: barTop + 'px', height: depth + 'px', left: eX + '%',
                  ['--c']: color,
                }}/>
              )}
              {/* END DOT pops at the rail */}
              <div className={'b-dot ' + (completed ? 'lit' : '')} style={{
                top: RAIL_Y + 'px', left: eX + '%',
                ['--c']: color,
              }}/>
              {/* LABEL on the horizontal */}
              <div className={'b-label ' + (completed ? 'lit' : '')} style={{
                top: (barTop - 14) + 'px', left: ((sX + eX) / 2) + '%',
                ['--c']: color,
              }}>{p.label}</div>
            </React.Fragment>
          );
        })}
      </div>
    );
  }

  function Scrubber({ year, setYear }) {
    const rail = useRef(null);
    const drag = useRef(false);    const onPoint = (cx) => {
      const r = rail.current.getBoundingClientRect();
      const p = Math.max(0, Math.min(100, ((cx - r.left) / r.width) * 100));
      setYear(pctToYear(p));
    };
    useEffect(() => {
      const m = e => { if (drag.current) onPoint(e.clientX); };
      const u = () => { drag.current = false; };
      window.addEventListener('mousemove', m);
      window.addEventListener('mouseup', u);
      return () => { window.removeEventListener('mousemove', m); window.removeEventListener('mouseup', u); };
    }, []);

    const pct = yearToPct(year);
    const era = year < 0 ? `${Math.abs(year)} BC` : `${year} AD`;

    // Find the nearest anchor to show in the readout
    const nearest = useMemo(() => {
      let best = null, bd = Infinity;
      for (const t of C.timeline) {
        const d = Math.abs(t.year - year);
        if (d < bd) { bd = d; best = t; }
      }
      return { t: best, d: bd };
    }, [year]);
    const showNearestTitle = nearest && nearest.d <= 60;

    // Minor ticks (every 100y from -600 to 2000)
    const minorTicks = [];
    for (let y = -600; y <= 2000; y += 100) minorTicks.push(y);
    const majorTicks = [-500, 0, 500, 1000, 1500, 2000];

    // Stagger label levels for anchors so adjacent ones don't collide.
    // 537 BC / 444 BC are close at the left; 1780 / 1947 / 2027 cluster
    // at the right. Alternate levels in each cluster.
    const levelFor = (yy) => {
      if (yy === -537) return 1;
      if (yy === -444) return 0;
      if (yy === 1780) return 0;
      if (yy === 1947) return 1;
      if (yy === 2027) return 0;
      return 0;
    };

    return (
      <div className="scrubber">
        <div className="scrub-head">
          <span className="lbl">THE HISTORICAL ARC · SECONDARY</span>
          <span>{era === '0 AD' ? 'AD/BC' : era} · {year >= 1947 ? 'IN THE GENERATION' : year >= 33 ? 'AFTER THE CROSS' : 'BEFORE THE CROSS'}</span>
        </div>
        <div className="scrub-rail" ref={rail}
          onMouseDown={e => { drag.current = true; onPoint(e.clientX); }}>
          <div className="scrub-line"></div>
          <div className="scrub-fill" style={{ width: pct + '%' }}></div>

          {/* minor ticks */}
          {minorTicks.map(y => (
            <div key={'mt'+y} className={'scrub-tick ' + (majorTicks.includes(y) ? 'major' : '')}
              style={{ left: yearToPct(y) + '%' }}/>
          ))}
          {majorTicks.map(y => (
            <div key={'mtl'+y} className="scrub-tickL" style={{ left: yearToPct(y) + '%' }}>
              {y === 0 ? '0' : y < 0 ? `${Math.abs(y)} BC` : `${y}`}
            </div>
          ))}

          {/* anchors */}
          {C.timeline.map(t => {
            const p = yearToPct(t.year);
            const passed = t.year <= year;
            const isAnchor = t.side === 'anchor' || t.side === 'end';
            const active = nearest.t && nearest.t.year === t.year && nearest.d <= 60;
            return (
              <React.Fragment key={t.year}>
                <div className={'scrub-mark ' + (passed?'passed ':'') + (isAnchor?'anchor':'')} style={{left: p+'%'}}></div>
                <div className={'scrub-anchor-label lvl' + levelFor(t.year) + (active ? ' active' : '')}
                  style={{ left: p+'%' }}>
                  {t.label}
                </div>
              </React.Fragment>
            );
          })}

          {/* prophecy bracket connectors — nest by length, animate with handle */}
          <ProphecyBrackets year={year}/>

          {/* draggable handle (short tick on the rail) */}
          <div className="scrub-handle" style={{left: pct+'%'}}/>
          {/* readout pinned above the brackets stack, tracks the handle X */}
          <div className="scrub-readout" style={{left: pct+'%'}}>
            <div className="y">{era}</div>
            <div className="ttl">{showNearestTitle ? nearest.t.title : '\u00A0'}</div>
            <div className="e">{year >= 1947 ? 'IN THE GENERATION' : year >= 33 ? 'AFTER THE CROSS' : 'BEFORE THE CROSS'}</div>
          </div>

          {/* prophecy connector stack — lines populate as slider crosses each end-date */}
        </div>
      </div>
    );
  }

  function FormulaBar() {
    return (
      <div className="formbar">
        <div className="lbl">The Solar Key</div>
        <div className="formula">
          <span>360</span><span className="op">÷</span><span>365.25</span><span className="op">=</span><span className="res">0.98563</span>
        </div>
        <div className="meta">Multiply any prophetic span by 0.98563 to land on the verified historical date.</div>
      </div>
    );
  }

  // ============================================================
  // MESSAGE — the theology lead. Three doctrinal pillars before any math.
  // ============================================================
  function Message() {
    const [ref, seen] = useInView();
    const msgs = [
      {
        ref: 'MATTHEW 7:13–14',
        title: <>The <em>gate</em> is the Messiah.</>,
        quote: '“Enter ye in at the strait gate … because strait is the gate, and narrow is the way which leadeth unto life, and few there be that find it.”',
        body: 'There is one gate, one name, one way. Yahusha the Messiah is the door of the sheep — every other path is a thief and a robber. Salvation is by His blood, not by your works; entered through repentance and obedience, not philosophy.'
      },
      {
        ref: 'MATTHEW 24:32–34',
        title: <>The <em>fig tree</em> has put forth her leaves.</>,
        quote: '“When ye shall see all these things, know that it is near, even at the doors. Verily I say unto you, This generation shall not pass, till all these things be fulfilled.”',
        body: 'Israel was reborn in 1947 — the fig tree budded. A generation is eighty years at the outside (Psalm 90:10). We are inside the final generation Yahusha sealed when He spoke. The deadline is not negotiable.'
      },
      {
        ref: 'AMOS 3:7 · ACTS 4:12',
        title: <>Yahuah <em>reveals</em> His secret.</>,
        quote: '“Surely Yahuah will do nothing, but He revealeth His secret unto His servants the prophets.”',
        body: 'We restore the Father’s name (Yahuah) and the Son’s name (Yahusha) — there is no other name under heaven given among men whereby we must be saved. The prophets named the day. The Spirit has not left the church without witness.'
      },
    ];
    return (
      <div className="section" id="message" ref={ref}>
        <div className="gutter">{Array.from({length:18}).map((_,i)=><div key={i}>{String(i+1).padStart(3,'0')}</div>)}</div>
        <div className="body">
          <div className={'reveal ' + (seen ? 'in' : '')}>
            <div className="sec-num">§ 01 · THE MESSAGE</div>
            <h2 className="sec-h">Three things before <em>anything</em> else.</h2>
            <p className="sec-lead">This site is not a math problem with scripture decoration. It is a warning from Yahusha the Messiah — spoken plainly two thousand years ago and dated, by His own words, to the generation that saw the fig tree bud. The numbers underneath simply confirm what the prophets already said.</p>
          </div>
          <div className="message-grid">
            {msgs.map((m,i) => (
              <div className={'msg reveal d' + (i+2) + ' ' + (seen ? 'in' : '')} key={i}>
                <div className="ref">{m.ref}</div>
                <h3>{m.title}</h3>
                <div className="quote">{m.quote}</div>
                <p>{m.body}</p>
              </div>
            ))}
          </div>
        </div>
      </div>
    );
  }

  // ============================================================
  // PILLARS — with count-up
  // ============================================================
  function PillarBN({ n, run }) {
    const v = useCountUp(n, run);
    return <div className="bn">{v.toLocaleString()}</div>;
  }

  function Pillars() {
    const [ref, seen] = useInView();
    return (
      <div className="section" id="proof" ref={ref}>
        <div className="gutter">{Array.from({length:22}).map((_,i)=><div key={i}>{String(i+19).padStart(3,'0')}</div>)}</div>
        <div className="body">
          <div className={'reveal ' + (seen ? 'in' : '')}>
            <div className="sec-num">§ 03 · THE PATTERN IN THREE STEPS</div>
            <h2 className="sec-h">A <em>primary</em> prophecy. A deadline. And a <em>method-verifier</em>.</h2>
            <p className="sec-lead">Three load-bearing calculations. The 2,520-year prophecy identifies the year of restoration. The fig-tree generation sets the maximum window. The 69 weeks to Messiah confirm that the Solar Key conversion method produces verified history.</p>
          </div>
          <div className="steps">
            {C.pillars.map((p,i) => (
              <div className={'step reveal d' + (i+2) + ' ' + (seen ? 'in' : '')} key={i}>
                <div className="h">
                  <span>STEP {String(i+1).padStart(2,'0')}</span>
                  <span className="role">{p.kicker.toUpperCase()}</span>
                </div>
                <PillarBN n={p.n} run={seen}/>
                <div className="unit">{p.unit}</div>
                <div className="src">{p.sub.toUpperCase()}</div>
                <div className="eq">
                  {i === 0 && <>537 BC <span className="arrow">+</span> 2520 × 0.98563 = 2483y <span className="arrow">→</span> <span className="res">1947 AD</span></>}
                  {i === 1 && <>1947 <span className="arrow">+</span> 80 <span className="arrow">→</span> <span className="res">2027 AD</span></>}
                  {i === 2 && <>444 BC <span className="arrow">+</span> 483 × 0.98563 = 476y <span className="arrow">→</span> <span className="res">33 AD ✓</span></>}
                </div>
                <p>{p.body}</p>
                {p.shadow ? <div className="shadow">{p.shadow}</div> : null}
              </div>
            ))}
          </div>
        </div>
      </div>
    );
  }

  // ============================================================
  // CALCULATOR
  // ============================================================
  const PRESETS = [
    { id:'2520', label:'2520y · Lev 26',     years:2520, fromY:-537, expected:1947 },
    { id:'69w',  label:'69w · Daniel 9',     years:483,  fromY:-444, expected:33 },
    { id:'2300', label:'2300d · Dan 8:14',   dayCount:2300, fromY:-457, expected:1809, days:true },
    { id:'1260', label:'1260d · Rev 12',     dayCount:1260, fromY:538,  expected:1780, days:true },
    { id:'1290', label:'1290d · Dan 12:11',  dayCount:1290, fromY:538,  expected:1809, days:true },
    { id:'1335', label:'1335d · Dan 12:12',  dayCount:1335, fromY:538,  expected:1854, days:true },
    { id:'430',  label:'430y · Exodus 12',   years:430,  fromY:1517, expected:1947 },
  ];

  function Calculator() {
    const [presetId, setPresetId] = useState('2520');
    const preset = PRESETS.find(p => p.id === presetId);
    const [startYear, setStartYear] = useState(preset.fromY);
    // Day-for-year principle (Ezekiel 4:6, Numbers 14:34):
    //   1 prophetic day = 1 prophetic year. They are the same count, different label.
    const [propYears, setPropYears] = useState(preset.years || preset.dayCount);
    const [propDays, setPropDays]   = useState(preset.dayCount || preset.years);
    const [mode, setMode] = useState(preset.days ? 'days' : 'years');

    useEffect(() => {
      const p = PRESETS.find(x => x.id === presetId);
      if (!p) return;
      setStartYear(p.fromY);
      setMode(p.days ? 'days' : 'years');
      if (p.days) { setPropDays(p.dayCount); setPropYears(p.dayCount); }
      else        { setPropYears(p.years);   setPropDays(p.years);    }
    }, [presetId]);

    // In day mode: 1260 days → 1260 prophetic years (day-for-year principle)
    // In year mode: input is already prophetic years
    const propeticYears = mode === 'days' ? propDays : propYears;
    const solarYears = propeticYears * (360/365.25);
    const result = Math.round(startYear + solarYears);
    const fmt = y => y < 0 ? `${Math.abs(y)} BC` : `${y} AD`;
    const matches = preset && Math.abs(result - preset.expected) <= 1;

    return (
      <div className="calc-card" id="calculator">
        <div className="calc-head">
          <span><span className="led"></span>SOLAR KEY CALCULATOR · LIVE</span>
          <span>360 ÷ 365.25 = 0.98563</span>
        </div>
        <div className="calc-body">
          <div className="calc-col">
            <label>Start year</label>
            <div className="field">
              <input type="number" className="short" value={startYear}
                onChange={e => setStartYear(parseInt(e.target.value || '0', 10))}/>
              <span className="unit">negative = BC</span>
            </div>
            <label>Prophetic span</label>
            <div style={{display:'flex',gap:8,marginBottom:8}}>
              <button className={'preset ' + (mode==='years'?'active':'')} onClick={() => setMode('years')}>Years</button>
              <button className={'preset ' + (mode==='days'?'active':'')} onClick={() => setMode('days')}>Days</button>
            </div>
            {mode === 'years' ? (
              <div className="field">
                <input type="number" value={propYears} onChange={e => setPropYears(parseFloat(e.target.value || '0'))}/>
                <span className="unit">prophetic years (360-day cycle)</span>
              </div>
            ) : (
              <div className="field">
                <input type="number" value={propDays} onChange={e => setPropDays(parseFloat(e.target.value || '0'))}/>
                <span className="unit">prophetic days → 1 day = 1 year</span>
              </div>
            )}
          </div>
          <div className="calc-divider"></div>
          <div className="calc-out">
            <div>
              {mode === 'days' ? (
                <>
                  <div className="stage-l">STEP 01 · PROPHETIC SPAN</div>
                  <div className="eq"><span className="v">{propDays.toLocaleString()}</span> prophetic days</div>

                  <div className="stage-l" style={{marginTop:14}}>STEP 02 · DAY-FOR-YEAR (Ezek 4:6 · Num 14:34)</div>
                  <div className="eq">
                    {propDays.toLocaleString()} days × <span style={{opacity:0.7}}>(1 day = 1 year)</span> = <span className="v">{propeticYears.toLocaleString()}</span> prophetic years
                  </div>

                  <div className="stage-l" style={{marginTop:14}}>STEP 03 · APPLY SOLAR KEY (× 360 ÷ 365.25)</div>
                  <div className="eq">
                    {propeticYears.toLocaleString()} × 0.98563 = <span className="v">{solarYears.toFixed(2)}</span> solar years
                  </div>
                </>
              ) : (
                <>
                  <div className="stage-l">STEP 01 · PROPHETIC SPAN</div>
                  <div className="eq"><span className="v">{propeticYears.toLocaleString()}</span> prophetic years <span style={{opacity:0.55}}>(360-day cycle)</span></div>

                  <div className="stage-l" style={{marginTop:14}}>STEP 02 · APPLY SOLAR KEY (× 360 ÷ 365.25)</div>
                  <div className="eq">
                    {propeticYears.toLocaleString()} × 0.98563 = <span className="v">{solarYears.toFixed(2)}</span> solar years
                  </div>
                </>
              )}
            </div>
            <div className="result-box">
              <div>
                <div className="l">START</div>
                <div className="y" style={{fontSize:28}}>{fmt(startYear)}</div>
              </div>
              <div style={{fontFamily:v4.mono,fontSize:11,color:'rgba(10,9,7,0.45)',position:'relative',zIndex:1}}>⟶</div>
              <div style={{textAlign:'right'}}>
                <div className="l">LANDS ON</div>
                <div className="y">{fmt(result)}{matches ? <em> ✓</em> : ''}</div>
              </div>
            </div>
          </div>
        </div>
        <div className="preset-row">
          <span>PRESETS:</span>
          {PRESETS.map(p => (
            <button key={p.id} className={'preset ' + (p.id === presetId ? 'active' : '')} onClick={() => setPresetId(p.id)}>{p.label}</button>
          ))}
        </div>
      </div>
    );
  }

  // ============================================================
  // CHEAT SHEET — all ten prophecies, pre-computed
  // ============================================================
  const CHEAT_SHEET = [
    { id:1,  span:'2,520 years',           conv:{type:'mul', solar:'2,483 y'}, from:'537 BC',  to:'1947 AD', src:'Leviticus 26',  tier:'primary'  },
    { id:2,  span:'80 years',              conv:{type:'lit'},                  from:'1947',    to:'2027 AD', src:'Matthew 24:34', tier:'deadline' },
    { id:3,  span:'69 weeks \u00b7 483 y', conv:{type:'mul', solar:'476 y'},   from:'444 BC',  to:'33 AD',   src:'Daniel 9',      tier:'confirm'  },
    { id:4,  span:'2,300 days',            conv:{type:'mul', solar:'2,267 y'}, from:'457 BC',  to:'1809 AD', src:'Daniel 8:14',   tier:'confirm'  },
    { id:5,  span:'1,260 days',            conv:{type:'mul', solar:'1,242 y'}, from:'538',     to:'1780 AD', src:'Revelation 12', tier:'confirm'  },
    { id:6,  span:'1,290 days',            conv:{type:'mul', solar:'1,272 y'}, from:'538',     to:'1809 AD', src:'Daniel 12:11',  tier:'confirm'  },
    { id:7,  span:'1,335 days',            conv:{type:'mul', solar:'1,316 y'}, from:'538',     to:'1854 AD', src:'Daniel 12:12',  tier:'confirm'  },
    { id:8,  span:'430 years',             conv:{type:'lit'},                  from:'1517',    to:'1947 AD', src:'Exodus 12:40',  tier:'confirm'  },
    { id:9,  span:'70 years',              conv:{type:'lit'},                  from:'1947',    to:'2017',    src:'Jeremiah 25:11',tier:'confirm'  },
    { id:10, span:'120 jubilees \u00b7 6,000 y', conv:{type:'mul', solar:'5,914 y'}, from:'~3887 BC', to:'2027 AD', src:'Genesis 6:3', tier:'confirm'  },
  ];

  function CheatSheet() {
    return (
      <div className="cheat-card">
        <div className="cheat-head">
          <span><span className="ttl">CHEAT SHEET</span> · TEN PROPHECIES, ALREADY CONVERTED</span>
          <span>360 ÷ 365.25 = 0.98563</span>
        </div>
        <div className="cheat-row head">
          <div>№</div>
          <div>PROPHETIC SPAN</div>
          <div>SOLAR KEY APPLIED</div>
          <div>LANDS ON</div>
          <div className="src">SOURCE</div>
        </div>
        {CHEAT_SHEET.map(r => (
          <div className={'cheat-row r-' + r.tier} key={r.id}>
            <div className="idx">№ {String(r.id).padStart(2,'0')}</div>
            <div className="span">{r.span}</div>
            <div className="conv">
              {r.conv.type === 'lit'
                ? <span className="lit">literal · no conversion</span>
                : <span>× 0.98563 = <span className="v">{r.conv.solar}</span></span>
              }
            </div>
            <div className="lands">{r.from} ⟶ <span className="land-res">{r.to}</span></div>
            <div className="src">{r.src.toUpperCase()}</div>
          </div>
        ))}
      </div>
    );
  }

  // ============================================================
  // FIG. 1 — convergence
  // ============================================================
  const CALCS = [
    { from:-537, to:1947, n:'2,520', u:'years',    tier:'primary',  ttl:'The Primary Prophecy',  src:'Leviticus 26' },
    { from:1947, to:2027, n:'80',    u:'years',    tier:'deadline', ttl:'The Fig-Tree Deadline', src:'Matthew 24:34' },
    { from:-444, to:33,   n:'69',    u:'weeks',    tier:'confirm',  ttl:'69 Weeks to Messiah',   src:'Daniel 9' },
    { from:-457, to:1809, n:'2,300', u:'days',     tier:'confirm',  ttl:'Sanctuary Cleansing',   src:'Daniel 8:14' },
    { from:538,  to:1780, n:'1,260', u:'days',     tier:'confirm',  ttl:'Time, Times, Half-Time',src:'Revelation 12' },
    { from:538,  to:1809, n:'1,290', u:'days',     tier:'confirm',  ttl:'Daily Sacrifice Taken', src:'Daniel 12:11' },
    { from:538,  to:1854, n:'1,335', u:'days',     tier:'confirm',  ttl:'The Blessed Day',       src:'Daniel 12:12' },
    { from:1517, to:1947, n:'430',   u:'years',    tier:'confirm',  ttl:'The Reformation Cycle', src:'Exodus 12:40' },
    { from:1947, to:2017, n:'70',    u:'years',    tier:'confirm',  ttl:'Jubilee Confirmation',  src:'Jeremiah 25:11' },
    { from:-2000,to:2027, n:'120',   u:'jubilees', tier:'confirm',  ttl:'6,000-Year Plan',       src:'Genesis 6:3' },
  ];

  function Fig1() {
    const [playKey, setPlayKey] = useState(0);
    const [hover, setHover] = useState(null);
    const W = 1180, H = 580;
    const PAD_L = 130, PAD_R = 80, PAD_T = 84, PAD_B = 80;
    const innerW = W - PAD_L - PAD_R, innerH = H - PAD_T - PAD_B;
    const minY = -2050, maxY = 2050;
    const xRaw = y => (y - minY) / (maxY - minY);
    const compress = u => {
      const c = (u - 0.5) * 1.6;
      return 0.5 + Math.tanh(c) / Math.tanh(0.8) * 0.5;
    };
    const xOf = y => PAD_L + compress(xRaw(y)) * innerW;
    const conv = xOf(2027);
    const rows = CALCS.length;
    const rowH = innerH / (rows + 1);
    const colors = { primary: v4.ember, deadline: v4.paper, confirm: v4.brass };

    const ticks = [-2000, -1000, -500, 0, 500, 1000, 1500, 2000];
    const tickLabel = y => y === 0 ? 'AD/BC' : y < 0 ? `${Math.abs(y)} BC` : `${y} AD`;

    return (
      <div className="fig">
        <div className="head">
          <span><span className="ttl">FIG. 1</span> · CONVERGENCE OF TEN PROPHETIC TIMELINES ONTO AD 2027</span>
          <div className="controls">
            <span>n = 10</span>
            <button className="play" onClick={() => { setPlayKey(k => k+1); setHover(null); }}>↻ REPLAY</button>
          </div>
        </div>
        <div className="chart-wrap">
          <svg viewBox={`0 0 ${W} ${H}`} key={playKey} style={{overflow:'visible'}}>
            <defs>
              <radialGradient id="v4conv" cx="50%" cy="50%" r="50%">
                <stop offset="0%"  stopColor={v4.brass} stopOpacity="0.9"/>
                <stop offset="60%" stopColor={v4.brass} stopOpacity="0.2"/>
                <stop offset="100%" stopColor={v4.brass} stopOpacity="0"/>
              </radialGradient>
              <linearGradient id="v4axisG" x1="0" y1="0" x2="1" y2="0">
                <stop offset="0" stopColor="rgba(236,228,207,0.05)"/>
                <stop offset="0.5" stopColor="rgba(236,228,207,0.18)"/>
                <stop offset="1" stopColor="rgba(207,164,74,0.6)"/>
              </linearGradient>
            </defs>

            <circle cx={conv} cy={PAD_T + innerH/2} r="180" fill="url(#v4conv)" opacity="0.55"/>

            <circle className="conv-pulse"    cx={conv} cy={PAD_T + innerH/2} r="14" fill="none" stroke={v4.brass} strokeWidth="1.2"/>
            <circle className="conv-pulse p2" cx={conv} cy={PAD_T + innerH/2} r="14" fill="none" stroke={v4.brass} strokeWidth="1.2"/>
            <circle className="conv-pulse p3" cx={conv} cy={PAD_T + innerH/2} r="14" fill="none" stroke={v4.brass} strokeWidth="1.2"/>
            <circle className="conv-core" cx={conv} cy={PAD_T + innerH/2} r="6" fill={v4.brass}/>

            <line x1={PAD_L} x2={W-PAD_R} y1={H-PAD_B+1} y2={H-PAD_B+1} stroke="url(#v4axisG)" strokeWidth="1"/>

            {ticks.map(y => (
              <g key={y}>
                <line x1={xOf(y)} x2={xOf(y)} y1={PAD_T} y2={H-PAD_B} stroke="rgba(236,228,207,0.04)" strokeWidth="1"/>
                <line x1={xOf(y)} x2={xOf(y)} y1={H-PAD_B} y2={H-PAD_B+8} stroke="rgba(236,228,207,0.25)" strokeWidth="1"/>
                <text x={xOf(y)} y={H-PAD_B+24} textAnchor="middle" fontFamily={v4.mono} fontSize="10" fill={v4.paperM} letterSpacing="0.06em">
                  {tickLabel(y)}
                </text>
              </g>
            ))}

            <line x1={xOf(0)} x2={xOf(0)} y1={PAD_T} y2={H-PAD_B} stroke={v4.rule} strokeWidth="1" strokeDasharray="1 3"/>
            <line x1={xOf(1947)} x2={xOf(1947)} y1={PAD_T} y2={H-PAD_B} stroke={v4.paperVD} strokeWidth="1" strokeDasharray="3 4"/>
            <text x={xOf(1947)} y={PAD_T-32} textAnchor="middle" fontFamily={v4.mono} fontSize="10" fill={v4.paperD} letterSpacing="0.16em">1947 · ISRAEL REBORN</text>
            <text x={conv} y={PAD_T-12} textAnchor="middle" fontFamily={v4.mono} fontSize="11" fill={v4.brass} letterSpacing="0.22em" fontWeight="500">2027 · CONVERGENCE</text>

            {CALCS.map((c, i) => {
              const y = PAD_T + (i + 1) * rowH;
              const x1 = xOf(c.from), x2 = xOf(c.to);
              const length = Math.abs(x2 - x1);
              const color = colors[c.tier];
              const isDim = hover !== null && hover !== i;
              const d = (i * 0.12).toFixed(2) + 's';
              const d2 = (i * 0.12 + 0.6).toFixed(2) + 's';
              const w = c.tier === 'primary' ? 2.4 : c.tier === 'deadline' ? 2.2 : 1.6;
              return (
                <g key={i} className={'arc-row' + (isDim ? ' dim' : '')}
                  onMouseEnter={() => setHover(i)}
                  onMouseLeave={() => setHover(null)}>
                  <line x1={x1} x2={x2} y1={y} y2={y} stroke="transparent" strokeWidth="24"/>
                  <line className="arc-line draw"
                    x1={x1} x2={x2} y1={y} y2={y}
                    stroke={color} strokeWidth={w} opacity={c.tier === 'confirm' ? 0.85 : 1}
                    style={{ strokeDasharray: length, '--len': length, '--d': d }}
                  />
                  <circle className="arc-dot" cx={x1} cy={y} r="2.5" fill={color} style={{ '--d2': d2 }}/>
                  <circle className="arc-end arc-dot" cx={x2} cy={y} r="4" fill={color} stroke={v4.deep} strokeWidth="1.5" style={{ '--d2': d2 }}/>
                  <text className="arc-dot" x={PAD_L - 12} y={y + 3} textAnchor="end"
                    fontFamily={v4.mono} fontSize="11" fill={v4.paperM} letterSpacing="0.06em"
                    style={{ '--d2': d2 }}>
                    {c.n} {c.u}
                  </text>
                </g>
              );
            })}

            <text x={W/2} y={H-10} textAnchor="middle" fontFamily={v4.mono} fontSize="9" fill={v4.paperVD} letterSpacing="0.32em">
              HISTORICAL TIME · ANNO DOMINI
            </text>
          </svg>

          {hover !== null && (() => {
            const c = CALCS[hover];
            const y = PAD_T + (hover + 1) * rowH;
            const tx = (xOf(c.from) + xOf(c.to)) / 2;
            const ty = y - 14;
            return (
              <div className="tooltip" style={{
                left: (tx / W) * 100 + '%',
                top:  Math.max(8, ty) / H * 100 + '%',
              }}>
                <div className="m">№ {String(hover+1).padStart(2,'0')} · {c.tier === 'primary' ? 'PRIMARY' : c.tier === 'deadline' ? 'DEADLINE' : 'CONFIRMS'}</div>
                <div className="t">{c.ttl}</div>
                <div className="a">{c.from < 0 ? `${Math.abs(c.from)} BC` : `${c.from} AD`} ⟶ {c.to < 0 ? `${Math.abs(c.to)} BC` : `${c.to} AD`}</div>
                <div className="src">{c.n} {c.u} · {c.src}</div>
              </div>
            );
          })()}
        </div>

        <div className="legend">
          <div className="d"><span className="sw" style={{background:v4.ember}}></span>Primary prophecy</div>
          <div className="d"><span className="sw" style={{background:v4.paper}}></span>The deadline</div>
          <div className="d"><span className="sw" style={{background:v4.brass,opacity:0.85}}></span>Eight confirmations</div>
          <div className="d" style={{marginLeft:'auto'}}><span className="swc" style={{background:v4.brass,boxShadow:'0 0 8px '+v4.brass}}></span>Convergence · AD 2027</div>
        </div>
      </div>
    );
  }

  function CalcSection() {
    const [ref, seen] = useInView();
    return (
      <div className="section" ref={ref}>
        <div className="gutter">{Array.from({length:30}).map((_,i)=><div key={i}>{String(i+23).padStart(3,'0')}</div>)}</div>
        <div className="body">
          <div className={'reveal ' + (seen ? 'in' : '')}>
            <div className="sec-num">§ 04 · VERIFY IT YOURSELF</div>
            <h2 className="sec-h">Plug in <em>any</em> prophetic span. Watch the year resolve.</h2>
            <p className="sec-lead">The Solar Key is a single ratio applied to every prophetic timeline in Scripture. Use the presets to walk through each of the ten calculations, or enter your own start year to see how the ratio lands.</p>
            <a className="m27" href="/prophecy-timeline-comprehensive.html">
              <span className="label">Deep dive</span>
              <span className="body">The full chronology, every cross-reference, every working — verse by verse — across the ten prophecies.</span>
              <span className="link">See the comprehensive timeline →</span>
            </a>
          </div>
          <div className={'reveal d1 ' + (seen ? 'in' : '')}><Calculator/></div>
          <div className={'reveal d2 ' + (seen ? 'in' : '')}><CheatSheet/></div>
          <div className={'reveal d3 ' + (seen ? 'in' : '')}><Fig1/></div>
        </div>
      </div>
    );
  }

  // ============================================================
  // EXHIBIT GRID
  // ============================================================
  function Exhibit() {
    const [ref, seen] = useInView();
    const tagFor = t => t === 'primary' ? 'PRIMARY' : t === 'deadline' ? 'DEADLINE' : 'CONFIRMS';
    return (
      <div className="section" id="calculations" ref={ref}>
        <div className="gutter">{Array.from({length:14}).map((_,i)=><div key={i}>{String(i+53).padStart(3,'0')}</div>)}</div>
        <div className="body">
          <div className={'reveal ' + (seen ? 'in' : '')}>
            <div className="sec-num">§ 05 · THE EVIDENCE · TEN CONFIRMATIONS</div>
            <h2 className="sec-h">Ten exhibits. <em>One convergence.</em></h2>
            <p className="sec-lead">A summary of the ten prophetic timelines that resolve to the same generation. The complete working — every cross-reference, every verse — is at <a href="https://messiah2027.com" target="_blank" rel="noopener" style={{color:v4.brass,textDecoration:'underline'}}>messiah2027.com</a>.</p>
          </div>
          <div className="ex-grid">
            {C.calculations.map((c, i) => (
              <a className={'ex-card reveal d' + (Math.min(6, Math.floor(i/2)+1)) + ' ' + (seen ? 'in' : '')}
                 key={i}
                 href={c.href || '#'}
                 style={{textDecoration:'none', color:'inherit'}}>
                <div>
                  <div style={{display:'flex',justifyContent:'space-between',alignItems:'baseline'}}>
                    <span className="idx">№ {String(i+1).padStart(2,'0')}</span>
                    <span className={'tier tier-' + c.tier}>{tagFor(c.tier)}</span>
                  </div>
                  <div className="bn">{c.n}</div>
                  <div className="un">{c.unit}</div>
                  <div className="arc">{c.from} ⟶ {c.to}</div>
                  <div className="ttl">{c.title}</div>
                </div>
                <div className="src">{c.source.toUpperCase()} →</div>
              </a>
            ))}
          </div>
        </div>
      </div>
    );
  }

  // ============================================================
  // DEADLINE — animated meter + orbiting rings
  // ============================================================
  function Deadline() {
    const [ref, seen] = useInView({ threshold: 0.3 });
    const { daysLeft, monthsLeft, yearsElapsed, pctElapsed } = COUNTDOWN;
    const monthsV  = useCountUp(monthsLeft, seen);
    const daysV    = useCountUp(daysLeft,   seen);
    const yearsV   = useCountUp(yearsElapsed, seen);
    return (
      <div className="deadline" ref={ref}>
        <div className="glow"></div>
        <div className="orbits">
          <svg viewBox="0 0 1100 1100" fill="none">
            <circle cx="550" cy="550" r="220" stroke="#cfa44a" strokeWidth="1" strokeDasharray="2 8"/>
            <circle cx="550" cy="550" r="340" stroke="#cfa44a" strokeWidth="1" strokeDasharray="2 12" opacity="0.7"/>
            <circle cx="550" cy="550" r="460" stroke="#cfa44a" strokeWidth="1" strokeDasharray="1 16" opacity="0.5"/>
            <circle cx="770" cy="550" r="4" fill="#cfa44a"/>
            <circle cx="550" cy="210" r="3" fill="#cfa44a" opacity="0.7"/>
            <circle cx="90"  cy="550" r="2" fill="#cfa44a" opacity="0.5"/>
          </svg>
        </div>
        <div className={'reveal ' + (seen ? 'in' : '')}>
          <div className="k">YEAR {yearsElapsed} OF 80 · THE FIG-TREE GENERATION</div>
          <h2>The window <em>is open.</em> It is also <em>closing.</em></h2>
        </div>
        <div className={'meter reveal d2 ' + (seen ? 'in' : '')}>
          <div className="l"><span>1947 · ISRAEL REBORN</span><span>2027 · FEAST OF TRUMPETS</span></div>
          <div className="bar"><div className="f" style={{ width: seen ? pctElapsed.toFixed(2) + '%' : '0%' }}></div></div>
          <div className="digits">
            <div className="d"><div className="v">{monthsV}</div><div className="lbl">MONTHS LEFT</div></div>
            <div className="d"><div className="sep">·</div></div>
            <div className="d"><div className="v">{daysV}</div><div className="lbl">DAYS TO TRUMPETS</div></div>
            <div className="d"><div className="sep">·</div></div>
            <div className="d"><div className="v">{yearsV}</div><div className="lbl">OF 80 YEARS ELAPSED</div></div>
          </div>
          <div className="trumpet-note">Target · Yom Teruah (Feast of Trumpets) · Tishri 1, 5788 · ≈ September 12, 2027</div>
        </div>
        <p className={'reveal d3 ' + (seen ? 'in' : '')}>This is the only generation in two millennia where ten independent prophetic timelines all resolve. The mathematics will not repeat.</p>
        <div className={'ctas reveal d4 ' + (seen ? 'in' : '')}>
          <a className="btn" href="/COMPLETE_The_Solar_Key.pdf">Get the free book</a>
          <a className="btn ghost" href="/the-key.html">Read the proof</a>
        </div>
      </div>
    );
  }

  // ============================================================
  // TEACHINGS
  // ============================================================
  function Teachings() {
    const [ref, seen] = useInView();
    const tags = ['DOCTRINE','PROPHECY','DOCTRINE','APOLOGETICS','HERMENEUTICS','ESCHATOLOGY','ESCHATOLOGY','HISTORY'];
    return (
      <div className="section" ref={ref}>
        <div className="gutter">{Array.from({length:16}).map((_,i)=><div key={i}>{String(i+67).padStart(3,'0')}</div>)}</div>
        <div className="body">
          <div className={'reveal ' + (seen ? 'in' : '')}>
            <div className="sec-num">§ 02 · THE DOCTRINE</div>
            <h2 className="sec-h">Explore the <em>teaching</em>.</h2>
            <p className="sec-lead">Where the message touches Torah, the cross, the feasts, the apostasy, the millennium, and the road home. Spring feasts already fulfilled by Yahusha at His first coming — the fall feasts wait for His return.</p>
          </div>
          <div className="reel">
            {C.teachings.map((t, i) => (
              <a className={'rcard reveal d' + (Math.min(6, Math.floor(i/2)+1)) + ' ' + (seen ? 'in' : '')}
                 key={i}
                 href={t.href || '#'}
                 {...(t.external ? {target:'_blank', rel:'noopener'} : {})}>
                <div className="img"><span className="tag">{tags[i] || 'TEACHING'}</span></div>
                <div className="body">
                  <div>
                    <h3>{t.t}</h3>
                    <div className="d">{t.d}</div>
                  </div>
                  <div className="go">READ {t.external ? '↗' : '→'}</div>
                </div>
              </a>
            ))}
          </div>
        </div>
      </div>
    );
  }

  // ============================================================
  // RESOURCES
  // ============================================================
  function Resources() {
    const [ref, seen] = useInView();
    const cnt = useCountUp(360, seen);
    return (
      <div className="section" ref={ref}>
        <div className="gutter">{Array.from({length:10}).map((_,i)=><div key={i}>{String(i+83).padStart(3,'0')}</div>)}</div>
        <div className="body">
          <div className={'reveal ' + (seen ? 'in' : '')}>
            <div className="sec-num">§ 06 · FREE WORKBOOKS</div>
            <h2 className="sec-h">A library, openly accessible.</h2>
          </div>
          <div className="resources">
            <a className={'res reveal d1 ' + (seen ? 'in' : '')}
               href="https://straightisthegate.com/learn/workbooks/" target="_blank" rel="noopener"
               style={{textDecoration:'none', color:'inherit'}}>
              <div>
                <div className="k">LIBRARY</div>
                <div className="cnt">{cnt}<span style={{color:v4.paperM,fontFamily:v4.mono,fontSize:14,marginLeft:6}}>+</span></div>
                <h3>Workbook Library</h3>
                <p>Torah, sacred names, prophecy, feasts. Adults, youth, children. Printable PDFs.</p>
              </div>
              <div className="go">BROWSE ALL ↗</div>
            </a>
            <a className={'res reveal d2 ' + (seen ? 'in' : '')}
               href="https://straightisthegate.com/teachings.html" target="_blank" rel="noopener"
               style={{textDecoration:'none', color:'inherit'}}>
              <div>
                <div className="k">CURRICULUM</div>
                <h3>Truth Carriers Academy</h3>
                <p>Structured learning paths from beginner to deep study, hosted at Straight Is The Gate.</p>
              </div>
              <div className="go">BEGIN ↗</div>
            </a>
            <a className={'res reveal d3 ' + (seen ? 'in' : '')}
               href="https://truthcarriers.com" target="_blank" rel="noopener"
               style={{textDecoration:'none', color:'inherit'}}>
              <div>
                <div className="k">AUDIO + VIDEO</div>
                <h3>Truth Carriers Music</h3>
                <p>Prophetic hip-hop and warning videos. The same message in another voice.</p>
              </div>
              <div className="go">LISTEN ↗</div>
            </a>
          </div>
        </div>
      </div>
    );
  }

  function Footer() {
    const [ref, seen] = useInView();
    return (
      <footer ref={ref}>
        <div className={'verse reveal ' + (seen ? 'in' : '')}>
          “{C.verse.text}”
          <span className="ref">— {C.verse.ref}</span>
        </div>
        <div className={'meta reveal d2 ' + (seen ? 'in' : '')}>
          <span>© 2026 MESSIAH 2027 · TRUTH CARRIERS MINISTRY · DOC M27-V4</span>
          <div className="links">
            <a href="/privacy.html">PRIVACY</a>
            <a href="/terms.html">TERMS</a>
            <a href="/contact.html">CONTACT</a>
            <a href="https://straightisthegate.com" target="_blank" rel="noopener">STRAIT ↗</a>
            <a href="https://truthcarriers.com" target="_blank" rel="noopener">TRUTH CARRIERS ↗</a>
          </div>
        </div>
      </footer>
    );
  }

  // ============================================================
  // ORNAMENT DIVIDER — visual breath between heavy sections
  // ============================================================
  function Ornament() {
    if (!C.ornament) return null;
    return (
      <div className="ornament-div" aria-hidden="true">
        <div className="line"></div>
        <img src={C.ornament} alt=""/>
        <div className="line"></div>
      </div>
    );
  }

  // ============================================================
  // BOOKS — eight free covers, link out to download / page
  // ============================================================
  function Books() {
    const [ref, seen] = useInView();
    if (!C.books || !C.books.length) return null;
    return (
      <div className="books-section" id="books" ref={ref}>
        <div className={'header reveal ' + (seen ? 'in' : '')}>
          <div className="sec-num">§ 07 · FREE LIBRARY</div>
          <h2>Eight books. <em>All free.</em></h2>
          <p>The full library — Solar Key, Mystery Babylon, the mark, the image, the wrath, the flood, the remnant handbook. Download any in PDF.</p>
        </div>
        <div className="books-grid">
          {C.books.map((b, i) => (
            <a className={'book reveal d' + (Math.min(6, Math.floor(i/2)+1)) + ' ' + (seen ? 'in' : '')}
               key={i} href={b.href}>
              <div className="cover-wrap">
                <img src={b.cover} alt={b.title} loading="lazy"/>
              </div>
              <div className="title">{b.title}</div>
              <div className="cta">Open →</div>
            </a>
          ))}
        </div>
      </div>
    );
  }

  // ============================================================
  // APP
  // ============================================================
  function App() {
    const [year, setYear] = useState(1947);
    return (
      <div className="v4">
        <style>{css}</style>
        <AmbientGlyphs/>
        <Topbar/>
        <Stage/>
        <Scrubber year={year} setYear={setYear}/>
        <FormulaBar/>
        <Message/>
        <Teachings/>
        <Ornament/>
        <Pillars/>
        <CalcSection/>
        <Exhibit/>
        <Deadline/>
        <Ornament/>
        <Books/>
        <Resources/>
        <Footer/>
      </div>
    );
  }

  return { App };
})();

window.V4 = V4;
