/* ============================================================
   THE ALETHEAN LEGACY — site app
   ============================================================ */

const { useState, useEffect, useRef } = React;

/* ---------- Holo-mesh background canvas (Earth + hex mesh + ship) ---------- */
function HoloMeshCanvas({ density = 1, showShip = true }) {
  const canvasRef = useRef(null);

  useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    const ctx = canvas.getContext("2d");
    let raf;
    let t = 0;
    const stars = [];

    const resize = () => {
      const dpr = window.devicePixelRatio || 1;
      const rect = canvas.getBoundingClientRect();
      canvas.width = rect.width * dpr;
      canvas.height = rect.height * dpr;
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
      stars.length = 0;
      const count = Math.floor((rect.width * rect.height) / 2200);
      for (let i = 0; i < count; i++) {
        stars.push({
          x: Math.random() * rect.width,
          y: Math.random() * rect.height,
          r: Math.random() * 1.2 + 0.2,
          tw: Math.random() * Math.PI * 2,
          spd: Math.random() * 0.6 + 0.2,
        });
      }
    };
    resize();
    window.addEventListener("resize", resize);

    const draw = () => {
      const w = canvas.clientWidth;
      const h = canvas.clientHeight;
      ctx.clearRect(0, 0, w, h);

      // gradient backdrop
      const g = ctx.createLinearGradient(0, 0, 0, h);
      g.addColorStop(0, "#05080F");
      g.addColorStop(0.55, "#0A1426");
      g.addColorStop(1, "#04070D");
      ctx.fillStyle = g;
      ctx.fillRect(0, 0, w, h);

      // stars
      for (const s of stars) {
        s.tw += 0.02 * s.spd;
        const a = 0.4 + 0.6 * (0.5 + 0.5 * Math.sin(s.tw));
        ctx.fillStyle = `rgba(220, 232, 248, ${a * 0.7})`;
        ctx.beginPath();
        ctx.arc(s.x, s.y, s.r, 0, Math.PI * 2);
        ctx.fill();
      }

      // Earth — large, anchored bottom, partly clipped
      const earthCx = w * 0.5;
      const earthCy = h * 1.15;
      const earthR = Math.max(w, h) * 0.85;

      // earth glow rim
      const rimGrad = ctx.createRadialGradient(earthCx, earthCy, earthR * 0.95, earthCx, earthCy, earthR * 1.08);
      rimGrad.addColorStop(0, "rgba(125,184,212,0)");
      rimGrad.addColorStop(0.5, "rgba(125,184,212,0.12)");
      rimGrad.addColorStop(1, "rgba(125,184,212,0)");
      ctx.fillStyle = rimGrad;
      ctx.beginPath();
      ctx.arc(earthCx, earthCy, earthR * 1.08, 0, Math.PI * 2);
      ctx.fill();

      // earth body
      const eGrad = ctx.createRadialGradient(
        earthCx - earthR * 0.4, earthCy - earthR * 0.4, earthR * 0.1,
        earthCx, earthCy, earthR
      );
      eGrad.addColorStop(0, "#2C4866");
      eGrad.addColorStop(0.6, "#13243C");
      eGrad.addColorStop(1, "#070C18");
      ctx.fillStyle = eGrad;
      ctx.beginPath();
      ctx.arc(earthCx, earthCy, earthR, 0, Math.PI * 2);
      ctx.fill();

      // earth top-rim highlight
      ctx.save();
      ctx.beginPath();
      ctx.arc(earthCx, earthCy, earthR, 0, Math.PI * 2);
      ctx.clip();
      const rim = ctx.createRadialGradient(earthCx, earthCy - earthR + 4, 0, earthCx, earthCy - earthR + 4, earthR * 0.5);
      rim.addColorStop(0, "rgba(160, 210, 240, 0.28)");
      rim.addColorStop(1, "rgba(160, 210, 240, 0)");
      ctx.fillStyle = rim;
      ctx.fillRect(0, 0, w, h);
      ctx.restore();

      // hex-mesh holo overlay (clipped to earth)
      ctx.save();
      ctx.beginPath();
      ctx.arc(earthCx, earthCy, earthR + 6, 0, Math.PI * 2);
      ctx.clip();
      drawHexMesh(ctx, w, h, earthCx, earthCy - earthR, t, density);
      ctx.restore();

      // ship silhouette over earth (matches cover)
      if (showShip) {
        drawShip(ctx, w * 0.5, h * 0.35 + Math.sin(t * 0.6) * 4, w * 0.06);
      }

      t += 0.016;
      raf = requestAnimationFrame(draw);
    };
    draw();

    return () => {
      cancelAnimationFrame(raf);
      window.removeEventListener("resize", resize);
    };
  }, [density, showShip]);

  return <canvas ref={canvasRef} aria-hidden="true" />;
}

