/* global React, BrushIcon, RadialMenuSVG */
const { useState: useStateF, useEffect: useEffectF, useRef: useRefF } = React;

// Features section — bento-style grid on warm bg, with brush callouts
const Features = () => {
  return (
    <section className="features-wrap" id="features" data-screen-label="04 Features">
      {/* Decorative brush thumbnails drifting behind cards */}
      <FeaturesBgBrushes />

      <div className="section-head">
        <div>
          <div className="lead">Features</div>
          <h2>Every brush, <span className="it">a flick away.</span></h2>
        </div>
        <div className="desc" style={{ textAlign: "left" }}>
          A radial menu at your cursor, an editor to fill it, child brushes nested in a slot, an auto-scanning library,
          and a plugin that wires it all directly into ZBrush. It keeps you focused on the work, not just faster.
        </div>
      </div>

      <div className="features-grid">
        {/* ROW 1 — Radial Menu + Custom Slots */}
        <div className="feat-card span-3">
          <span className="feat-num">01 — RADIAL MENU</span>
          <div className="feat-split">
            <div className="feat-split-text">
              <h3>Your hotkey. Your brushes. Right there.</h3>
              <p>Press your shortcut. A radial wheel appears at your cursor with the brushes you chose. Hover a slot, lift your pen. Done. ZBrush loads the brush silently in the background.</p>
            </div>
            <div className="feat-split-visual">
              {/* Variants override: swap the Move brush (variant 2) for the rounder
                   Clay brush so all 8 spokes read consistently at this small size. */}
              <RadialMenuSVG size={180} spokeCount={8} brushSize={34} glow={1} variants={[0, 1, 11, 3, 4, 5, 6, 7]} />
            </div>
          </div>
        </div>

        <div className="feat-card span-3 dark">
          <span className="feat-num">02 — CUSTOM SLOTS</span>
          <div className="feat-split">
            <div className="feat-split-text">
              <h3>Drag. Drop. Done.</h3>
              <p>Assign any brush to any slot in the Radial Menu Editor. Build separate menus for blockout, secondary forms, and polish. Drag from your library straight onto the visual wheel.</p>
            </div>
            <div className="feat-split-visual">
              <EditorVisual />
            </div>
          </div>
        </div>

        {/* ROW 2 — Children Slots + ZBrush Bridge (hero) */}
        <div className="feat-card span-2">
          <span className="feat-num">03 — CHILDREN SLOTS</span>
          <h3>Three brushes in one slot.</h3>
          <p>Each slot holds up to 3 child brushes. Hover to expand a sub-menu without lifting your pen. More options, same footprint.</p>
          <div className="feat-visual">
            <ChildrenVisual />
          </div>
        </div>

        <div className="feat-card span-4 dark hero-card">
          <span className="feat-num">04 — ZBRUSH BRIDGE</span>
          <div className="bridge-layout">
            <div className="bridge-text">
              <h3>RadialZ talks to ZBrush.</h3>
              <p>The Bridge plugin connects RadialZ directly to your ZBrush install. No manual hotkey sequences, no scripting. Select it in RadialZ, it loads in ZBrush.</p>
            </div>
            <div className="bridge-visual">
              <BridgeVisual />
            </div>
          </div>
        </div>

        {/* ROW 3 — Brush Library (wide) */}
        <div className="feat-card span-6">
          <span className="feat-num">05 — BRUSH LIBRARY</span>
          <div className="library-layout">
            <div className="library-text">
              <h3>Every brush. One panel.</h3>
              <p>RadialZ scans your ZBrush library automatically. Search, filter by category, zoom thumbnails, and drag directly into your radial slots. No more hunting.</p>
            </div>
            <div className="library-visual">
              <LibraryVisual />
            </div>
          </div>
        </div>
      </div>
    </section>);

};

// === Visuals ===

// 02 — Custom Slots: a horizontal brush library sits ABOVE the wheel; a brush
// is picked from the library and dragged DOWN into the next empty slot in the
// radial wheel. The library uses a diverse set of brushes (no repeats), and
// drops only land on slots that are currently empty.
//
// Sequence at module scope so the array reference is stable regardless of
// React hot-reload or render quirks.
const EDITOR_SEQUENCE = [
// libIdx points into EDITOR_LIB; slot is the wheel slot to fill (0..7)
{ lib: 0, slot: 0 },
{ lib: 1, slot: 2 },
{ lib: 2, slot: 5 },
{ lib: 3, slot: 3 },
{ lib: 4, slot: 6 },
{ lib: 5, slot: 1 }];

// A diverse, non-repeating set of brush variants for the library row.
const EDITOR_LIB = [0, 8, 11, 4, 17, 13, 9, 10, 16, 19];

