---
const { data } = Astro.locals.starlightRoute.entry;
const hero = data.hero;

const title    = hero?.title    ?? data.title ?? '';
const tagline  = hero?.tagline  ?? data.description ?? '';
const eyebrow  = hero?.eyebrow  ?? 'Modular MMVC PHP framework';
const actions  = Array.isArray(hero?.actions) ? hero.actions : [];

const shouldRender = !!hero;
---

{shouldRender && (
  <section class="galaxy-hero">
    <canvas class="galaxy-hero__canvas" id="galaxy-canvas" aria-hidden="true"></canvas>
    <div class="galaxy-hero__veil" aria-hidden="true"></div>

    <span class="galaxy-hero__number" aria-hidden="true">01</span>

    <div class="galaxy-hero__copy">
      {eyebrow && <p class="galaxy-hero__eyebrow">{eyebrow}</p>}
      {title && <h1 class="galaxy-hero__title" set:html={title} />}
      {tagline && <p class="galaxy-hero__lede" set:html={tagline} />}

      {actions.length > 0 && (
        <div class="galaxy-hero__cta">
          {actions.map((a: any) => (
            <a
              href={a.link}
              class={`galaxy-button ${
                a.variant === 'minimal' || a.variant === 'secondary'
                  ? 'galaxy-button--ghost'
                  : 'galaxy-button--primary'
              }`}
            >
              <span>{a.text}</span>
              {a.icon === 'right-arrow' && <span class="galaxy-button__arrow" aria-hidden="true">→</span>}
              {a.icon === 'external' && <span class="galaxy-button__arrow" aria-hidden="true">↗</span>}
            </a>
          ))}
        </div>
      )}
    </div>
  </section>
)}