function drawHexMesh(ctx, w, h, cx, topY, t, density) {
  const size = 26 / density;        // hex radius
  const hSpace = size * Math.sqrt(3);
  const vSpace = size * 1.5;
  ctx.lineWidth = 1;
  for (let row = -2; row * vSpace < h + size; row++) {
    for (let col = -2; col * hSpace < w + size; col++) {
      const x = col * hSpace + (row % 2 ? hSpace / 2 : 0);
      const y = topY + row * vSpace;
      // distance from center for fade
      const dx = x - cx;
      const dy = y - (topY + h * 0.3);
      const d = Math.sqrt(dx * dx + dy * dy);
      const fade = Math.max(0, 1 - d / (Math.max(w, h) * 0.7));
      const flick = 0.6 + 0.4 * Math.sin(t * 1.5 + col * 0.3 + row * 0.7);
      const a = fade * 0.18 * flick;
      if (a < 0.005) continue;
      ctx.strokeStyle = `rgba(125, 184, 212, ${a})`;
      hexPath(ctx, x, y, size);
      ctx.stroke();
    }
  }
}
function hexPath(ctx, cx, cy, r) {
  ctx.beginPath();
  for (let i = 0; i < 6; i++) {
    const a = Math.PI / 3 * i + Math.PI / 6;
    const x = cx + r * Math.cos(a);
    const y = cy + r * Math.sin(a);
    if (i === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y);
  }
  ctx.closePath();
}
function drawShip(ctx, cx, cy, scale) {
  ctx.save();
  ctx.translate(cx, cy);
  // glow under ship
  const g = ctx.createRadialGradient(0, scale * 0.4, 0, 0, scale * 0.4, scale * 4);
  g.addColorStop(0, "rgba(220,240,255,0.25)");
  g.addColorStop(0.4, "rgba(125,184,212,0.08)");
  g.addColorStop(1, "rgba(125,184,212,0)");
  ctx.fillStyle = g;
  ctx.beginPath();
  ctx.arc(0, scale * 0.4, scale * 4, 0, Math.PI * 2);
  ctx.fill();

  // ship body — angular insectoid silhouette evoking the cover
  ctx.fillStyle = "#0F1A2A";
  ctx.strokeStyle = "rgba(125,184,212,0.6)";
  ctx.lineWidth = 1.2;
  ctx.beginPath();
  ctx.moveTo(-scale * 1.4, 0);
  ctx.lineTo(-scale * 0.5, -scale * 0.5);
  ctx.lineTo(-scale * 0.1, -scale * 0.2);
  ctx.lineTo(scale * 0.1, -scale * 0.5);
  ctx.lineTo(scale * 0.4, -scale * 0.45);
  ctx.lineTo(scale * 1.4, -scale * 0.05);
  ctx.lineTo(scale * 0.6, scale * 0.25);
  ctx.lineTo(0, scale * 0.15);
  ctx.lineTo(-scale * 0.6, scale * 0.25);
  ctx.closePath();
  ctx.fill();
  ctx.stroke();
  // little rim highlight
  ctx.strokeStyle = "rgba(220,240,255,0.45)";
  ctx.beginPath();
  ctx.moveTo(-scale * 1.2, -scale * 0.05);
  ctx.lineTo(scale * 1.2, -scale * 0.05);
  ctx.stroke();
  ctx.restore();
}

/* ---------- 18-year countdown HUD ---------- */
function CountdownHUD() {
  // story-fiction date: Marshall has 18 years to prepare. Anchor to a future date.
  const target = new Date("2044-09-15T00:00:00Z").getTime();
  const [now, setNow] = useState(Date.now());
  useEffect(() => {
    const id = setInterval(() => setNow(Date.now()), 1000);
    return () => clearInterval(id);
  }, []);
  const diff = Math.max(0, target - now);
  const yrs = Math.floor(diff / (365.25 * 24 * 3600 * 1000));
  const days = Math.floor((diff % (365.25 * 24 * 3600 * 1000)) / (24 * 3600 * 1000));
  const hrs = Math.floor((diff % (24 * 3600 * 1000)) / (3600 * 1000));
  const mins = Math.floor((diff % (3600 * 1000)) / 60000);

  const pad = (n, l = 2) => String(n).padStart(l, "0");

  return (
    <div className="hud">
      <div className="hud-label">
        <span className="blip"></span>
        Threat ETA · Marshall protocol
      </div>
      <div className="hud-readout">
        <div className="hud-cell">
          <div className="hud-num">{pad(yrs)}</div>
          <div className="hud-tick">Years</div>
        </div>
        <div className="hud-cell">
          <div className="hud-num">{pad(days, 3)}</div>
          <div className="hud-tick">Days</div>
        </div>
        <div className="hud-cell">
          <div className="hud-num">{pad(hrs)}</div>
          <div className="hud-tick">Hrs</div>
        </div>
        <div className="hud-cell">
          <div className="hud-num">{pad(mins)}</div>
          <div className="hud-tick">Min</div>
        </div>
      </div>
      <div className="hud-foot">
        <span>ORBIT · LEO 412 KM</span>
        <span>STATUS · STANDBY</span>
      </div>
    </div>
  );
}

