/* global React, Icons, Badge, Btn, Pill, Meter, Score, Stat, Eyebrow */
const { useState, useMemo } = React;

/* =========================================================================
   BUILD CONFIGURATOR — flagship interactive
   ========================================================================= */

const GEAR_CATALOG = {
  camera: [
    { id: 'osmo-pocket-3', name: 'DJI Osmo Pocket 3',     price: 519, draw: 4.2,  weight: 179, score: 9.1, tag: 'IRL favorite' },
    { id: 'sony-zv-1f',    name: 'Sony ZV-1F',            price: 449, draw: 3.8,  weight: 229, score: 8.2, tag: null },
    { id: 'gopro-13',      name: 'GoPro Hero 13 Black',   price: 399, draw: 5.5,  weight: 154, score: 8.8, tag: 'Rugged' },
    { id: 'iphone-only',   name: 'iPhone (use yours)',    price: 0,   draw: 3.0,  weight: 200, score: 7.4, tag: 'Free' },
  ],
  audio: [
    { id: 'rode-wgo3',     name: 'Rode Wireless GO 3',    price: 349, draw: 1.2, weight: 84,  score: 9.0, tag: 'Lab pick' },
    { id: 'dji-mic-2',     name: 'DJI Mic 2',             price: 349, draw: 1.4, weight: 91,  score: 8.7, tag: null },
    { id: 'shure-mv7',     name: 'Shure MV7+',            price: 279, draw: 2.0, weight: 550, score: 8.0, tag: 'Static' },
  ],
  router: [
    { id: 'peplink-br1',   name: 'Peplink MAX BR1 Pro 5G',price: 1499, draw: 12,  weight: 470, score: 9.4, tag: 'Pro' },
    { id: 'gl-mt2500',     name: 'GL.iNet Brume 2',       price: 159,  draw: 4,   weight: 180, score: 7.6, tag: 'Budget' },
    { id: 'liveu-solo-pro',name: 'LiveU Solo Pro',        price: 1499, draw: 14,  weight: 380, score: 9.2, tag: null },
    { id: 'none-router',   name: 'No router (tethered)',  price: 0,    draw: 0,   weight: 0,   score: 6.0, tag: 'Skip' },
  ],
  power: [
    { id: 'omni-20',       name: 'Omnicharge Omni 20+',   price: 199, draw: 0,   weight: 510, capacity: 70, score: 8.8, tag: null },
    { id: 'ec-river-2',    name: 'EcoFlow River 2',       price: 239, draw: 0,   weight: 3500, capacity: 256, score: 8.6, tag: 'Stationary' },
    { id: 'anker-737',     name: 'Anker 737 PowerCore',   price: 149, draw: 0,   weight: 632, capacity: 86,  score: 9.1, tag: 'Best value' },
    { id: 'none-power',    name: 'None (run off phone)',  price: 0,   draw: 0,   weight: 0,   capacity: 0,   score: 5.0, tag: 'Risky' },
  ],
  pack: [
    { id: 'gunrun-v3',     name: 'Gunrun Mark V backpack',price: 749, draw: 0, weight: 1800, score: 9.6, tag: 'Flagship' },
    { id: 'peak-30',       name: 'Peak Design Travel 30L',price: 299, draw: 0, weight: 1500, score: 7.9, tag: 'Generic' },
    { id: 'sling',         name: 'Just a sling bag',      price: 89,  draw: 0, weight: 400,  score: 6.5, tag: 'Starter' },
  ],
};

const PRESETS = {
  starter:   { name: 'Starter',        budget: 500,   audience: 'Beginner', env: 'Phone' },
  mobile:    { name: 'Mobile Creator', budget: 1500,  audience: 'Beginner', env: 'Phone' },
  travel:    { name: 'Travel IRL',     budget: 2500,  audience: 'Upgrader', env: 'Travel' },
  backpack:  { name: 'Pro Backpack',   budget: 4500,  audience: 'Pro',      env: 'Backpack' },
  event:     { name: 'Event Broadcast',budget: 6000,  audience: 'Pro',      env: 'Event' },
  house:     { name: 'StreamerHouse',  budget: 9500,  audience: 'Pro',      env: 'Stationary' },
};