<style is:global>
  /* === HERO container — FULL-BLEED, framed, centered === */
  .galaxy-hero {
    position: relative;
    isolation: isolate;
    width: 100vw;
    margin-left: 50%;
    margin-right: 50%;
    transform: translateX(-50%);
    padding: clamp(3rem, 6vh, 6rem) clamp(1.5rem, 4vw, 4rem) clamp(4rem, 9vh, 8rem);
    min-height: calc(100vh - var(--sl-nav-height, 5.2rem));
    display: flex;
    align-items: center;
    justify-content: flex-start;
    overflow: hidden;
    color: #f5f1e8;
    background: linear-gradient(
      180deg,
      #07041a 0%,
      #0c0728 30%,
      #150a3c 60%,
      #2c1a52 85%,
      #4a2f6c 100%
    );
  }

  /* Inset frame — a thin paper-coloured rule outlining the cosmic stage,
   * like a framed astronomy photograph. Doubles as a scrim that darkens
   * the left third so the copy reads cleanly against the spiral arms. */
  .galaxy-hero__veil {
    position: absolute;
    inset: clamp(10px, 1.2vw, 22px);
    z-index: 1;
    pointer-events: none;
    border: 1px solid rgba(196, 181, 253, 0.28);
    border-radius: 6px;
    box-shadow:
      inset 0 0 160px rgba(0, 0, 0, 0.55),
      inset 0 0 0 1px rgba(7, 4, 26, 0.6);
    background:
      /* dark scrim on the left half so text reads through */
      linear-gradient(
        90deg,
        rgba(7, 4, 26, 0.78) 0%,
        rgba(7, 4, 26, 0.55) 22%,
        rgba(7, 4, 26, 0.18) 42%,
        rgba(7, 4, 26, 0) 60%
      ),
      /* faint amethyst glow toward the galactic core */
      radial-gradient(ellipse at 62% 50%, rgba(124, 112, 171, 0.10) 0%, transparent 60%);
  }

  /* Soft fade-to-paper at the bottom. Bezier-eased gradient stops give
   * a perceptually-smooth transition: most of the alpha change happens
   * in the middle of the fade region, none at the extremes. The bottom
   * of this fade exactly matches the body's cream colour — no seam. */
  .galaxy-hero::after {
    content: '';
    position: absolute;
    inset: auto 0 -1px 0;
    height: 38vh;
    min-height: 320px;
    background: linear-gradient(
      to bottom,
      rgba(245, 241, 232, 0)    0%,
      rgba(245, 241, 232, 0.01) 12%,
      rgba(245, 241, 232, 0.04) 25%,
      rgba(245, 241, 232, 0.10) 38%,
      rgba(245, 241, 232, 0.22) 50%,
      rgba(245, 241, 232, 0.40) 62%,
      rgba(245, 241, 232, 0.62) 74%,
      rgba(245, 241, 232, 0.82) 86%,
      rgba(245, 241, 232, 0.95) 94%,
      rgba(245, 241, 232, 1)   100%
    );
    pointer-events: none;
    z-index: 2;
  }

  :root[data-theme='dark'] .galaxy-hero::after {
    background: linear-gradient(
      to bottom,
      rgba(24, 20, 40, 0)    0%,
      rgba(24, 20, 40, 0.10) 30%,
      rgba(24, 20, 40, 0.40) 60%,
      rgba(24, 20, 40, 0.80) 85%,
      rgba(24, 20, 40, 1)   100%
    );
  }

  .galaxy-hero__canvas {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    z-index: 0;
    display: block;
  }

  /* Subtle vignette + atmospheric haze on top of the canvas */
  .galaxy-hero__veil {
    position: absolute;
    inset: 0;
    z-index: 1;
    pointer-events: none;
    background:
      radial-gradient(ellipse at 65% 50%, rgba(124, 112, 171, 0.12) 0%, transparent 60%),
      radial-gradient(ellipse at 100% 100%, rgba(0, 0, 0, 0.55) 0%, transparent 60%);
  }

  /* Copy floats over the galaxy on the left third of the framed stage.
   * The galaxy itself stays visible behind the text via low-opacity backdrop. */
  .galaxy-hero__copy {
    position: relative;
    z-index: 3;
    max-width: 32rem;
    margin-left: max(2rem, calc((100vw - var(--sl-content-width, 50rem)) / 2));
  }

  @media (max-width: 768px) {
    .galaxy-hero__copy {
      margin-left: 1.5rem;
      margin-right: 1.5rem;
    }
  }

  .galaxy-hero__number {
    position: absolute;
    right: clamp(2rem, 5vw, 6rem);
    bottom: clamp(2rem, 6vh, 5rem);
    font-family: var(--font-display, 'Bricolage Grotesque', sans-serif);
    font-variation-settings: 'opsz' 96, 'wght' 200;
    font-size: clamp(8rem, 14vw, 16rem);
    line-height: 1;
    color: rgba(196, 181, 253, 0.10);
    letter-spacing: -0.06em;
    pointer-events: none;
    z-index: 2;
    user-select: none;
  }

  .galaxy-hero__eyebrow {
    font-family: var(--font-display, 'Bricolage Grotesque', sans-serif);
    font-size: 0.72rem;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    color: #fbbf24;
    margin: 0 0 1.6rem;
    display: inline-flex;
    align-items: center;
    gap: 0.7rem;
  }
  .galaxy-hero__eyebrow::before {
    content: '';
    display: inline-block;
    width: 24px;
    height: 1px;
    background: #fbbf24;
  }

  .galaxy-hero__title {
    font-family: var(--font-display, 'Bricolage Grotesque', sans-serif);
    font-variation-settings: 'opsz' 96, 'wght' 600;
    font-size: clamp(2.6rem, 1.8rem + 4vw, 4.8rem);
    line-height: 0.96;
    letter-spacing: -0.04em;
    margin: 0 0 1.4rem;
    color: #ffffff;
    max-width: 16ch;
    text-shadow: 0 2px 24px rgba(0, 0, 0, 0.5);
  }

  .galaxy-hero__title em {
    font-style: normal;
    color: #c4b5fd;
    position: relative;
    white-space: nowrap;
  }
  .galaxy-hero__title em::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0.05em;
    height: 0.18em;
    background: #fbbf24;
    opacity: 0.55;
    z-index: -1;
    box-shadow: 0 0 24px rgba(251, 191, 36, 0.5);
  }

  .galaxy-hero__lede {
    font-family: var(--font-text, 'Bricolage Grotesque', sans-serif);
    font-variation-settings: 'opsz' 24, 'wght' 400;
    font-size: clamp(1.05rem, 0.9rem + 0.3vw, 1.22rem);
    line-height: 1.55;
    letter-spacing: -0.012em;
    color: rgba(245, 241, 232, 0.85);
    max-width: 36ch;
    margin: 0 0 2rem;
    text-shadow: 0 1px 12px rgba(0, 0, 0, 0.4);
  }

  .galaxy-hero__lede strong {
    color: #ffffff;
    font-variation-settings: 'opsz' 24, 'wght' 600;
  }

  .galaxy-hero__cta {
    display: flex;
    gap: 0.7rem;
    flex-wrap: wrap;
    align-items: center;
  }

  .galaxy-button {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.7rem 1.2rem;
    font-family: var(--font-text, 'Bricolage Grotesque', sans-serif);
    font-variation-settings: 'opsz' 14, 'wght' 600;
    font-size: 0.92rem;
    letter-spacing: -0.005em;
    border-radius: 3px;
    text-decoration: none;
    transition: transform 200ms cubic-bezier(0.2, 0.7, 0.2, 1),
                box-shadow 200ms,
                background-color 200ms;
  }

  .galaxy-button--primary {
    background: linear-gradient(135deg, #fbbf24, #fde68a);
    color: #1f1b2e;
    border: 1px solid rgba(251, 191, 36, 0.6);
    box-shadow: 0 1px 0 rgba(251, 191, 36, 0.4),
                0 0 32px rgba(251, 191, 36, 0.25);
  }
  .galaxy-button--primary:hover {
    transform: translateY(-1px);
    box-shadow: 0 8px 32px -4px rgba(251, 191, 36, 0.5);
  }

  .galaxy-button--ghost {
    background: rgba(255, 255, 255, 0.06);
    color: #f5f1e8;
    border: 1px solid rgba(245, 241, 232, 0.3);
    backdrop-filter: blur(8px);
  }
  .galaxy-button--ghost:hover {
    border-color: #c4b5fd;
    color: #c4b5fd;
  }

  .galaxy-button .galaxy-button__arrow {
    transition: transform 240ms cubic-bezier(0.2, 0.7, 0.2, 1);
  }
  .galaxy-button:hover .galaxy-button__arrow {
    transform: translateX(3px);
  }

  /* Lotus mark anchored on the right — floating in front of the galaxy */
  .galaxy-hero__mark {
    width: clamp(150px, 22vw, 290px);
    height: auto;
    filter:
      drop-shadow(0 0 30px rgba(196, 181, 253, 0.45))
      drop-shadow(0 0 60px rgba(125, 183, 220, 0.25))
      brightness(1.15);
    animation: galaxy-breathe 14s ease-in-out infinite;
    transition: filter 400ms ease;
  }

  @keyframes galaxy-breathe {
    0%, 100% { transform: translateY(0)    rotate(0deg);  }
    50%      { transform: translateY(-8px) rotate(0.8deg); }
  }

  @media (prefers-reduced-motion: reduce) {
    .galaxy-hero__mark { animation: none; }
  }

  @media (max-width: 768px) {
    .galaxy-hero__mark {
      width: 130px;
      opacity: 0.85;
    }
  }
</style>

<script is:inline>
(function () {
  const canvas = document.getElementById('galaxy-canvas');
  if (!canvas) return;

  const reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;

  const gl = canvas.getContext('webgl2', {
    alpha: true,
    antialias: true,
    premultipliedAlpha: false,
  }) || canvas.getContext('webgl', { alpha: true, antialias: true });
  if (!gl) return;

  // ---------------------------------------------------------------------------
  // Particle generation — a tilted spiral galaxy with 4 arms.
  // ---------------------------------------------------------------------------
  const PARTICLE_COUNT = 14000;

  const positions = new Float32Array(PARTICLE_COUNT * 3);
  const colors    = new Float32Array(PARTICLE_COUNT * 4);
  const sizes     = new Float32Array(PARTICLE_COUNT);
  const seeds     = new Float32Array(PARTICLE_COUNT);

  const ARMS = 4;
  const ARM_SPREAD = 0.55;

  for (let i = 0; i < PARTICLE_COUNT; i++) {
    // Bias toward the centre — pow(rand, 0.55) clusters values near zero.
    const r = Math.pow(Math.random(), 0.55) * 1.0;
    const arm = Math.floor(Math.random() * ARMS);
    const baseTheta = (arm * (Math.PI * 2) / ARMS) + Math.log(r + 0.05) * 5.0;
    const theta = baseTheta + (Math.random() - 0.5) * ARM_SPREAD * (1 - r);

    // Thin disk with slight thickness toward the centre.
    const thickness = 0.04 + (1 - r) * 0.10;
    const y = (Math.random() - 0.5) * thickness;

    positions[i * 3 + 0] = r * Math.cos(theta);
    positions[i * 3 + 1] = y;
    positions[i * 3 + 2] = r * Math.sin(theta);

    // Colour by radius — gold core → violet → cyan → pale lavender outer.
    let cr, cg, cb;
    if (r < 0.12) {
      // Hot bright core
      cr = 1.00; cg = 0.93; cb = 0.70;
    } else if (r < 0.32) {
      cr = 0.99; cg = 0.75; cb = 0.42;   // marduk gold
    } else if (r < 0.55) {
      cr = 0.66; cg = 0.51; cb = 0.96;   // amethyst
    } else if (r < 0.80) {
      cr = 0.49; cg = 0.72; cb = 0.86;   // skyfall blue
    } else {
      cr = 0.92; cg = 0.90; cb = 0.95;   // pale lavender
    }

    // Slight per-particle colour wobble for richness
    const jitter = (Math.random() - 0.5) * 0.10;
    cr = Math.max(0, Math.min(1, cr + jitter));
    cg = Math.max(0, Math.min(1, cg + jitter));
    cb = Math.max(0, Math.min(1, cb + jitter));

    colors[i * 4 + 0] = cr;
    colors[i * 4 + 1] = cg;
    colors[i * 4 + 2] = cb;
    // Higher base alpha — the additive blend on a near-black canvas needs
    // brighter contribution per-particle to make the spiral structure pop.
    colors[i * 4 + 3] = 0.65 + Math.random() * 0.35;

    sizes[i] = 1.6 + Math.random() * 3.0;
    if (r < 0.10) sizes[i] *= 2.6;  // hot core particles much bigger
    else if (r < 0.30) sizes[i] *= 1.4;  // inner arms emphasized
    if (r > 0.85) sizes[i] *= 0.85; // outer faint dust slightly smaller

    seeds[i] = Math.random();
  }

  // ---------------------------------------------------------------------------
  // Shaders
  // ---------------------------------------------------------------------------
  const vsSrc = `
    precision mediump float;
    attribute vec3  aPos;
    attribute vec4  aCol;
    attribute float aSize;
    attribute float aSeed;
    uniform float   uTime;
    uniform float   uRatio;
    uniform float   uPixelRatio;
    varying vec4    vCol;

    void main() {
      // Inner particles rotate faster than outer (like a real galaxy).
      // Slow, contemplative pace: outer arms ~ 90s/revolution.
      float r = length(aPos.xz);
      float speed = 0.28 / (r + 0.12);
      float angle = uTime * speed * 0.024;
      float c = cos(angle), s = sin(angle);
      vec3 p = vec3(c * aPos.x - s * aPos.z, aPos.y, s * aPos.x + c * aPos.z);

      // 3/4 perspective view — disk tilted at ~50° so the spiral is clearly
      // visible as an ellipse, not a perfect circle. Less face-on than 1.15,
      // less edge-on than 0.42; sweet spot for editorial composition.
      float tx = 0.85;
      float cx = cos(tx), sx = sin(tx);
      p = vec3(p.x, cx * p.y - sx * p.z, sx * p.y + cx * p.z);

      // Subtle camera drift — Lissajous oscillation, very slow.
      vec2 drift = vec2(sin(uTime * 0.018) * 0.04, cos(uTime * 0.013) * 0.03);

      // Centred. scale 1.1 so the disk fills the framed stage comfortably
      // without clipping at top/bottom.
      float scale = 1.1;
      vec2 screen = vec2(p.x * scale + drift.x, p.y * 1.0 * scale + drift.y);
      screen.x /= uRatio;

      gl_Position = vec4(screen, p.z * 0.5, 1.0);

      // Twinkle: subtle, only ±10% size modulation, never dims below baseline.
      float tw = 0.95 + 0.05 * sin(uTime * 0.6 + aSeed * 6.28);
      gl_PointSize = aSize * uPixelRatio * tw;

      vCol = aCol;
    }
  `;

  const fsSrc = `
    precision mediump float;
    varying vec4 vCol;

    void main() {
      vec2 uv = gl_PointCoord - 0.5;
      float d = length(uv);
      // Soft round particle with brighter halo; widened halo radius so
      // particles bleed into each other and form visible dust lanes.
      float core  = 1.0 - smoothstep(0.0,  0.22, d);
      float halo  = 1.0 - smoothstep(0.22, 0.50, d);
      float alpha = core + halo * 0.75;
      // Add brightness boost in the core for the bloom feel.
      gl_FragColor = vec4(vCol.rgb * (1.0 + core * 1.0), vCol.a * alpha);
    }
  `;

  function compile(type, src) {
    const sh = gl.createShader(type);
    gl.shaderSource(sh, src);
    gl.compileShader(sh);
    if (!gl.getShaderParameter(sh, gl.COMPILE_STATUS)) {
      console.error('shader compile:', gl.getShaderInfoLog(sh));
      gl.deleteShader(sh);
      return null;
    }
    return sh;
  }

  const vs = compile(gl.VERTEX_SHADER, vsSrc);
  const fs = compile(gl.FRAGMENT_SHADER, fsSrc);
  if (!vs || !fs) return;

  const prog = gl.createProgram();
  gl.attachShader(prog, vs);
  gl.attachShader(prog, fs);
  gl.linkProgram(prog);
  if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) {
    console.error('program link:', gl.getProgramInfoLog(prog));
    return;
  }
  gl.useProgram(prog);

  // ---------------------------------------------------------------------------
  // Buffers
  // ---------------------------------------------------------------------------
  function buf(data, attrName, size) {
    const b = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, b);
    gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
    const loc = gl.getAttribLocation(prog, attrName);
    gl.enableVertexAttribArray(loc);
    gl.vertexAttribPointer(loc, size, gl.FLOAT, false, 0, 0);
  }

  buf(positions, 'aPos', 3);
  buf(colors,    'aCol', 4);
  buf(sizes,     'aSize', 1);
  buf(seeds,     'aSeed', 1);

  const uTime  = gl.getUniformLocation(prog, 'uTime');
  const uRatio = gl.getUniformLocation(prog, 'uRatio');
  const uPx    = gl.getUniformLocation(prog, 'uPixelRatio');

  gl.enable(gl.BLEND);
  gl.blendFunc(gl.SRC_ALPHA, gl.ONE);          // additive
  gl.disable(gl.DEPTH_TEST);

  // ---------------------------------------------------------------------------
  // Resize + animation loop
  // ---------------------------------------------------------------------------
  function resize() {
    const dpr = Math.min(window.devicePixelRatio || 1, 2);
    const rect = canvas.getBoundingClientRect();
    canvas.width  = Math.max(2, Math.floor(rect.width  * dpr));
    canvas.height = Math.max(2, Math.floor(rect.height * dpr));
    gl.viewport(0, 0, canvas.width, canvas.height);
    gl.uniform1f(uRatio, canvas.width / canvas.height);
    gl.uniform1f(uPx, dpr);
  }
  resize();
  window.addEventListener('resize', resize);

  let start = performance.now();
  function frame(now) {
    const t = (now - start) * 0.001;
    gl.clearColor(0, 0, 0, 0);
    gl.clear(gl.COLOR_BUFFER_BIT);
    gl.uniform1f(uTime, t);
    gl.drawArrays(gl.POINTS, 0, PARTICLE_COUNT);
    if (!reduced) requestAnimationFrame(frame);
  }
  requestAnimationFrame(frame);

  // Pause when tab is hidden
  document.addEventListener('visibilitychange', () => {
    if (!document.hidden && !reduced) requestAnimationFrame(frame);
  });
})();
</script>