/* ---------- abstract silhouette portrait (no real-person likeness) ----------
   Simple angular bust silhouette + HUD chrome. Use as a placeholder for a real
   portrait commission later. */
function Silhouette({ variant = 0 }) {
  // 4 subtly different head/shoulder shapes so the dossiers don't look identical
  const paths = [
    // 0 — square jaw
    "M50,30 C58,30 64,36 64,46 C64,53 61,58 58,61 L60,68 C72,72 80,80 84,92 L84,100 L16,100 L16,92 C20,80 28,72 40,68 L42,61 C39,58 36,53 36,46 C36,36 42,30 50,30 Z",
    // 1 — narrower
    "M50,28 C58,28 63,35 63,45 C63,52 60,58 57,61 L59,68 C73,73 82,82 86,94 L86,100 L14,100 L14,94 C18,82 27,73 41,68 L43,61 C40,58 37,52 37,45 C37,35 42,28 50,28 Z",
    // 2 — rounder
    "M50,32 C59,32 65,38 65,48 C65,55 62,60 58,63 L60,69 C71,73 79,81 83,93 L83,100 L17,100 L17,93 C21,81 29,73 40,69 L42,63 C38,60 35,55 35,48 C35,38 41,32 50,32 Z",
    // 3 — slim
    "M50,29 C57,29 62,35 62,44 C62,51 59,57 56,60 L58,67 C70,71 79,80 83,92 L83,100 L17,100 L17,92 C21,80 30,71 42,67 L44,60 C41,57 38,51 38,44 C38,35 43,29 50,29 Z",
  ];
  return (
    <svg className="dossier-silhouette" viewBox="0 0 100 100" preserveAspectRatio="xMidYMax meet">
      <defs>
        <linearGradient id={`sg-${variant}`} x1="0" y1="0" x2="0" y2="1">
          <stop offset="0" stopColor="#1B2D45" />
          <stop offset="0.5" stopColor="#0E1A2C" />
          <stop offset="1" stopColor="#070C18" />
        </linearGradient>
      </defs>
      <path d={paths[variant % paths.length]} fill={`url(#sg-${variant})`} stroke="rgba(125,184,212,0.45)" strokeWidth="0.4" />
    </svg>
  );
}

/* ---------- direct order form (mailto-based for static hosting) ---------- */
function DirectOrderForm() {
  const [format, setFormat] = useState("paperback");
  const [qty, setQty] = useState(1);
  const [name, setName] = useState("");
  const [email, setEmail] = useState("");
  const [addr, setAddr] = useState("");
  const [signed, setSigned] = useState(true);
  const [dedication, setDedication] = useState("");

  const subject = `Direct order: The Alethean Legacy (${format} × ${qty})`;
  const body = [
    `Format: ${format}`,
    `Quantity: ${qty}`,
    `Signed: ${signed ? "yes" : "no"}`,
    dedication ? `Dedication: ${dedication}` : null,
    "",
    `Name: ${name}`,
    `Email: ${email}`,
    `Shipping address:`,
    addr,
  ].filter(Boolean).join("\n");

  const href = `mailto:hello@robking.example?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(body)}`;

  return (
    <form className="order" id="order" onSubmit={(e) => { e.preventDefault(); window.location.href = href; }}>
      <div className="order-head">
        <div className="eyebrow" style={{ justifyContent: "center" }}>
          <span className="dot"></span>Order direct from the author
        </div>
        <h3 className="order-title">Signed copies, shipped from the UK.</h3>
        <p className="order-sub">Fill in your details below — your order opens in your email client and lands in Rob's inbox. He'll reply with payment options and shipping costs.</p>
      </div>

      <div className="order-grid">
        <label className="order-field">
          <span>Format</span>
          <select value={format} onChange={(e) => setFormat(e.target.value)}>
            <option value="paperback">Paperback</option>
            <option value="ebook">eBook (EPUB / PDF)</option>
          </select>
        </label>
        <label className="order-field">
          <span>Quantity</span>
          <input type="number" min="1" max="20" value={qty} onChange={(e) => setQty(Math.max(1, +e.target.value || 1))} />
        </label>
        <label className="order-field order-field--full">
          <span>Your name</span>
          <input type="text" required value={name} onChange={(e) => setName(e.target.value)} placeholder="Jane Doe" />
        </label>
        <label className="order-field order-field--full">
          <span>Email</span>
          <input type="email" required value={email} onChange={(e) => setEmail(e.target.value)} placeholder="you@example.com" />
        </label>
        <label className="order-field order-field--full">
          <span>Shipping address</span>
          <textarea rows="3" required value={addr} onChange={(e) => setAddr(e.target.value)} placeholder="Street, city, postcode, country" />
        </label>
        <label className="order-check order-field--full">
          <input type="checkbox" checked={signed} onChange={(e) => setSigned(e.target.checked)} />
          <span>Signed by the author</span>
        </label>
        {signed && (
          <label className="order-field order-field--full">
            <span>Dedication (optional)</span>
            <input type="text" value={dedication} onChange={(e) => setDedication(e.target.value)} placeholder="e.g. For Sam — keep watching the sky." maxLength="80" />
          </label>
        )}
      </div>

      <div className="order-foot">
        <button className="btn btn-primary" type="submit">
          Send order request
          <svg className="arrow" viewBox="0 0 14 14" fill="none" stroke="currentColor" strokeWidth="1.6">
            <path d="M2 7h10M8 3l4 4-4 4" />
          </svg>
        </button>
        <span className="order-note">Static-site safe · opens your email client</span>
      </div>
    </form>
  );
}