// The bonding/data market bifurcated into three lanes in 2025-26 — not a
// single starter→pro ladder. Each lane is a different bet, not a different
// budget. Builds are grouped by the lane they actually live in.
const LANES = [
  {
    id: 'vertical',
    cls: 'is-vertical',
    title: 'Phone · Vertical',
    tag: 'Osmo + Speedify lane',
    sub: 'Mobile-first, vertical-native, software bonding. TikTok Live / Kick.',
    presets: ['starter', 'mobile'],
  },
  {
    id: 'sleeper',
    cls: 'is-sleeper',
    title: 'Sleeper · BELABOX',
    tag: 'BELABOX + Calyx lane',
    sub: 'DIY / open-source bonding, no monthly hardware tax. Mixed rigs.',
    presets: ['travel', 'house'],
  },
  {
    id: 'prestige',
    cls: 'is-prestige',
    title: 'Prestige · LiveU',
    tag: 'Solo PRO + UnlimitedIRL lane',
    sub: 'Carrier-grade hardware, broadcast-grade SLA. Pays for itself at scale.',
    presets: ['backpack', 'event'],
  },
];

const PRESET_PICKS = {
  starter:  { camera: 'iphone-only',   audio: 'shure-mv7',  router: 'none-router',    power: 'none-power',  pack: 'sling' },
  mobile:   { camera: 'sony-zv-1f',    audio: 'dji-mic-2',  router: 'gl-mt2500',      power: 'anker-737',   pack: 'peak-30' },
  travel:   { camera: 'osmo-pocket-3', audio: 'rode-wgo3',  router: 'gl-mt2500',      power: 'omni-20',     pack: 'peak-30' },
  backpack: { camera: 'osmo-pocket-3', audio: 'rode-wgo3',  router: 'peplink-br1',    power: 'omni-20',     pack: 'gunrun-v3' },
  event:    { camera: 'gopro-13',      audio: 'rode-wgo3',  router: 'liveu-solo-pro', power: 'omni-20',     pack: 'gunrun-v3' },
  house:    { camera: 'osmo-pocket-3', audio: 'shure-mv7',  router: 'peplink-br1',    power: 'ec-river-2',  pack: 'peak-30' },
};

const SLOTS = [
  { key: 'camera', label: 'Camera',         icon: <Icons.camera size={16} /> },
  { key: 'audio',  label: 'Audio',          icon: <Icons.mic size={16} /> },
  { key: 'router', label: 'Bonded uplink',  icon: <Icons.router size={16} /> },
  { key: 'power',  label: 'Power',          icon: <Icons.battery size={16} /> },
  { key: 'pack',   label: 'Pack / mount',   icon: <Icons.backpack size={16} /> },
];

const PRESET_KEYS = Object.keys(PRESETS);