const EditorVisual = () => {
  const [step, setStep] = useStateF(0);
  const [t, setT] = useStateF(0); // 0..1 within step
  // Persistent fills on slots (mutated as drops complete)
  const fillsRef = useRefF({}); // slot index -> brush variant
  const [, force] = useStateF(0);

  useEffectF(() => {
    const DUR = 2200; // ms per step
    let raf;
    const start = performance.now();
    const tick = (now) => {
      const elapsed = now - start;
      const len = EDITOR_SEQUENCE.length || 1;
      // Loop the sequence; reset fills at the start of each full cycle so the
      // animation reads as "filling an empty wheel" over and over.
      const totalSteps = Math.floor(elapsed / DUR);
      const stepIdx = totalSteps % len;
      const local = elapsed % DUR / DUR;
      if (totalSteps > 0 && stepIdx === 0 && local < 0.05 &&
      Object.keys(fillsRef.current).length > 0) {
        fillsRef.current = {};
        force((n) => n + 1);
      }
      setStep(stepIdx);
      setT(local);
      // Commit the drop after the snap point so the slot stays filled.
      const cur = EDITOR_SEQUENCE[stepIdx];
      if (cur && local > 0.62 && fillsRef.current[cur.slot] === undefined) {
        fillsRef.current[cur.slot] = EDITOR_LIB[cur.lib];
        force((n) => n + 1);
      }
      raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, []);

  const cur = EDITOR_SEQUENCE[step] || EDITOR_SEQUENCE[0];

  // Library layout (horizontal row, top of stage). Brushes sit at evenly spaced
  // x positions; y is fixed near the top.
  const LIB_COUNT = 6;
  const LIB_GAP = 30;
  const LIB_W = (LIB_COUNT - 1) * LIB_GAP;
  const libX = (i) => -LIB_W / 2 + i * LIB_GAP;
  const LIB_Y = -78;

  // Wheel slot positions (8 slots around a 56-radius ring).
  const slots = Array.from({ length: 8 }, (_, i) => {
    const a = i / 8 * Math.PI * 2 - Math.PI / 2;
    return { x: Math.cos(a) * 56, y: Math.sin(a) * 56 + 18 };
  });

  const src = { x: libX(cur.lib), y: LIB_Y };
  const dst = slots[cur.slot];

  // Easing for the drag arc.
  const easeInOut = (x) => x < 0.5 ? 4 * x * x * x : 1 - Math.pow(-2 * x + 2, 3) / 2;

  let gx = src.x,gy = src.y,gOp = 1,gScale = 1;
  if (t < 0.55) {
    const p = easeInOut(t / 0.55);
    gx = src.x + (dst.x - src.x) * p;
    gy = src.y + (dst.y - src.y) * p;
    gOp = 1;
    gScale = 1 + 0.05 * Math.sin(p * Math.PI);
  } else if (t < 0.72) {
    const p = (t - 0.55) / 0.17;
    gx = dst.x;gy = dst.y;
    gScale = 1 + 0.15 * Math.sin(p * Math.PI);
    gOp = 1 - p * 0.65;
  } else if (t < 0.92) {
    gx = dst.x;gy = dst.y;
    gOp = 0;
  } else {
    const next = EDITOR_SEQUENCE[(step + 1) % EDITOR_SEQUENCE.length] || EDITOR_SEQUENCE[0];
    gx = libX(next.lib);gy = LIB_Y;
    gOp = 0;
  }

  const libItemDim = (i) => i === cur.lib && t < 0.7 ? 0.3 : 0.85;
  const slotIsTarget = (i) => i === cur.slot && t < 0.62 && fillsRef.current[i] === undefined;
  const slotJustLanded = (i) => i === cur.slot && t >= 0.55 && t < 0.78;

  return (
    <div style={{ position: 'relative', width: '100%', height: 250, display: 'grid', placeItems: 'center' }}>
      {/* Library row — horizontal, anchored to the top of the stage */}
      <div style={{
        position: 'absolute', left: '50%', top: '50%',
        transform: `translate(-50%, calc(-50% + ${LIB_Y}px))`,
        display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 6,
        padding: '7px 10px',
        borderRadius: 10,
        background: 'rgba(255,255,255,0.04)',
        border: '1px solid rgba(255,255,255,0.08)'
      }}>
        <div style={{
          display: 'flex', gap: LIB_GAP - 26, alignItems: 'center'
        }}>
          {EDITOR_LIB.slice(0, LIB_COUNT).map((v, i) =>
          <div key={i} style={{
            width: 26, height: 26,
            opacity: libItemDim(i),
            transition: 'opacity .2s'
          }}>
              <BrushIcon variant={v} size={26} />
            </div>
          )}
        </div>
        <div style={{
          fontFamily: 'JetBrains Mono', fontSize: 7, color: 'rgba(237,231,218,0.5)',
          letterSpacing: '0.18em'
        }}>LIBRARY</div>
      </div>

      {/* Wheel — slightly below center so library has room above */}
      <div style={{ position: 'relative', width: 150, height: 150, marginTop: 36 }}>
        {/* Ring */}
        <div style={{
          position: 'absolute', inset: 12, borderRadius: '50%',
          border: '1px dashed rgba(237,231,218,0.15)'
        }} />
        {/* Center dot */}
        <div style={{
          position: 'absolute', left: '50%', top: '50%',
          width: 6, height: 6, borderRadius: '50%',
          background: 'var(--o)', transform: 'translate(-50%,-50%)',
          boxShadow: '0 0 10px 2px var(--o)'
        }} />
        {/* Slots */}
        {slots.map((s, i) => {
          const fillVariant = fillsRef.current[i];
          const targeted = slotIsTarget(i);
          const landed = slotJustLanded(i);
          return (
            <div key={i} style={{
              position: 'absolute',
              left: `calc(50% + ${s.x}px)`, top: `calc(50% + ${s.y - 18}px)`,
              transform: `translate(-50%,-50%) ${landed ? 'scale(1.08)' : 'scale(1)'}`,
              width: 28, height: 28, borderRadius: 6,
              border: targeted ?
              '1.5px dashed var(--o)' :
              '1px solid rgba(237,231,218,0.12)',
              background: landed ?
              'rgba(248,107,51,0.22)' :
              fillVariant !== undefined ?
              'rgba(255,255,255,0.04)' :
              'rgba(255,255,255,0.02)',
              display: 'grid', placeItems: 'center',
              transition: 'transform .2s, background .25s, border-color .2s'
            }}>
              {fillVariant !== undefined &&
              <BrushIcon variant={fillVariant} size={22} />
              }
            </div>);

        })}
      </div>

      {/* Drag ghost */}
      <div style={{
        position: 'absolute', left: '50%', top: '50%',
        transform: `translate(calc(-50% + ${gx}px), calc(-50% + ${gy}px)) scale(${gScale})`,
        width: 28, height: 28, borderRadius: 6,
        background: 'rgba(248,107,51,0.18)',
        border: '1px solid rgba(248,107,51,0.55)',
        display: 'grid', placeItems: 'center',
        boxShadow: '0 6px 18px rgba(0,0,0,0.45), 0 0 0 1px rgba(248,107,51,0.15)',
        opacity: gOp,
        pointerEvents: 'none',
        willChange: 'transform, opacity'
      }}>
        <BrushIcon variant={EDITOR_LIB[cur.lib]} size={22} />
      </div>
    </div>);

};

// 03 — Children Slots: a primary slot with 3 child brushes fanning out
const ChildrenVisual = () => {
  const [open, setOpen] = useStateF(false);
  useEffectF(() => {
    const t = setInterval(() => setOpen((o) => !o), 1800);
    return () => clearInterval(t);
  }, []);
  return (
    <div style={{ position: 'relative', width: 200, height: 180 }}>
      {/* Center cursor dot */}
      <div style={{
        position: 'absolute', left: '50%', top: '50%',
        width: 6, height: 6, borderRadius: '50%',
        background: 'var(--o)', transform: 'translate(-50%,-50%)',
        boxShadow: '0 0 10px 2px var(--o)'
      }} />
      {/* Primary slot (right side) */}
      <div style={{
        position: 'absolute', left: 'calc(50% + 50px)', top: '50%',
        transform: 'translate(-50%,-50%)',
        width: 44, height: 44, borderRadius: 8,
        background: 'rgba(248,107,51,0.18)',
        border: '1.5px solid var(--o)',
        display: 'grid', placeItems: 'center',
        boxShadow: '0 0 18px rgba(248,107,51,0.35)',
        zIndex: 3
      }}>
        <BrushIcon variant={2} size={34} />
      </div>
      {/* 3 child brushes fanning above/below/right when open */}
      {[
      { dx: 56, dy: -38, v: 4 },
      { dx: 78, dy: 0, v: 5 },
      { dx: 56, dy: 38, v: 6 }].
      map((c, i) =>
      <div key={i} style={{
        position: 'absolute', left: `calc(50% + 50px)`, top: `50%`,
        transform: open ?
        `translate(calc(-50% + ${c.dx}px), calc(-50% + ${c.dy}px)) scale(1)` :
        `translate(-50%, -50%) scale(0.4)`,
        opacity: open ? 1 : 0,
        width: 36, height: 36, borderRadius: 7,
        background: 'rgba(255,255,255,0.04)',
        border: '1px solid rgba(26,24,20,0.15)',
        display: 'grid', placeItems: 'center',
        transition: `transform .35s cubic-bezier(.3,.8,.3,1.2) ${i * 50}ms, opacity .25s ${i * 50}ms`,
        zIndex: 2
      }}>
          <BrushIcon variant={c.v} size={26} />
        </div>
      )}
      {/* Connector hint line */}
      <div style={{
        position: 'absolute', left: 0, right: 0, bottom: 8,
        textAlign: 'center',
        fontFamily: 'JetBrains Mono', fontSize: 9,
        letterSpacing: '0.16em', color: 'rgba(26,24,20,0.4)'
      }}>
        HOVER · EXPAND · PICK
      </div>
    </div>);

};

// 04 — ZBrush Bridge: two panels connected by an active link
const BridgeVisual = () => {
  const [pulse, setPulse] = useStateF(0);
  useEffectF(() => {
    let raf,start = performance.now();
    const tick = (t) => {
      setPulse((t - start) / 1800 % 1);
      raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, []);

  // Position of moving dot along the connector
  const dotL = `${10 + pulse * 80}%`;

  return (
    <div style={{ position: 'relative', width: '100%', height: 200 }}>
      {/* RadialZ panel (left) */}
      <div style={{
        position: 'absolute', left: 0, top: '50%', transform: 'translateY(-50%)',
        width: 130, padding: '14px 12px',
        background: 'rgba(248,107,51,0.10)',
        border: '1px solid rgba(248,107,51,0.45)',
        borderRadius: 10,
        display: 'flex', flexDirection: 'column', gap: 8
      }}>
        <div style={{ fontFamily: 'JetBrains Mono', fontSize: 9, letterSpacing: '0.16em', color: 'var(--o)' }}>RADIALZ</div>
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 4 }}>
          {[0, 1, 2, 3, 4, 5].map((v) =>
          <div key={v} style={{
            aspectRatio: '1',
            borderRadius: 4,
            background: v === 2 ? 'rgba(248,107,51,0.25)' : 'rgba(237,231,218,0.06)',
            border: `1px solid ${v === 2 ? 'var(--o)' : 'rgba(237,231,218,0.08)'}`,
            display: 'grid', placeItems: 'center'
          }}>
              <BrushIcon variant={v} size={18} />
            </div>
          )}
        </div>
        <div style={{ fontFamily: 'JetBrains Mono', fontSize: 8, color: 'rgba(237,231,218,0.5)', letterSpacing: '0.1em' }}>
          → SELECTED
        </div>
      </div>

      {/* Connector + bridge label */}
      <div style={{
        position: 'absolute', left: 130, right: 130, top: '50%',
        height: 32, transform: 'translateY(-50%)',
        display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center'
      }}>
        {/* Label */}
        <div style={{
          fontFamily: 'JetBrains Mono', fontSize: 8, letterSpacing: '0.18em',
          color: 'rgba(237,231,218,0.5)', marginBottom: 4
        }}>
          BRIDGE.PLUGIN
        </div>
        {/* Line */}
        <div style={{
          position: 'relative', width: '100%', height: 2,
          background: 'linear-gradient(90deg, var(--o), rgba(248,107,51,0.3), var(--o))',
          borderRadius: 1
        }}>
          {/* Moving pulse */}
          <div style={{
            position: 'absolute', left: dotL, top: '50%',
            transform: 'translate(-50%, -50%)',
            width: 8, height: 8, borderRadius: '50%',
            background: '#fff',
            boxShadow: '0 0 12px 2px var(--o)'
          }} />
        </div>
        <div style={{
          marginTop: 4, fontFamily: 'JetBrains Mono', fontSize: 8,
          color: 'var(--o)', letterSpacing: '0.16em'
        }}>
          ▸ LOAD
        </div>
      </div>

      {/* ZBrush panel (right) — mocked UI */}
      <div style={{
        position: 'absolute', right: 0, top: '50%', transform: 'translateY(-50%)',
        width: 130, padding: '14px 12px',
        background: 'rgba(58,58,62,0.6)',
        border: '1px solid rgba(237,231,218,0.18)',
        borderRadius: 10,
        display: 'flex', flexDirection: 'column', gap: 8
      }}>
        <div style={{ fontFamily: 'JetBrains Mono', fontSize: 9, letterSpacing: '0.16em', color: 'rgba(237,231,218,0.7)' }}>ZBRUSH</div>
        {/* Tool palette mock */}
        <div style={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
          {[0, 1, 2, 3].map((i) =>
          <div key={i} style={{
            height: 16, borderRadius: 3,
            background: i === 2 ? 'rgba(248,107,51,0.55)' : 'rgba(237,231,218,0.06)',
            border: `1px solid ${i === 2 ? 'var(--o)' : 'rgba(237,231,218,0.08)'}`,
            boxShadow: i === 2 ? '0 0 10px rgba(248,107,51,0.4)' : 'none',
            display: 'flex', alignItems: 'center', padding: '0 6px',
            fontFamily: 'JetBrains Mono', fontSize: 7, letterSpacing: '0.1em',
            color: i === 2 ? '#fff' : 'rgba(237,231,218,0.4)'
          }}>
              {['STANDARD', 'CLAY', 'DAMSTD', 'MOVE'][i]}
            </div>
          )}
        </div>
        <div style={{ fontFamily: 'JetBrains Mono', fontSize: 8, color: 'var(--o)', letterSpacing: '0.1em' }}>
          ✓ ACTIVE
        </div>
      </div>
    </div>);

};

// 05 — Brush Library: an auto-scanning library panel with search + category
// filters. The full brush registry is sourced from window.brushRegistry; chips
// filter by case-insensitive substring match on the brush name. No repeats —
// every cell is a unique brush.
const LibraryVisual = () => {
  // Build the library directly from the live registry so we never repeat icons.
  const FULL_LIB = useRefF(null);
  if (!FULL_LIB.current) {
    const reg = (window.brushRegistry || []).map((b, i) => ({
      i,
      name: b && b.name ? b.name : `Brush ${i}`
    }));
    FULL_LIB.current = reg;
  }

  // Each category is a label + the keywords it filters on (matched against
  // brush name, case-insensitive). 'ALL' shows everything.
  const cats = [
  { label: 'ALL', keys: null },
  { label: 'STANDARD', keys: ['standard'] },
  { label: 'CLAY', keys: ['clay'] },
  { label: 'POLISH', keys: ['polish', 'hpolish'] },
  { label: 'TRIM', keys: ['trim'] },
  { label: 'MOVE', keys: ['move'] },
  { label: 'MASK', keys: ['mask'] },
  { label: 'CUSTOM', keys: ['zmodeler', 'imm', 'morph', 'rake'] }];

  const [activeCat, setActiveCat] = useStateF(0);
  const [hover, setHover] = useStateF(null);

  const matches = (name, keys) => {
    if (!keys) return true;
    const n = name.toLowerCase();
    return keys.some((k) => n.includes(k));
  };
  const visible = FULL_LIB.current.filter((b) => matches(b.name, cats[activeCat].keys));

  return (
    <div style={{
      width: '100%',
      borderRadius: 14,
      background: 'rgba(26,24,20,0.04)',
      border: '1px solid rgba(26,24,20,0.08)',
      padding: 14,
      display: 'flex', flexDirection: 'column', gap: 12
    }}>
      {/* Top bar: search + filter chips */}
      <div style={{ display: 'flex', gap: 10, alignItems: 'center', flexWrap: 'wrap' }}>
        <div style={{
          flex: '0 0 200px',
          display: 'flex', alignItems: 'center', gap: 8,
          padding: '7px 11px', borderRadius: 8,
          background: 'rgba(255,255,255,0.6)',
          border: '1px solid rgba(26,24,20,0.1)',
          fontFamily: 'JetBrains Mono', fontSize: 11,
          color: 'rgba(26,24,20,0.5)'
        }}>
          <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5">
            <circle cx="11" cy="11" r="7" />
            <path d="m20 20-3.5-3.5" />
          </svg>
          <span>search brushes…</span>
        </div>
        <div style={{ display: 'flex', gap: 4, flexWrap: 'wrap' }}>
          {cats.map((c, i) =>
          <button key={c.label}
          onClick={() => setActiveCat(i)}
          style={{
            fontFamily: 'JetBrains Mono', fontSize: 9, letterSpacing: '0.12em',
            padding: '5px 9px', borderRadius: 999,
            background: activeCat === i ? 'var(--o)' : 'transparent',
            color: activeCat === i ? '#fff' : 'rgba(26,24,20,0.65)',
            border: `1px solid ${activeCat === i ? 'var(--o)' : 'rgba(26,24,20,0.18)'}`,
            cursor: 'pointer',
            transition: 'all .15s'
          }}>
              {c.label}
            </button>
          )}
        </div>
      </div>

      {/* Thumbnail grid — one cell per matching brush, no repeats */}
      <div style={{
        display: 'grid',
        gridTemplateColumns: 'repeat(10, 1fr)',
        gap: 6,
        minHeight: 120
      }}>
        {visible.map((b) => {
          const isHover = hover === b.i;
          return (
            <div key={b.i}
            onMouseEnter={() => setHover(b.i)}
            onMouseLeave={() => setHover(null)}
            title={b.name}
            style={{
              aspectRatio: '1',
              borderRadius: 7,
              background: isHover ? 'rgba(248,107,51,0.12)' : 'rgba(255,255,255,0.5)',
              border: `1px solid ${isHover ? 'var(--o)' : 'rgba(26,24,20,0.07)'}`,
              display: 'grid', placeItems: 'center',
              transition: 'all .15s',
              cursor: 'grab',
              transform: isHover ? 'translateY(-2px)' : 'none',
              boxShadow: isHover ? '0 6px 16px rgba(26,24,20,0.12)' : 'none'
            }}>
              <BrushIcon variant={b.i} size={26} />
            </div>);

        })}
        {visible.length === 0 &&
        <div style={{
          gridColumn: '1 / -1',
          padding: '20px 0',
          textAlign: 'center',
          fontFamily: 'JetBrains Mono', fontSize: 10,
          letterSpacing: '0.14em', color: 'rgba(26,24,20,0.35)'
        }}>
            no brushes match this filter
          </div>
        }
      </div>

      {/* Footer hint */}
      <div style={{
        display: 'flex', justifyContent: 'space-between', alignItems: 'center',
        fontFamily: 'JetBrains Mono', fontSize: 9,
        letterSpacing: '0.14em', color: 'rgba(26,24,20,0.45)'
      }}>
        <span>{visible.length} of {FULL_LIB.current.length} brushes</span>
        <span>↳ drag onto any slot</span>
      </div>
    </div>);

};

// Drifting brush icons behind feature cards
const FeaturesBgBrushes = () => {
  const items = [
  { x: '4%', y: '8%', v: 0, s: 50 },
  { x: '92%', y: '14%', v: 4, s: 60 },
  { x: '8%', y: '60%', v: 2, s: 48 },
  { x: '95%', y: '70%', v: 7, s: 56 },
  { x: '46%', y: '92%', v: 3, s: 42 },
  { x: '24%', y: '95%', v: 5, s: 40 }];

  return (
    <div className="features-bg-icons">
      {items.map((it, i) =>
      <div key={i} className="bg-brush" style={{ left: it.x, top: it.y, width: it.s, height: it.s }}>
          <BrushIcon variant={it.v} size={it.s} />
        </div>
      )}
    </div>);

};

// Smooth concave curve cutting the warm Features section into the dark How-it-works.
// Shallow dome — doesn't eat into the "Three steps" headline. Padding on the
// .how-wrap clears extra space below the curve.
const HowTopCurve = () =>
<svg className="how-top-curve" viewBox="0 0 1440 240" preserveAspectRatio="none" aria-hidden="true">
    <path d="M0,0 Q720,300 1440,0 L1440,0 Z" fill="#ede7da" />
  </svg>;


// HOW IT WORKS section — wide horizontal step cards that stack into a deck
// as you scroll (each new card slides up and the previous one recedes via
// scale-down + opacity dim). Uses position:sticky for the deck mechanic and a
// scroll RAF loop to drive the recede effect for cards being covered.
const HowItWorks = () => {
  const stackRef = useRefF(null);

  useEffectF(() => {
    const stack = stackRef.current;
    if (!stack) return;
    let raf = 0;
    const update = () => {
      raf = 0;
      // Deck-stack effect: each card recedes as the next slides up over it.
      const cards = stack.querySelectorAll('.step-card');
      for (let i = 0; i < cards.length; i++) {
        const card = cards[i];
        const next = cards[i + 1];
        let p = 0;
        if (next) {
          const cardRect = card.getBoundingClientRect();
          const nextRect = next.getBoundingClientRect();
          const distance = nextRect.top - cardRect.top;
          const range = cardRect.height;
          p = Math.max(0, Math.min(1, 1 - distance / range));
        }
        const scale = 1 - 0.06 * p;
        const yOff = -p * 10;
        const op = 1 - 0.55 * p;
        card.style.transform = `translateY(${yOff}px) scale(${scale})`;
        card.style.opacity = String(op);
      }
    };
    const onScroll = () => {
      if (raf) return;
      raf = requestAnimationFrame(update);
    };
    update();
    window.addEventListener('scroll', onScroll, { passive: true });
    window.addEventListener('resize', onScroll);
    return () => {
      if (raf) cancelAnimationFrame(raf);
      window.removeEventListener('scroll', onScroll);
      window.removeEventListener('resize', onScroll);
    };
  }, []);

  return (
    <section className="how-wrap" id="how" data-screen-label="06 How it works">
      <div className="section-head">
        <div>
          <div className="lead">How it works</div>
          <h2>Three steps. <span className="it">Install
Connect, create!</span></h2>
        </div>
        <div className="desc">
          Install. Bind one Bridge hotkey. Build your menus.
          The installer does the wiring. You just pick brushes.
        </div>
      </div>

      <div className="steps-stack" ref={stackRef}>
        <article className="step-card" style={{ '--i': 0 }}>
          <div className="step-card-text">
            <span className="step-num">STEP 01</span>
            <h3>Run the installer.</h3>
            <p>RadialZ detects your ZBrush version and wires up the Python bridge automatically. On recent ZBrush versions you can point it to a custom brush folder. Then you're done.</p>
          </div>
          <div className="step-card-visual">
            <div className="step-visual-bg" aria-hidden="true" />
            <div className="step-visual-inner">
              <InstallerVisual />
            </div>
          </div>
        </article>

        <article className="step-card" style={{ '--i': 1 }}>
          <div className="step-card-text">
            <span className="step-num">STEP 02</span>
            <h3>Bind the Bridge hotkey.</h3>
            <p>Pick a single key. The Bridge listens on it, every brush you select in RadialZ loads in ZBrush through it. Set once, forget forever.</p>
          </div>
          <div className="step-card-visual">
            <div className="step-visual-bg" aria-hidden="true" />
            <div className="step-visual-inner">
              <BridgeBindVisual />
            </div>
          </div>
        </article>

        <article className="step-card" style={{ '--i': 2 }}>
          <div className="step-card-text">
            <span className="step-num">STEP 03</span>
            <h3>Build your menus.</h3>
            <p>Drop brushes into a radial menu in the editor. Assign it a hotkey. Make as many menus as you want, one per workflow. Press, pick, sculpt.</p>
          </div>
          <div className="step-card-visual">
            <div className="step-visual-bg" aria-hidden="true" />
            <div className="step-visual-inner">
              <MenusVisual />
            </div>
          </div>
        </article>
      </div>
    </section>);
};

// === Step visuals ===

// STEP 01 — Installer card with auto-detect checklist + manual SYNC click
const InstallerVisual = () => {
  const items = [
  'ZBrush 2026 detected',
  'Python bridge installed',
  'Library ready to sync'];

  // phase 0=idle, 1=item0 ticked, 2=item1 ticked, 3=sync btn pulsing,
  //       4=sync pressed, 5=item2 ticked (library synced)
  const [phase, setPhase] = useStateF(0);
  useEffectF(() => {
    let alive = true;
    let timeouts = [];
    const schedule = (delay, fn) => {
      const t = setTimeout(() => {if (alive) fn();}, delay);
      timeouts.push(t);
    };
    const run = () => {
      timeouts.forEach(clearTimeout);
      timeouts = [];
      setPhase(0);
      schedule(500, () => setPhase(1));
      schedule(1100, () => setPhase(2));
      schedule(1700, () => setPhase(3));
      schedule(3100, () => setPhase(4));
      schedule(3350, () => setPhase(5));
      schedule(5400, run);
    };
    run();
    return () => {alive = false;timeouts.forEach(clearTimeout);};
  }, []);

  const itemDone = (i) => i === 0 && phase >= 1 || i === 1 && phase >= 2 || i === 2 && phase >= 5;
  const showSyncBtn = phase >= 3 && phase < 5;
  const pressedSync = phase === 4;

  return (
    <div style={{
      width: 252,
      padding: '14px 14px 12px',
      border: '1px solid var(--line-strong)',
      borderRadius: 12,
      background: 'linear-gradient(180deg, rgba(255,255,255,0.04), rgba(255,255,255,0.01))',
      boxShadow: '0 10px 30px -10px rgba(0,0,0,0.5)'
    }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 12 }}>
        <div style={{
          width: 30, height: 30, borderRadius: 7,
          backgroundImage: `url(${window.__resources['app-icon-radialz']})`,
          backgroundSize: 'cover',
          backgroundPosition: 'center',
          boxShadow: '0 0 0 1px rgba(248,107,51,0.4), 0 4px 14px rgba(248,107,51,0.3)'
        }} />
        <div style={{ flex: 1 }}>
          <div style={{ fontSize: 12, fontWeight: 600, color: 'var(--t)' }}>RadialZ.app</div>
          <div style={{ fontFamily: 'JetBrains Mono', fontSize: 9, color: 'var(--t-3)', letterSpacing: '0.1em' }}>INSTALLER · v1.0</div>
        </div>
      </div>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
        {items.map((label, i) => {
          const isDone = itemDone(i);
          const isSyncRow = i === 2;
          return (
            <div key={i} style={{
              display: 'flex', alignItems: 'center', gap: 8,
              fontFamily: 'JetBrains Mono', fontSize: 10, letterSpacing: '0.04em',
              color: isDone ? 'var(--o)' : 'var(--t-3)',
              transition: 'color .25s'
            }}>
              <div style={{
                width: 14, height: 14, borderRadius: 4,
                border: `1px solid ${isDone ? 'var(--o)' : 'var(--line-strong)'}`,
                background: isDone ? 'var(--o)' : 'transparent',
                display: 'grid', placeItems: 'center',
                color: '#fff', fontSize: 9,
                transition: 'all .25s'
              }}>
                {isDone ? '✓' : ''}
              </div>
              <span style={{ flex: 1 }}>{label}</span>
              {isSyncRow && showSyncBtn &&
              <span className={pressedSync ? 'sync-pill pressed' : 'sync-pill pulse'}>
                  SYNC
                </span>
              }
            </div>);

        })}
      </div>
    </div>);

};

// STEP 02 — Single key pressed → bridge link lights up between RadialZ and ZBrush.
// Self-sized at 280px so it doesn't depend on parent width (which was breaking
// when scaled inside the card). Cleaner spacing, clearer wire + signal pulse.
const BridgeBindVisual = () => {
  const [pressed, setPressed] = useStateF(false);
  useEffectF(() => {
    let alive = true;
    let to;
    const cycle = () => {
      if (!alive) return;
      setPressed(true);
      to = setTimeout(() => {if (alive) setPressed(false);}, 1200);
    };
    cycle();
    const id = setInterval(cycle, 2800);
    return () => {alive = false;clearInterval(id);clearTimeout(to);};
  }, []);

  const accent = '#f86b33';
  const wirePressed = `linear-gradient(90deg, ${accent}, rgba(248,107,51,0.9), ${accent})`;
  const wireRest = 'linear-gradient(90deg, rgba(255,255,255,0.14), rgba(255,255,255,0.06), rgba(255,255,255,0.14))';

  const appTile = (variant) => ({
    width: 38, height: 38, borderRadius: 9,
    backgroundImage: variant === 'orange'
      ? `url(${window.__resources['app-icon-radialz']})`
      : `url(${window.__resources['app-icon-zbrush']})`,
    backgroundSize: 'cover',
    backgroundPosition: 'center',
    border: variant === 'orange' ? 'none' : '1px solid #4a4a4e',
    boxShadow: pressed ?
    variant === 'orange' ?
    `0 0 22px ${accent}88, 0 0 0 1px ${accent}99` :
    `0 0 18px ${accent}55, 0 0 0 1px rgba(255,255,255,0.25)` :
    variant === 'orange' ? '0 0 0 1px rgba(248,107,51,0.35)' : 'none',
    transition: 'box-shadow .3s'
  });

  return (
    <div style={{
      position: 'relative',
      width: 280,
      display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 22
    }}>
      <div style={{
        position: 'relative',
        width: '100%', height: 70,
        display: 'flex', alignItems: 'center', justifyContent: 'space-between'
      }}>
        {/* Connector wire behind everything */}
        <div style={{
          position: 'absolute',
          left: 38, right: 38, top: 19,
          height: 1.5,
          background: pressed ? wirePressed : wireRest,
          transition: 'background .25s',
          zIndex: 0
        }} />

        {/* Left app */}
        <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 8, zIndex: 1 }}>
          <div style={appTile('orange')} />
          <div style={{ fontFamily: 'JetBrains Mono', fontSize: 8, color: pressed ? accent : 'var(--t-3)', letterSpacing: '0.18em', transition: 'color .25s' }}>RADIALZ</div>
        </div>

        {/* Center F1 key — absolutely centered on top of the wire */}
        <div style={{
          position: 'absolute',
          left: '50%', top: 19, transform: 'translate(-50%, -50%)',
          zIndex: 2
        }}>
          <span style={{
            display: 'inline-block',
            fontFamily: 'JetBrains Mono', fontSize: 18, fontWeight: 700,
            padding: '9px 18px',
            background: pressed ? accent : 'rgba(20,20,24,0.95)',
            color: pressed ? '#fff' : 'var(--t)',
            border: `1px solid ${pressed ? accent : 'rgba(255,255,255,0.16)'}`,
            borderRadius: 10,
            boxShadow: pressed ?
            `0 10px 28px ${accent}77, inset 0 -1px 0 rgba(0,0,0,0.25)` :
            'inset 0 -3px 0 rgba(0,0,0,0.45), 0 6px 18px rgba(0,0,0,0.5)',
            transform: pressed ? 'translateY(2px)' : 'translateY(0)',
            transition: 'all .15s',
            letterSpacing: '0.04em'
          }}>F1</span>
        </div>

        {/* Right app */}
        <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 8, zIndex: 1 }}>
          <div style={appTile('gray')} />
          <div style={{ fontFamily: 'JetBrains Mono', fontSize: 8, color: pressed ? accent : 'var(--t-3)', letterSpacing: '0.18em', transition: 'color .25s' }}>ZBRUSH</div>
        </div>
      </div>

      {/* Caption */}
      <div style={{
        fontFamily: 'JetBrains Mono', fontSize: 9, letterSpacing: '0.22em',
        color: pressed ? accent : 'var(--t-3)',
        transition: 'color .25s',
        textAlign: 'center'
      }}>
        {pressed ? '▸ BRIDGE ACTIVE' : 'PRESS · BIND ONCE'}
      </div>
    </div>);

};