/* ---------- crew data ---------- */const CREW = [
  {
    callsign: "M-01",
    name: "Ryan Marshall",
    role: "Captain · Commanding Officer",
    origin: "Newcastle, UK",
    age: "Mid-30s",
    unit: "22 SAS (active)",
    clearance: "OMEGA",
    bio: "Stepped through an Alethean portal mid-operation in Iraq and stepped out into orbit. Inherits a ship he doesn't understand and an 18-year deadline he can't ignore.",
    detail: "A career soldier turned reluctant captain, Marshall holds the line between the people he chose and the deadline he can't outrun. Pragmatic, allergic to ceremony, fluent in four flavours of insubordination.",
    specialty: "Command · CQB · Ship Systems",
    variant: 0,
  },
  {
    callsign: "M-02",
    name: "John Medforth",
    role: "First Officer",
    origin: "London, UK",
    age: "Late 40s",
    unit: "22 SAS (ret.) · former DS, Selection",
    clearance: "OMEGA",
    bio: "Ran SAS Selection for years. Knows how to break a man and how to put him back together — both useful, when the crew you're given is the only crew you'll get.",
    detail: "Medforth is the steady hand on Marshall's shoulder and the bad cop in every wardroom. Two decades of choosing who deserved to wear the badge means he has a finely tuned ear for bullshit, ambition, and quiet courage.",
    specialty: "Leadership · Selection · CQB",
    variant: 1,
  },
  {
    callsign: "H-03",
    name: "Chris Howells",
    role: "Chief Engineer",
    origin: "Cardiff, UK",
    age: "Mid-30s",
    unit: "Formula 1 · former Chief Engineer",
    clearance: "OMEGA",
    bio: "Spent his career squeezing tenths out of engines that cost more than houses. Now squeezing performance out of a 400-year-old ship whose manual nobody can read.",
    detail: "Howells thinks in tolerances and lap times. He treats the Alethean drive like a misbehaving racing engine — instrument it, listen to it, fix the part nobody else thought was broken.",
    specialty: "Propulsion · Thermal · Materials",
    variant: 2,
  },
  {
    callsign: "H-04",
    name: "Julia Harrison",
    role: "Chief Medical Officer",
    origin: "Oxford, UK",
    age: "Mid-30s",
    unit: "Médecins Sans Frontières (fmr.)",
    clearance: "OMEGA",
    bio: "The ship's doctor. Keeps the crew alive and runs down the pathogen that emptied the Alethean home world four hundred years ago — because nobody has ruled out it finding its way here next.",
    detail: "Harrison runs the sickbay and, by necessity, the only working pathology lab in Earth orbit. Pragmatic, sharp, untheatrical — the team's compass when nothing on the bridge looks human.",
    specialty: "Trauma · Pathology · Pharmacology",
    variant: 3,
  },
  {
    callsign: "S-05",
    name: "Ricky Singh",
    role: "Pilot",
    origin: "Birmingham, UK",
    age: "Late 20s",
    unit: "RAF (fmr.)",
    clearance: "OMEGA",
    bio: "Flies the only working Alethean vessel in the solar system. Learnt to fly it the way most pilots learn — by guessing, swearing, and not crashing.",
    detail: "Singh has the lightest hands of anyone on the bridge and the worst poker face. Half pilot, half translator — he's the one who first got the ship to answer when spoken to.",
    specialty: "Flight Control · Navigation · Systems",
    variant: 0,
  },
];