const BuildPage = () => {
  const [presetId, setPresetId] = useState('backpack');
  const [picks, setPicks] = useState(PRESET_PICKS.backpack);
  const [carriers, setCarriers] = useState(3);
  const [hours, setHours] = useState(4);
  const [bitrate, setBitrate] = useState(6);

  const applyPreset = (id) => {
    setPresetId(id);
    setPicks(PRESET_PICKS[id]);
  };
  const swap = (slot, id) => {
    setPicks({ ...picks, [slot]: id });
  };

  // Roll up the selected items
  const items = SLOTS.map((s) => {
    const item = GEAR_CATALOG[s.key].find(g => g.id === picks[s.key]);
    return { slot: s, item };
  });

  const total = items.reduce((sum, it) => sum + (it.item?.price || 0), 0);
  const totalDraw = items.reduce((sum, it) => sum + (it.item?.draw || 0), 0);
  const totalWeight = items.reduce((sum, it) => sum + (it.item?.weight || 0), 0);
  const powerItem = items.find(i => i.slot.key === 'power').item;
  const powerCap = powerItem.capacity || 0; // Wh
  const runtime = totalDraw > 0 ? powerCap / totalDraw : 0;
  const avgScore = items.filter(i => i.item).reduce((s, i) => s + i.item.score, 0) / items.filter(i => i.item).length;

  // Data plan strategy
  const dataMonthly = 60 + carriers * 65 + (bitrate > 8 ? 40 : 0);
  const dataNeeded = bitrate * 0.45 * hours;

  return (
    <div className="page-fade">
      {/* HEADER ========================================================== */}
      <section style={{ borderBottom: '1px solid var(--line)', padding: '40px 0 32px' }}>
        <div className="container">
          <div className="row between" style={{ alignItems: 'flex-end' }}>
            <div>
              <Eyebrow>Flagship tool · Build Configurator v2.1</Eyebrow>
              <h1 className="mt-4" style={{ fontSize: 56 }}>Configure your build.</h1>
              <p className="lede mt-3" style={{ maxWidth: '54ch' }}>
                Pick a preset, swap any component. The BOM, runtime, weight, and data
                strategy recalculate live. Every part is tested in our lab.
              </p>
            </div>
            <div className="col gap-2">
              <div className="row gap-2">
                <Badge kind="tested" dot>Live calc</Badge>
                <Badge kind="updated">Catalog · Q2 ’26</Badge>
              </div>
              <div className="mono fs-12 text-fg4" style={{ textAlign: 'right' }}>BUILD ID · BC-{presetId.toUpperCase()}-{Math.abs(hash(JSON.stringify(picks))).toString(36).slice(0,5).toUpperCase()}</div>
            </div>
          </div>
        </div>
      </section>

      {/* PRESETS — three lanes, not a ladder ============================ */}
      <section style={{ background: 'var(--bg-1)', borderBottom: '1px solid var(--line)', padding: '32px 0' }}>
        <div className="container">
          <div className="row between mb-4">
            <div>
              <Eyebrow>01 · Pick your lane</Eyebrow>
              <h3 className="mt-2">Three lanes, not a ladder.</h3>
              <p className="text-fg3 fs-13 mt-1" style={{ maxWidth: '60ch' }}>
                The 2026 IRL market bifurcated. Each lane is a different bet — not a different budget tier.
                Pick the lane your stream actually lives in, then a starting build inside it.
              </p>
            </div>
            <span className="mono fs-12 text-fg4">Tip: swap any part after selecting</span>
          </div>
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 14 }}>
            {LANES.map(lane => (
              <div key={lane.id} className={`lane ${lane.cls}`}>
                <div className="lane-head">
                  <span className="lane-tag">{lane.tag}</span>
                  <span className="mono fs-12 text-fg4">{lane.presets.length} builds</span>
                </div>
                <div className="lane-title">{lane.title}</div>
                <p className="text-fg3 fs-13 mt-2" style={{ minHeight: 40 }}>{lane.sub}</p>
                <div className="col gap-2 mt-3">
                  {lane.presets.map(k => (
                    <button key={k} onClick={() => applyPreset(k)}
                      className="card"
                      style={{
                        padding: '12px 14px',
                        cursor: 'pointer',
                        background: presetId === k ? 'var(--bg-3)' : 'var(--bg-2)',
                        borderColor: presetId === k ? 'var(--accent)' : 'var(--line-2)',
                        textAlign: 'left',
                        position: 'relative',
                      }}>
                      {presetId === k && <Icons.check size={12} style={{ position: 'absolute', top: 10, right: 10, color: 'var(--accent)' }} />}
                      <div className="mono fs-12" style={{ color: presetId === k ? 'var(--accent)' : 'var(--fg-4)', letterSpacing: '0.1em', textTransform: 'uppercase' }}>{k}</div>
                      <div className="row between mt-1">
                        <div style={{ fontSize: 14, fontWeight: 500 }}>{PRESETS[k].name}</div>
                        <div className="fs-12 text-fg3 mono">~${PRESETS[k].budget.toLocaleString()}</div>
                      </div>
                    </button>
                  ))}
                </div>
              </div>
            ))}
          </div>
        </div>
      </section>

      {/* MAIN BUILDER ==================================================== */}
      <section style={{ padding: '40px 0' }}>
        <div className="container">
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 380px', gap: 28, alignItems: 'start' }}>

            {/* LEFT: slots ============================================== */}
            <div className="col gap-3">
              <div className="row between mb-2">
                <Eyebrow>02 · Bill of Materials</Eyebrow>
                <div className="row gap-2">
                  {/* Share build uses native Web Share API where available, falls back to clipboard. */}
                  <button className="btn btn-line btn-sm" onClick={() => {
                    const url = window.location.href;
                    if (navigator.share) navigator.share({ title: 'My livestreamers.org build', url });
                    else navigator.clipboard?.writeText(url);
                  }}><Icons.share size={12} />Share build</button>
                  {/* Export PDF = browser print-to-PDF for now. Real PDF export with custom layout lands in Phase 2. */}
                  <button className="btn btn-line btn-sm" onClick={() => window.print()}><Icons.download size={12} />Export PDF</button>
                </div>
              </div>

              {SLOTS.map((slot) => {
                const item = GEAR_CATALOG[slot.key].find(g => g.id === picks[slot.key]);
                return (
                  <SlotRow key={slot.key}
                    slot={slot}
                    selected={item}
                    options={GEAR_CATALOG[slot.key]}
                    onChange={(id) => swap(slot.key, id)} />
                );
              })}

              {/* Data plan sub-section */}
              <div className="card card-pad mt-5">
                <div className="row between mb-4">
                  <div>
                    <Eyebrow>03 · Data strategy</Eyebrow>
                    <div style={{ fontSize: 16, fontWeight: 600, marginTop: 6 }}>Carrier mix & uplink budget</div>
                  </div>
                  <Badge kind="updated">Quarterly verified</Badge>
                </div>

                <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 24 }}>
                  <SliderControl label="Carriers bonded" value={carriers} setValue={setCarriers} min={1} max={4} step={1} unit="" />
                  <SliderControl label="Stream hours / day" value={hours} setValue={setHours} min={1} max={12} step={1} unit="h" />
                  <SliderControl label="Bitrate" value={bitrate} setValue={setBitrate} min={2} max={12} step={0.5} unit="Mbps" />
                </div>

                <div className="mt-5" style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 16 }}>
                  <Tile label="Est. data / day" value={`${dataNeeded.toFixed(1)} GB`} sub={`${(dataNeeded * 30).toFixed(0)} GB / mo`} />
                  <Tile label="Data plan cost"  value={`$${dataMonthly}`} sub="per month, all SIMs" />
                  <Tile label="Recommended"     value={carriers >= 3 ? 'SIM injector' : carriers === 2 ? 'Dual-SIM router' : 'Hotspot + phone'} sub="See bonding guide" tone="accent" />
                </div>

                <div className="mt-4" style={{ padding: 14, borderTop: '1px dashed var(--line-2)', display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 12 }}>
                  {[
                    { n: 'Verizon',  plan: 'Business 5G Unl Plus', mbps: '15-50 ↑' },
                    { n: 'T-Mobile', plan: 'Business 5G Unl Adv',  mbps: '20-100 ↑' },
                    { n: 'AT&T',     plan: 'Business Unl Premium', mbps: '10-25 ↑' },
                    { n: 'Visible',  plan: 'Plus (deprio at 50G)', mbps: '5-15 ↑' },
                  ].slice(0, carriers).map((c, i) => (
                    <div key={i} className="card" style={{ padding: '10px 12px', background: 'var(--bg-2)' }}>
                      <div className="mono fs-12 text-fg4">{c.n.toUpperCase()}</div>
                      <div style={{ fontSize: 13, marginTop: 2 }}>{c.plan}</div>
                      <div className="mono fs-12 text-accent mt-2">{c.mbps}</div>
                    </div>
                  ))}
                  {carriers < 4 && Array.from({ length: 4 - carriers }).map((_, i) => (
                    <div key={`e${i}`} className="card" style={{ padding: '10px 12px', background: 'transparent', borderStyle: 'dashed', borderColor: 'var(--line)', minHeight: 70, display: 'grid', placeItems: 'center' }}>
                      <span className="mono fs-12 text-fg4">empty slot</span>
                    </div>
                  ))}
                </div>
              </div>
            </div>

            {/* RIGHT: live spec sheet ================================== */}
            <div style={{ position: 'sticky', top: 80 }}>
              <div className="card" style={{ overflow: 'hidden' }}>
                {/* mini-header */}
                <div style={{ padding: '14px 18px', background: 'var(--bg-2)', borderBottom: '1px solid var(--line)' }}>
                  <div className="row between">
                    <div className="mono fs-12 text-accent">SPEC SHEET · LIVE</div>
                    <div className="row gap-1"><span style={{ width: 6, height: 6, background: 'var(--accent)', borderRadius: '50%' }} /></div>
                  </div>
                  <h3 className="mt-2" style={{ fontSize: 20 }}>{PRESETS[presetId].name}</h3>
                  <div className="fs-13 text-fg3 mt-1">For: {PRESETS[presetId].audience} · {PRESETS[presetId].env}</div>
                </div>

                {/* total */}
                <div style={{ padding: '20px 18px', borderBottom: '1px solid var(--line)' }}>
                  <div className="row between">
                    <span className="stat-label">Build total</span>
                    <span className="mono fs-12 text-fg4">{items.filter(i => i.item?.price > 0).length} parts</span>
                  </div>
                  <div className="mt-1" style={{
                    fontFamily: 'var(--font-mono)',
                    fontSize: 42, fontWeight: 500, letterSpacing: '-0.02em',
                    color: 'var(--accent)',
                  }}>
                    ${total.toLocaleString()}
                  </div>
                  <div className="text-fg3 fs-12 mt-1">
                    Affiliate disclosed · prices reflect typical street, May ’26
                  </div>
                </div>

                {/* sub-stats */}
                <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', borderBottom: '1px solid var(--line)' }}>
                  <SpecTile label="Runtime" value={runtime ? `${runtime.toFixed(1)}h` : '—'} sub={`${totalDraw.toFixed(1)}W draw`} />
                  <SpecTile label="Pack weight" value={`${(totalWeight / 1000).toFixed(2)} kg`} sub={`${(totalWeight / 28.35).toFixed(0)} oz`} />
                  <SpecTile label="Avg gear score" value={<Score value={avgScore} />} sub="lab-tested" />
                  <SpecTile label="Setup time" value={`${(items.filter(i => i.item?.price > 0).length * 2.5).toFixed(0)} min`} sub="cold start" />
                </div>

                {/* runtime visual */}
                <div style={{ padding: '18px' }}>
                  <div className="row between mb-2">
                    <span className="stat-label">Power budget</span>
                    <span className="mono fs-12" style={{ color: runtime >= 4 ? 'var(--accent)' : runtime >= 2 ? 'var(--warn)' : 'var(--bad)' }}>
                      {runtime >= 4 ? 'HEALTHY' : runtime >= 2 ? 'TIGHT' : 'INSUFFICIENT'}
                    </span>
                  </div>
                  <Meter value={Math.min(runtime / 8 * 100, 100)} tone={runtime >= 4 ? '' : runtime >= 2 ? 'warn' : 'bad'} />
                  <div className="row between mt-2 mono fs-12 text-fg4">
                    <span>0h</span><span>4h target</span><span>8h+</span>
                  </div>
                </div>

                {/* call-out */}
                <div style={{ padding: '14px 18px', background: 'var(--bg-2)', borderTop: '1px solid var(--line)' }}>
                  <div className="row gap-2 mb-2"><Icons.zap size={14} style={{ color: 'var(--accent)' }} /><span className="mono fs-12 text-accent">PRO TIP</span></div>
                  <p className="text-fg2 fs-13" style={{ lineHeight: 1.4 }}>
                    {runtime < 2 ? 'Your draw outpaces your battery. Swap to the Omnicharge 20+ or add a second pack.' :
                     totalDraw > 15 ? 'High draw — keep a 100W USB-PD wall in reach during cuts.' :
                     'Solid balance. Pack an extra cable for the router; it’s the #1 dead-stream cause we log.'}
                  </p>
                </div>

                {/* CTA */}
                <div className="col gap-2" style={{ padding: 18, borderTop: '1px solid var(--line)' }}>
                  {/* Add-to-cart routes through the affiliate registry in Phase 5.
                      For now: open an early-access mailto with the build summary. */}
                  <Btn kind="primary" iconRight={<Icons.arrow size={14} />}
                       href={`mailto:hello@livestreamers.org?subject=${encodeURIComponent('Build: ' + PRESETS[presetId].name + ' ($' + total + ')')}`}>
                    Email me this build
                  </Btn>
                  <div className="row gap-2">
                    <Btn kind="line" size="sm" onClick={() => window.print()}><Icons.download size={12} />Print PDF</Btn>
                    <Btn kind="line" size="sm" onClick={() => {
                      const url = window.location.href;
                      if (navigator.share) navigator.share({ title: 'My livestreamers.org build', url });
                      else navigator.clipboard?.writeText(url);
                    }}><Icons.share size={12} />Share</Btn>
                  </div>
                </div>
              </div>

              {/* wiring diagram peek */}
              <div className="card card-pad mt-3 grid-bg">
                <div className="row between mb-3">
                  <Eyebrow>Wiring diagram</Eyebrow>
                  {/* Full interactive wiring diagram lands in Phase 3 — for now the SVG below is the preview. */}
                  <span className="mono fs-12 text-fg4">PREVIEW</span>
                </div>
                <svg viewBox="0 0 320 120" style={{ width: '100%', height: 120 }}>
                  <defs>
                    <marker id="bcarrow" markerWidth="6" markerHeight="6" refX="5" refY="3" orient="auto">
                      <path d="M0,0 L6,3 L0,6 z" fill="var(--fg-3)" />
                    </marker>
                  </defs>
                  {/* boxes */}
                  {[['CAM', 30, 20], ['MIC', 30, 80], ['BAT', 290, 20], ['BAT', 290, 80]].map(([l, x, y], i) => (
                    <g key={i}>
                      <rect x={x-22} y={y-12} width="44" height="24" rx="3" fill="var(--bg-2)" stroke="var(--line-2)" />
                      <text x={x} y={y+3} textAnchor="middle" fontFamily="var(--font-mono)" fontSize="9" fill="var(--fg-3)">{l}</text>
                    </g>
                  ))}
                  <rect x={130} y="36" width="60" height="48" rx="4" fill="var(--bg-3)" stroke="var(--accent)" />
                  <text x={160} y={56} textAnchor="middle" fontFamily="var(--font-mono)" fontSize="9" fill="var(--accent)">ROUTER</text>
                  <text x={160} y={70} textAnchor="middle" fontFamily="var(--font-mono)" fontSize="8" fill="var(--fg-3)">bonded</text>
                  {/* lines */}
                  <line x1={52} y1={20} x2={130} y2={50} stroke="var(--fg-3)" strokeWidth="0.8" markerEnd="url(#bcarrow)" />
                  <line x1={52} y1={80} x2={130} y2={70} stroke="var(--fg-3)" strokeWidth="0.8" markerEnd="url(#bcarrow)" />
                  <line x1={190} y1={50} x2={268} y2={20} stroke="var(--fg-3)" strokeWidth="0.8" strokeDasharray="2 2" />
                  <line x1={190} y1={70} x2={268} y2={80} stroke="var(--fg-3)" strokeWidth="0.8" strokeDasharray="2 2" />
                </svg>
              </div>
            </div>
          </div>
        </div>
      </section>

      {/* TEST LOG ========================================================= */}
      <section style={{ padding: '64px 0', background: 'var(--bg-1)', borderTop: '1px solid var(--line)' }}>
        <div className="container">
          <div className="section-head">
            <div>
              <Eyebrow>Field-tested · Most recent runs for this build</Eyebrow>
              <h2 className="mt-3">From the lab log.</h2>
            </div>
            {/* "All test logs" lands when MDX runs ship in Phase 4. */}
          </div>
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 14 }}>
            {[
              { loc: 'Downtown LA', date: 'May 02 ’26', drops: 0, bitrate: 6.2, dur: '4h 18m', notes: 'Heavy crowd, dropped to T-Mobile once.' },
              { loc: 'Shibuya · Tokyo', date: 'Apr 24 ’26', drops: 2, bitrate: 5.4, dur: '3h 02m', notes: 'Convention floor, AT&T roaming spotty.' },
              { loc: 'Suburban drive · TX', date: 'Apr 18 ’26', drops: 0, bitrate: 7.0, dur: '6h 41m', notes: 'Highway test, 4 carriers, all healthy.' },
            ].map((t, i) => (
              <div key={i} className="card card-pad">
                <div className="row between mb-3">
                  <span className="mono fs-12 text-fg4">{t.date.toUpperCase()}</span>
                  <Badge kind={t.drops === 0 ? 'tested' : 'beginner'}>{t.drops === 0 ? '0 drops' : `${t.drops} drops`}</Badge>
                </div>
                <h4 style={{ fontSize: 17 }}>{t.loc}</h4>
                <div className="row gap-4 mt-3 mono fs-12 text-fg3">
                  <span>BITRATE <span style={{ color: 'var(--accent)' }}>{t.bitrate} Mbps</span></span>
                  <span>DUR <span style={{ color: 'var(--fg)' }}>{t.dur}</span></span>
                </div>
                <p className="text-fg3 fs-13 mt-3">{t.notes}</p>
              </div>
            ))}
          </div>
        </div>
      </section>
    </div>
  );
};