// STEP 03 — A radial menu that swaps between configs. Each menu has its own
// hotkey + label, and the DETAIL menu uses the secondary cyan accent to hint
// that menus can be themed.
const MenusVisual = () => {
  const menus = [
  { label: 'BLOCKOUT', key: 'Q', brushes: [0, 1, 2, 3, 4, 5, 6, 7], accent: 'orange' },
  { label: 'DETAIL', key: 'W', brushes: [3, 4, 5, 6, 7, 0, 1, 2], accent: 'cyan' },
  { label: 'POLISH', key: 'E', brushes: [4, 5, 6, 7, 0, 1, 2, 3], accent: 'orange' }];

  const [idx, setIdx] = useStateF(0);
  const [fading, setFading] = useStateF(false);

  useEffectF(() => {
    let alive = true;
    let timers = [];
    const sched = (delay, fn) => {
      const t = setTimeout(() => {if (alive) fn();}, delay);
      timers.push(t);
    };
    const cycle = () => {
      timers.forEach(clearTimeout);
      timers = [];
      sched(2000, () => setFading(true));
      sched(2320, () => setIdx((i) => (i + 1) % menus.length));
      sched(2380, () => setFading(false));
      sched(2400, cycle);
    };
    cycle();
    return () => {alive = false;timers.forEach(clearTimeout);};
  }, []);

  const m = menus[idx];
  const isCyan = m.accent === 'cyan';
  const accent = isCyan ? '#4dc8e9' : '#f86b33';
  const accentSoft = isCyan ? 'rgba(77,200,233,0.16)' : 'rgba(248,107,51,0.16)';
  const accentGlow = isCyan ? 'rgba(77,200,233,0.45)' : 'rgba(248,107,51,0.5)';

  // 8 brush positions around the ring
  const positions = Array.from({ length: 8 }, (_, i) => {
    const a = i / 8 * Math.PI * 2 - Math.PI / 2;
    return { x: Math.cos(a) * 50, y: Math.sin(a) * 50 };
  });

  return (
    <div style={{ position: 'relative', width: '100%', height: 220, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'space-between', padding: '8px 0 4px', gap: 18 }}>
      {/* Header pill: menu name + hotkey */}
      <div style={{
        display: 'inline-flex', alignItems: 'center', gap: 7,
        padding: '5px 6px 5px 12px',
        background: accentSoft,
        border: `1px solid ${accent}`,
        borderRadius: 999,
        transition: 'all .25s'
      }}>
        <span style={{
          fontFamily: 'JetBrains Mono', fontSize: 10, letterSpacing: '0.16em',
          color: accent, transition: 'color .25s'
        }}>{m.label}</span>
        <span style={{
          fontFamily: 'JetBrains Mono', fontSize: 10, fontWeight: 700,
          padding: '2px 8px', borderRadius: 999,
          background: accent, color: '#0a0a0b',
          letterSpacing: '0.06em',
          transition: 'background .25s'
        }}>{m.key}</span>
      </div>

      {/* The radial menu itself */}
      <div style={{ position: 'relative', width: 140, height: 130, marginTop: 4 }}>
        {/* Dashed ring */}
        <div style={{
          position: 'absolute', left: '50%', top: '50%',
          width: 110, height: 110, borderRadius: '50%',
          transform: 'translate(-50%,-50%)',
          border: '1px dashed rgba(255,255,255,0.12)'
        }} />
        {/* Center cursor */}
        <div style={{
          position: 'absolute', left: '50%', top: '50%',
          width: 6, height: 6, borderRadius: '50%',
          background: accent, transform: 'translate(-50%,-50%)',
          boxShadow: `0 0 14px 2px ${accentGlow}`,
          transition: 'all .25s',
          zIndex: 2
        }} />
        {/* 8 brushes */}
        {positions.map((p, i) =>
        <div key={i} style={{
          position: 'absolute',
          left: `calc(50% + ${p.x}px)`, top: `calc(50% + ${p.y}px)`,
          transform: `translate(-50%,-50%) scale(${fading ? 0.55 : 1})`,
          opacity: fading ? 0 : 1,
          transition: 'transform .22s ease, opacity .22s ease',
          transitionDelay: `${i * 14}ms`
        }}>
            <BrushIcon variant={m.brushes[i]} size={22} />
          </div>
        )}
      </div>

      {/* Menu pagination dots */}
      <div style={{ display: 'flex', gap: 6 }}>
        {menus.map((mm, i) => {
          const isHere = i === idx;
          const dotColor = isHere ?
          mm.accent === 'cyan' ? '#4dc8e9' : '#f86b33' :
          'rgba(255,255,255,0.18)';
          return (
            <div key={i} style={{
              width: isHere ? 18 : 6, height: 6, borderRadius: 3,
              background: dotColor,
              transition: 'all .3s'
            }} />);

        })}
      </div>
    </div>);

};

window.Features = Features;
window.HowItWorks = HowItWorks;