/* ---------- crew dossier ---------- */
function Dossier({ person, idx, onOpen }) {
  return (
    <article className="dossier" onClick={() => onOpen(person)}>
      <div className="dossier-portrait">
        <div className="dossier-callsign">CALLSIGN · {person.callsign}</div>
        <div className="dossier-classified">CLASSIFIED</div>
        <Silhouette variant={person.variant} />
        <div className="dossier-id">
          <span>FILE · 00{idx + 1}</span>
          <span>OMEGA</span>
        </div>
      </div>
      <h3 className="dossier-name">{person.name}</h3>
      <div className="dossier-role">{person.role}</div>
      <dl className="dossier-meta">
        <dt>Origin</dt><dd>{person.origin}</dd>
        <dt>Unit</dt><dd>{person.unit}</dd>
      </dl>
      <p className="dossier-bio">{person.bio}</p>
    </article>
  );
}

/* ---------- modal ---------- */
function CrewModal({ person, onClose }) {
  useEffect(() => {
    const onKey = (e) => { if (e.key === "Escape") onClose(); };
    if (person) document.addEventListener("keydown", onKey);
    return () => document.removeEventListener("keydown", onKey);
  }, [person, onClose]);

  return (
    <div className={`modal-backdrop ${person ? "open" : ""}`} onClick={onClose}>
      {person && (
        <div className="modal" onClick={(e) => e.stopPropagation()}>
          <button className="modal-close" onClick={onClose} aria-label="Close">×</button>
          <div className="mono" style={{ fontSize: 10, letterSpacing: "0.28em", color: "var(--steel)", marginBottom: 8 }}>
            DOSSIER · {person.callsign} · CLEARANCE {person.clearance}
          </div>
          <h3>{person.name}</h3>
          <div className="role">{person.role}</div>
          <p>{person.detail}</p>
          <p style={{ color: "var(--fg-2)", fontStyle: "italic" }}>{person.bio}</p>
          <dl className="modal-grid">
            <dt>Origin</dt><dd>{person.origin}</dd>
            <dt>Age</dt><dd>{person.age}</dd>
            <dt>Unit</dt><dd>{person.unit}</dd>
            <dt>Specialty</dt><dd>{person.specialty}</dd>
          </dl>
        </div>
      )}
    </div>
  );
}

/* ---------- holo-mesh diagram for lore section ---------- */
function HoloMeshDiagram() {
  return (
    <svg viewBox="0 0 400 400">
      <defs>
        <radialGradient id="earth-g" cx="50%" cy="50%" r="50%">
          <stop offset="0%" stopColor="#2C4866" />
          <stop offset="60%" stopColor="#13243C" />
          <stop offset="100%" stopColor="#070C18" />
        </radialGradient>
        <radialGradient id="rim-g" cx="50%" cy="50%" r="55%">
          <stop offset="85%" stopColor="rgba(125,184,212,0)" />
          <stop offset="92%" stopColor="rgba(125,184,212,0.5)" />
          <stop offset="100%" stopColor="rgba(125,184,212,0)" />
        </radialGradient>
      </defs>

      {/* outer holo-mesh ring */}
      <circle cx="200" cy="200" r="180" fill="none" stroke="rgba(125,184,212,0.25)" strokeDasharray="2 6" />
      <circle cx="200" cy="200" r="170" fill="none" stroke="rgba(125,184,212,0.18)" strokeDasharray="1 3" />

      {/* hex pattern around earth */}
      {Array.from({ length: 32 }).map((_, i) => {
        const a = (i / 32) * Math.PI * 2;
        const r = 165;
        const cx = 200 + Math.cos(a) * r;
        const cy = 200 + Math.sin(a) * r;
        const pts = [];
        for (let k = 0; k < 6; k++) {
          const ka = Math.PI / 3 * k + Math.PI / 6;
          pts.push(`${cx + 8 * Math.cos(ka)},${cy + 8 * Math.sin(ka)}`);
        }
        return (
          <polygon key={i} points={pts.join(" ")}
            fill="rgba(125,184,212,0.05)"
            stroke="rgba(125,184,212,0.4)" strokeWidth="0.6" />
        );
      })}

      {/* earth */}
      <circle cx="200" cy="200" r="120" fill="url(#earth-g)" />
      <circle cx="200" cy="200" r="120" fill="url(#rim-g)" />

      {/* ship orbit + ship */}
      <circle cx="200" cy="200" r="145" fill="none" stroke="rgba(125,184,212,0.2)" strokeDasharray="3 6" />
      <g transform="translate(310 130)">
        <polygon points="-14,0 -5,-5 0,-2 5,-5 12,-3 14,0 8,3 0,2 -8,3"
          fill="#0F1A2A" stroke="rgba(125,184,212,0.7)" strokeWidth="0.8" />
        <line x1="-16" y1="0" x2="16" y2="0" stroke="rgba(220,240,255,0.4)" strokeWidth="0.4" />
      </g>

      {/* labels */}
      <g style={{ fontFamily: "var(--f-mono)", fontSize: 8, letterSpacing: 1.5, fill: "var(--steel)" }}>
        <text x="200" y="32" textAnchor="middle">HOLO-MESH · OUTER LAYER</text>
        <line x1="200" y1="36" x2="200" y2="44" stroke="var(--steel)" strokeWidth="0.6" />

        <text x="330" y="118" textAnchor="start">ALETHEAN VESSEL</text>
        <line x1="328" y1="120" x2="320" y2="128" stroke="var(--steel)" strokeWidth="0.6" />

        <text x="200" y="200" textAnchor="middle" fill="rgba(232,238,245,0.7)" fontSize="9">EARTH</text>
        <text x="200" y="214" textAnchor="middle" fill="var(--fg-2)" fontSize="7">UNAWARE</text>
      </g>

      {/* tiny nodes around the mesh */}
      {Array.from({ length: 8 }).map((_, i) => {
        const a = (i / 8) * Math.PI * 2;
        const r = 178;
        const cx = 200 + Math.cos(a) * r;
        const cy = 200 + Math.sin(a) * r;
        return <circle key={i} cx={cx} cy={cy} r="2" fill="var(--steel)" />;
      })}
    </svg>
  );
}