const SlotRow = ({ slot, selected, options, onChange }) => {
  const [open, setOpen] = useState(false);
  return (
    <div className="card" style={{ overflow: 'visible' }}>
      <div className="row between" style={{ padding: '18px 20px' }}>
        <div className="row gap-4">
          <div style={{
            width: 40, height: 40, borderRadius: 6,
            display: 'grid', placeItems: 'center',
            background: 'var(--bg-3)',
            color: 'var(--accent)',
            border: '1px solid var(--line-2)',
          }}>{slot.icon}</div>
          <div>
            <div className="mono fs-12 text-fg4" style={{ letterSpacing: '0.1em', textTransform: 'uppercase' }}>{slot.label}</div>
            <div style={{ fontSize: 15, fontWeight: 500, marginTop: 2 }}>{selected?.name || 'Empty'}</div>
            {selected?.tag && <span className="badge mt-2" style={{ marginTop: 4 }}>{selected.tag}</span>}
          </div>
        </div>
        <div className="row gap-4">
          <div style={{ textAlign: 'right' }}>
            <div className="mono fs-12 text-fg4">PRICE</div>
            <div className="mono" style={{ fontSize: 18, marginTop: 2, color: 'var(--fg)' }}>${selected?.price || 0}</div>
          </div>
          <div style={{ textAlign: 'right', minWidth: 70 }}>
            <div className="mono fs-12 text-fg4">SCORE</div>
            <div className="mt-1"><Score value={selected?.score || 0} /></div>
          </div>
          <button className="btn btn-line btn-sm" onClick={() => setOpen(!open)}>
            {open ? 'Close' : 'Swap'}
            {open ? <Icons.x size={12} /> : <Icons.arrowDown size={12} />}
          </button>
        </div>
      </div>
      {open && (
        <div style={{ borderTop: '1px solid var(--line)', padding: '14px 20px 18px', background: 'var(--bg-2)' }}>
          <div className="mono fs-12 text-fg4 mb-3" style={{ letterSpacing: '0.1em', textTransform: 'uppercase' }}>Alternatives · {options.length}</div>
          <div className="col gap-2">
            {options.map(o => (
              <button key={o.id} onClick={() => { onChange(o.id); setOpen(false); }}
                className="row between"
                style={{
                  padding: '10px 12px',
                  background: o.id === selected?.id ? 'var(--bg-3)' : 'var(--bg-1)',
                  border: '1px solid ' + (o.id === selected?.id ? 'var(--accent)' : 'var(--line-2)'),
                  borderRadius: 6,
                  textAlign: 'left',
                  cursor: 'pointer',
                  width: '100%',
                }}>
                <div className="row gap-3">
                  {o.id === selected?.id ? <Icons.check size={14} style={{ color: 'var(--accent)' }} /> : <span style={{ width: 14, height: 14, borderRadius: 50, border: '1px solid var(--line-3)' }} />}
                  <div style={{ fontSize: 14 }}>{o.name}</div>
                  {o.tag && <span className="badge">{o.tag}</span>}
                </div>
                <div className="row gap-4">
                  <span className="mono fs-12 text-fg3">${o.price}</span>
                  <Score value={o.score} />
                </div>
              </button>
            ))}
          </div>
        </div>
      )}
    </div>
  );
};

const SliderControl = ({ label, value, setValue, min, max, step, unit }) => (
  <div>
    <div className="row between mb-2">
      <span className="mono fs-12 text-fg3" style={{ letterSpacing: '0.1em', textTransform: 'uppercase' }}>{label}</span>
      <span className="mono fs-13" style={{ color: 'var(--accent)' }}>{value}{unit && ` ${unit}`}</span>
    </div>
    <input type="range" className="slider"
           min={min} max={max} step={step} value={value}
           onChange={e => setValue(parseFloat(e.target.value))} />
  </div>
);

const Tile = ({ label, value, sub, tone }) => (
  <div className="card" style={{ padding: '14px 16px' }}>
    <div className="mono fs-12 text-fg4" style={{ letterSpacing: '0.1em', textTransform: 'uppercase' }}>{label}</div>
    <div style={{ fontFamily: 'var(--font-mono)', fontSize: 22, marginTop: 6, color: tone === 'accent' ? 'var(--accent)' : 'var(--fg)' }}>{value}</div>
    {sub && <div className="text-fg3 fs-12 mt-1">{sub}</div>}
  </div>
);

const SpecTile = ({ label, value, sub }) => (
  <div style={{ padding: '14px 18px', borderRight: '1px solid var(--line)', borderTop: '1px solid var(--line)' }}>
    <div className="stat-label">{label}</div>
    <div style={{ fontFamily: 'var(--font-mono)', fontSize: 18, marginTop: 4 }}>{value}</div>
    <div className="text-fg4 fs-12 mt-1 mono">{sub}</div>
  </div>
);

function hash(s) {
  let h = 0;
  for (let i = 0; i < s.length; i++) h = ((h << 5) - h) + s.charCodeAt(i) | 0;
  return h;
}

window.BuildPage = BuildPage;