/* ---------- nav scroll listener ---------- */
function useScrolled(threshold = 40) {
  const [s, set] = useState(false);
  useEffect(() => {
    const on = () => set(window.scrollY > threshold);
    on();
    window.addEventListener("scroll", on, { passive: true });
    return () => window.removeEventListener("scroll", on);
  }, [threshold]);
  return s;
}

/* ---------- main app ---------- */
function App() {
  const [openCrew, setOpenCrew] = useState(null);
  const scrolled = useScrolled();

  // Tweaks
  const TWEAKS_DEFAULTS = /*EDITMODE-BEGIN*/{
    "meshDensity": 1,
    "showCountdown": true,
    "accent": "steel"
  }/*EDITMODE-END*/;
  const [tweaks, setTweak] = window.useTweaks ? window.useTweaks(TWEAKS_DEFAULTS) : [TWEAKS_DEFAULTS, () => {}];

  // accent var
  useEffect(() => {
    const root = document.documentElement;
    if (tweaks.accent === "steel") {
      root.style.setProperty("--steel", "#7DB8D4");
      root.style.setProperty("--steel-glow", "rgba(125, 184, 212, 0.35)");
    } else if (tweaks.accent === "ice") {
      root.style.setProperty("--steel", "#A8D8F0");
      root.style.setProperty("--steel-glow", "rgba(168, 216, 240, 0.35)");
    } else if (tweaks.accent === "amber") {
      root.style.setProperty("--steel", "#E8A547");
      root.style.setProperty("--steel-glow", "rgba(232, 165, 71, 0.35)");
    }
  }, [tweaks.accent]);

  const TweaksPanel = window.TweaksPanel;
  const TweakSection = window.TweakSection;
  const TweakSlider = window.TweakSlider;
  const TweakToggle = window.TweakToggle;
  const TweakRadio = window.TweakRadio;

  return (
    <>
      {/* Nav */}
      <nav className={`nav ${scrolled ? "scrolled" : ""}`}>
        <div className="nav-brand">
          <span className="glyph"></span>
          <span>Alethean Legacy</span>
        </div>
        <ul className="nav-links">
          <li><a href="#synopsis">Synopsis</a></li>
          <li><a href="#crew">Crew</a></li>
          <li><a href="#lore">The Aletheans</a></li>
          <li><a href="#author">Author</a></li>
        </ul>
        <a className="nav-cta" href="#buy">Buy the Book →</a>
      </nav>

      {/* Hero */}
      <section className="hero" id="top">
        <div className="hero-bg">
          <HoloMeshCanvas density={tweaks.meshDensity} showShip={false} />
        </div>

        <div className="hero-grid">
          <div>
            <div className="hero-eyebrow">
              <span className="eyebrow"><span className="dot"></span>Available now · eBook · Audiobook · Direct from the author</span>
            </div>
            <h1 className="hero-title">
              <span>The Alethean</span>
              <span className="line2">Legacy</span>
            </h1>
            <div className="hero-byline">
              A Novel by <span className="author">Rob King</span>
            </div>
            <p className="hero-pitch">
              An SAS operator steps through an Alethean portal in the middle of a firefight in Iraq —
              and out into the bridge of a 400-year-old derelict in Earth orbit. Its crew
              is dead. Its mission is unfinished. And he has eighteen years to build a
              fleet before something new comes for the planet.
            </p>
            <div className="hero-actions">
              <a className="btn btn-primary" href="#buy">
                Buy the Book
                <svg className="arrow" viewBox="0 0 14 14" fill="none" stroke="currentColor" strokeWidth="1.6">
                  <path d="M2 7h10M8 3l4 4-4 4" />
                </svg>
              </a>
              <a className="btn btn-ghost" href="#synopsis">
                Read the Synopsis
              </a>
            </div>
            {tweaks.showCountdown && <CountdownHUD />}
          </div>

          <div className="cover-stage">
            <div className="cover-frame">
              <span className="cover-corners">
                <span className="tl"></span><span className="tr"></span>
                <span className="bl"></span><span className="br"></span>
              </span>
              <img className="cover-img" src="assets/book-cover.jpg" alt="The Alethean Legacy book cover" />
              <div className="cover-ticks">
                <div>ROB KING</div>
                <div>ED. 01</div>
                <div>2026</div>
              </div>
            </div>
          </div>
        </div>

        <div className="scroll-cue">
          <span>Scroll</span>
          <span className="line"></span>
        </div>
      </section>

      {/* Synopsis */}
      <section className="synopsis" id="synopsis">
        <div className="container">
          <div className="eyebrow"><span className="dot"></span>Transmission · 001</div>
          <h2 className="section-title">A ship that should not exist.</h2>
          <div className="synopsis-grid">
            <aside className="synopsis-aside">
              <div className="stat">
                <div className="stat-key">Setting</div>
                <div className="stat-val">Low Earth Orbit</div>
                <div className="stat-sub">412 km · derelict vessel</div>
              </div>
              <div className="stat">
                <div className="stat-key">First Contact</div>
                <div className="stat-val">1657 AD</div>
                <div className="stat-sub">Date of Alethean arrival</div>
              </div>
              <div className="stat">
                <div className="stat-key">Time to Threat</div>
                <div className="stat-val">18 Years</div>
                <div className="stat-sub">Marshall protocol · classified</div>
              </div>
              <div className="stat">
                <div className="stat-key">Genre</div>
                <div className="stat-val">Hard SF · Military</div>
                <div className="stat-sub">Standalone · Book One</div>
              </div>
            </aside>
            <div className="synopsis-body">
              <p className="lead">
                Ryan Marshall is on a routine SAS extraction when something pulls
                him out of the world entirely — and into the corridors of a vessel that has
                been quietly orbiting Earth for four centuries.
              </p>
              <p>
                The Aletheans were tall, insectoid, patient. They came to watch us —
                and never went home. A pathogen brought back from another world killed
                their species on the home planet, leaving the orbiting crew with nowhere
                to return to and no way to reproduce. For centuries they watched alone.
                When the last of them died, the ship was left adrift above us.
              </p>
              <p>
                What Marshall learns aboard the ship is worse than the silence. There
                is something coming. He has eighteen years to build a fleet of replica
                Alethean ships — alone, with the crew he has and the specialists he can
                recruit in secret. The Aletheans left him a legacy. The question is whether
                he can finish it in time.
              </p>
            </div>
          </div>
        </div>
      </section>

      {/* Crew */}
      <section className="crew" id="crew">
        <div className="container">
          <div className="crew-head">
            <div>
              <div className="eyebrow"><span className="dot"></span>Personnel · Marshall protocol</div>
              <h2 className="section-title">The crew.</h2>
            </div>
            <div className="stamp">// Senior officers · OMEGA</div>
          </div>
          <div className="crew-grid">
            {CREW.map((p, i) => <Dossier key={p.callsign} person={p} idx={i} onOpen={setOpenCrew} />)}
          </div>
          <div className="divider" style={{ marginTop: 56 }}>
            <span>Senior officers · 05 of 05 · Wider crew classified</span>
          </div>
        </div>
      </section>

      {/* Lore */}
      <section className="lore" id="lore">
        <div className="container">
          <div className="eyebrow"><span className="dot"></span>Subject · Aletheans &amp; the Holo-Mesh</div>
          <h2 className="section-title">A blanket over the sky.</h2>
          <div className="lore-grid">
            <div className="lore-visual">
              <HoloMeshDiagram />
            </div>
            <div className="lore-card-stack">
              <div className="lore-card">
                <div className="lore-card-key"><span>The Aletheans</span><span className="num">// 01</span></div>
                <h3>Seven feet tall. Patient. Gone.</h3>
                <p>
                  An insectoid civilisation that crossed an unimaginable distance to study us
                  four hundred years ago. A pathogen brought back from another world killed
                  their species on the home planet — marooning the crew above us, with no
                  way home and no way to reproduce. They watched alone for centuries. When
                  the last of them died, the most advanced vessel in our solar system was
                  left drifting empty above our heads.
                </p>
              </div>
              <div className="lore-card">
                <div className="lore-card-key"><span>The Holo-Mesh</span><span className="num">// 02</span></div>
                <h3>The lie above our heads.</h3>
                <p>
                  A holographic projection wrapped around the Earth, broadcasting a clean,
                  empty sky downward. Every observatory, every satellite, every airline pilot
                  has been looking up at a forgery. The ship has hidden behind it since the
                  reign of Charles I.
                </p>
              </div>
              <div className="lore-card threat-stripe">
                <div className="lore-card-key"><span>The Threat</span><span className="num">// 03</span></div>
                <h3>Eighteen years out.</h3>
                <p>
                  Something is coming. New, hostile, and unconcerned with us. Marshall has
                  eighteen years to build a fleet of replica Alethean ships — quietly, in
                  the dark, with the crew he has and whoever else he can pull in.
                </p>
              </div>
            </div>
          </div>
        </div>
      </section>

      {/* Buy */}
      <section className="buy" id="buy">
        <div className="container buy-inner">
          <div className="eyebrow" style={{ justifyContent: "center" }}>
            <span className="dot"></span>Get the book
          </div>
          <h2 className="buy-title">Take command.</h2>
          <p className="buy-sub">
The Alethean Legacy is available now as an eBook, an audiobook, and direct from the author.
            Read it before the eighteen years run out.
          </p>
          <div className="buy-grid">
            <a className="buy-card" href="https://amzn.to/4dmJAh0" target="_blank" rel="noopener">
              <span className="format">eBook · Kindle</span>
              <span className="vendor">Amazon Kindle</span>
              <span className="price"><span>Read on any device</span><span className="arr">→</span></span>
            </a>
            <a className="buy-card" href="https://books.apple.com/ca/audiobook/the-alethean-legacy/id1806660410" target="_blank" rel="noopener">
              <span className="format">Audiobook · narrated</span>
              <span className="vendor">Apple Books</span>
              <span className="price"><span>Listen on Apple</span><span className="arr">→</span></span>
            </a>
            <a className="buy-card" href="https://www.smashwords.com/books/view/633717" target="_blank" rel="noopener">
              <span className="format">eBook · multi-format</span>
              <span className="vendor">Smashwords</span>
              <span className="price"><span>EPUB · MOBI · PDF</span><span className="arr">→</span></span>
            </a>
            <a className="buy-card buy-card--featured" href="#order">
              <span className="format">Signed by the author</span>
              <span className="vendor">Direct from Rob</span>
              <span className="price"><span>Order below</span><span className="arr">↓</span></span>
            </a>
          </div>

          <DirectOrderForm />
        </div>
      </section>

      {/* Author */}
      <section className="author" id="author">
        <div className="container">
          <div className="eyebrow"><span className="dot"></span>About the author</div>
          <h2 className="section-title">Rob King.</h2>
          <div className="author-grid">
            <div className="author-portrait">
              <div className="stamp">AUTHOR · RK</div>
              <Silhouette variant={1} />
            </div>
            <div className="author-bio">
              <p>
                Rob King is a British sci-fi fan from the north of England. Heavily
                influenced by the likes of Star Trek, Isaac Asimov, and the SAS novels of
                Chris Ryan, the idea for The Alethean Legacy took twenty years to become a
                complete story.
              </p>
              <div className="author-links">
                <a className="author-link" href="#" target="_blank" rel="noopener">Newsletter</a>
                <a className="author-link" href="#" target="_blank" rel="noopener">Bluesky</a>
                <a className="author-link" href="#" target="_blank" rel="noopener">Instagram</a>
                <a className="author-link" href="mailto:hello@example.com">Contact</a>
              </div>
            </div>
          </div>
        </div>
      </section>

      {/* Foot */}
      <footer className="foot">
        <div className="foot-row">
          <span>© 2026 Rob King · The Alethean Legacy</span>
          <span><a href="#top">Back to top ↑</a></span>
        </div>
      </footer>

      {/* Crew modal */}
      <CrewModal person={openCrew} onClose={() => setOpenCrew(null)} />

      {/* Tweaks */}
      {TweaksPanel && (
        <TweaksPanel title="Tweaks">
          <TweakSection title="Holo-mesh">
            <TweakSlider label="Mesh density" min={0.6} max={1.8} step={0.1}
              value={tweaks.meshDensity} onChange={(v) => setTweak("meshDensity", v)} />
          </TweakSection>
          <TweakSection title="HUD">
            <TweakToggle label="Show 18-year countdown"
              value={tweaks.showCountdown} onChange={(v) => setTweak("showCountdown", v)} />
          </TweakSection>
          <TweakSection title="Accent">
            <TweakRadio
              value={tweaks.accent}
              onChange={(v) => setTweak("accent", v)}
              options={[
                { value: "steel", label: "Steel" },
                { value: "ice", label: "Ice" },
                { value: "amber", label: "Amber" },
              ]}
            />
          </TweakSection>
        </TweaksPanel>
      )}
    </>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
