/* global React, ReactDOM */
const { useState, useRef, useEffect, useMemo, useCallback } = React;

// ====== CZŁONKOWIE RADY ======
const MEMBERS = [
  { id: 'karolina',  label: 'Karolina',   img: 'img/karolina.png' },
  { id: 'karol_g',   label: 'Karol G.',   img: 'img/karol_g.png' },
  { id: 'karol_z',   label: 'Karol Ż.',   img: 'img/karol_z.png' },
  { id: 'sebastian', label: 'Sebastian',  img: 'img/sebastian.png' },
  { id: 'damian_s',  label: 'Damian S.',  img: 'img/damian_s.png' },
  { id: 'damian_w',  label: 'Damian W.',  img: 'img/damian_w.png' },
  { id: 'tomasz',    label: 'Tomasz',     img: 'img/tomasz.png' },
  { id: 'daniel',    label: 'Daniel',     img: 'img/daniel.png' },
  { id: 'agnieszka', label: 'Agnieszka',  img: 'img/agnieszka.png' },
  { id: 'dawid',     label: 'Dawid',      img: 'img/dawid.png' },
  { id: 'sandra',    label: 'Sandra',     img: 'img/sandra.png' },
];

const SPEED_PRESETS = {
  goniec: { label: 'Pośpieszny goniec', sub: 'krótko, raz dwa',     duration: 3.2, turns: 6 },
  rycerz: { label: 'Spokojny rycerz',   sub: 'jak Bóg przykazał',   duration: 6.5, turns: 9 },
  mnich:  { label: 'Cierpliwy mnich',   sub: 'i pomódl się przy tym', duration: 11,  turns: 13 },
};

// ====== MOTYWY SEZONOWE ======
const THEME_ORDER = ['spring', 'summer', 'autumn', 'winter', 'legacy'];
const THEMES = {
  spring: { label: 'Wiosenny jarmark', glyph: '✿', effect: 'petals', wheel: { grad: ['#8b7748','#6b5a35','#48401e'], grain1: '#3c3a18', grain2: '#4c4a24', line: '#12160a', lineHi: '#9aa668', deep: '#12160a', frame: '#8ca64b', frameHi: '#cfe094', text: '#f2f6da', stroke: '#12160a', boss: ['#7e8c4e','#3b4422','#161a0c'], bossRing: '#a9bb6d', bossText: '#dbe8ac', bossSub: '#a9bb6d', rivet: '#a9bb6d', rivetBg: '#262c12', deco: 'flower', decoRing: '#cfe094', deco2: 'sprig', dress: 'garland' } },
  summer: { label: 'Letnie żniwa', glyph: '☀︎', effect: 'fireflies', wheel: { grad: ['#a2743a','#7d5626','#523712'], grain1: '#523712', grain2: '#64451c', line: '#1b1105', lineHi: '#b98f4c', deep: '#1b1105', frame: '#cb9e3f', frameHi: '#f2d98f', text: '#fceec5', stroke: '#1b1105', boss: ['#a08040','#4c3a16','#1d1506'], bossRing: '#d3ab55', bossText: '#f2d98f', bossSub: '#d3ab55', rivet: '#d3ab55', rivetBg: '#33230c', deco: 'wheat', decoRing: '#f2d98f', deco2: 'spark' } },
  autumn: { label: 'Jesienna karczma', glyph: '❧', effect: 'leaves', wheel: { grad: ['#83491f','#602f12','#3d1d09'], grain1: '#3d1d09', grain2: '#4e2810', line: '#170a03', lineHi: '#a5622c', deep: '#170a03', frame: '#b26c2d', frameHi: '#e6ae72', text: '#f5ddb2', stroke: '#170a03', boss: ['#8f5c2c','#402312','#180c04'], bossRing: '#c08448', bossText: '#e6ae72', bossSub: '#c08448', rivet: '#c08448', rivetBg: '#2c1608', deco: 'leaf', decoRing: '#e6ae72', deco2: 'acorn', dress: 'tavern' } },
  winter: { label: 'Zimowa twierdza', glyph: '❄︎', effect: 'snow', wheel: { grad: ['#5d7389','#3d5064','#26313f'], grain1: '#26313f', grain2: '#31404f', line: '#0b1118', lineHi: '#7e94aa', deep: '#0b1118', frame: '#8fa6bb', frameHi: '#d3e2f2', text: '#eaf2fa', stroke: '#0b1118', boss: ['#72869a','#303e4e','#111a24'], bossRing: '#9db3c8', bossText: '#d3e2f2', bossSub: '#9db3c8', rivet: '#9db3c8', rivetBg: '#1e2833', deco: 'snow', decoRing: '#d6e9fb', deco2: 'icicle', frost: true, dress: 'snow' } },
  legacy: { label: 'Stara izba', glyph: '✦', effect: null, wheel: { grad: ['#7a5230','#5a3a1f','#3a2412'], grain1: '#3a2412', grain2: '#4a2e18', line: '#1a0e04', lineHi: '#9c7544', deep: '#1a0e04', frame: '#8b6f3a', frameHi: '#d9b870', text: '#f1dfb1', stroke: '#1a0e04', boss: ['#7d6a44','#3d3320','#1a140a'], bossRing: '#a08355', bossText: '#e8cf8f', bossSub: '#a08355', rivet: '#a08355', rivetBg: '#2a1f10' } },
};
const seasonForMonth = (m) => (m >= 2 && m <= 4) ? 'spring' : (m >= 5 && m <= 7) ? 'summer' : (m >= 8 && m <= 10) ? 'autumn' : 'winter';

// ====== SEZONOWE ZDOBIENIA KOŁA ======
function DecoMotif({ kind, idx }) {
  if (kind === 'snow') return (
    <g stroke="#d6e9fb" strokeWidth="1.4" strokeLinecap="round" opacity="0.85" fill="none">
      <line x1="0" y1="-8" x2="0" y2="8" />
      <line x1="-6.9" y1="-4" x2="6.9" y2="4" />
      <line x1="-6.9" y1="4" x2="6.9" y2="-4" />
      <line x1="-2.6" y1="-5.2" x2="0" y2="-7.8" /><line x1="2.6" y1="-5.2" x2="0" y2="-7.8" />
      <line x1="-2.6" y1="5.2" x2="0" y2="7.8" /><line x1="2.6" y1="5.2" x2="0" y2="7.8" />
      <circle cx="0" cy="0" r="1.5" fill="#eef7ff" stroke="none" />
    </g>
  );
  if (kind === 'flower') {
    const petal = idx % 2 === 0 ? '#f2c4d2' : '#f6e3ea';
    return (
      <g>
        {[0, 72, 144, 216, 288].map((a) => (
          <ellipse key={a} cx="0" cy="-4.6" rx="2.6" ry="4" transform={`rotate(${a})`} fill={petal} stroke="#b0486a" strokeWidth="0.5" opacity="0.95" />
        ))}
        <circle cx="0" cy="0" r="2.2" fill="#e8c84f" stroke="#8a6218" strokeWidth="0.5" />
      </g>
    );
  }
  if (kind === 'wheat') return (
    <g>
      <line x1="0" y1="9" x2="0" y2="-9" stroke="#c89c3e" strokeWidth="1.2" />
      {[0, 1, 2].map((k) => (
        <g key={k}>
          <ellipse cx="-2.6" cy={-6.5 + k * 4} rx="1.9" ry="3.1" transform={`rotate(-24 -2.6 ${-6.5 + k * 4})`} fill="#eed48c" stroke="#8a6218" strokeWidth="0.4" />
          <ellipse cx="2.6" cy={-6.5 + k * 4} rx="1.9" ry="3.1" transform={`rotate(24 2.6 ${-6.5 + k * 4})`} fill="#eed48c" stroke="#8a6218" strokeWidth="0.4" />
        </g>
      ))}
    </g>
  );
  if (kind === 'leaf') {
    const c = ['#c9701e', '#a8481a', '#b8860e'][idx % 3];
    return (
      <g fill={c} stroke="#5c2410" strokeWidth="0.6">
        <path d="M 0 -8 Q 6.5 -1.5 0 8.5 Q -6.5 -1.5 0 -8 Z" />
        <line x1="0" y1="-7" x2="0" y2="7.5" strokeWidth="0.8" fill="none" />
      </g>
    );
  }
  return null;
}

function DecoMotif2({ kind, idx }) {
  if (kind === 'icicle') return (
    <g fill="#cfe4f6" opacity="0.85" stroke="#9fc3e2" strokeWidth="0.4">
      <rect x="-8" y="-2.5" width="16" height="3" rx="1.5" fill="#dcebf8" stroke="none" opacity="0.9" />
      <path d="M -6 0 L -4.4 0 L -5.2 7 Z" />
      <path d="M -1.2 0 L 0.8 0 L -0.2 11.5 Z" />
      <path d="M 4 0 L 5.6 0 L 4.8 5.5 Z" />
    </g>
  );
  if (kind === 'sprig') return (
    <g fill="#7fa04a" stroke="#42551f" strokeWidth="0.5">
      <path d="M 0 1 Q -7 -2 -9 -8 Q -2 -7 0 1 Z" />
      <path d="M 0 1 Q 7 -2 9 -8 Q 2 -7 0 1 Z" />
      <line x1="0" y1="1" x2="0" y2="5" stroke="#42551f" strokeWidth="0.8" fill="none" />
    </g>
  );
  if (kind === 'spark') return (
    <g stroke="#f2d98f" strokeWidth="1.1" strokeLinecap="round">
      {[0, 45, 90, 135, 180, 225, 270, 315].map((a) => {
        const r1 = 4.2, r2 = 7.2, rad = a * Math.PI / 180;
        return <line key={a} x1={r1 * Math.cos(rad)} y1={r1 * Math.sin(rad)} x2={r2 * Math.cos(rad)} y2={r2 * Math.sin(rad)} />;
      })}
      <circle cx="0" cy="0" r="2.6" fill="#eed48c" stroke="#8a6218" strokeWidth="0.5" />
    </g>
  );
  if (kind === 'acorn') return (
    <g>
      <ellipse cx="0" cy="2" rx="3" ry="4" fill="#9a6634" stroke="#5c2410" strokeWidth="0.5" />
      <path d="M -3.6 -0.5 Q 0 -4.5 3.6 -0.5 Q 0 1.5 -3.6 -0.5 Z" fill="#6e4020" stroke="#3c1e0c" strokeWidth="0.5" />
      <line x1="0" y1="-3.4" x2="0" y2="-5.4" stroke="#3c1e0c" strokeWidth="0.8" />
    </g>
  );
  return null;
}

// ====== DEKORACJE STATYCZNE (nie kręcą się z kołem) ======
function RingDressing({ kind, size }) {
  if (!kind) return null;
  const S = size + 60, c = S / 2;
  const Rout = size / 2 + 14, Rin = Rout - 26;
  const P = (r, aDeg) => [c + r * Math.cos(aDeg * Math.PI / 180), c + r * Math.sin(aDeg * Math.PI / 180)];
  const rndS = (i) => { const x = Math.sin(i * 127.1 + 311.7) * 43758.5453; return x - Math.floor(x); };
  let content = null;
  if (kind === 'snow') {
    let d = '';
    for (let a = -152; a <= -28; a += 4) {
      const [x, y] = P(Rout + 4 + 7 * rndS(a), a);
      d += (d ? ' L ' : 'M ') + x.toFixed(1) + ' ' + y.toFixed(1);
    }
    for (let a = -28; a >= -152; a -= 8) {
      const [x, y] = P(Rin + 4 * rndS(a + 999), a);
      d += ' L ' + x.toFixed(1) + ' ' + y.toFixed(1);
    }
    d += ' Z';
    const ics = [-138, -117, -99, -84, -66, -47].map((a, i) => {
      const [x, y] = P(Rin + 2, a);
      const len = 12 + 18 * rndS(i * 7 + 3);
      return <path key={i} d={`M ${x - 3} ${y} L ${x + 3} ${y} L ${x} ${y + len} Z`} fill="#d8eafa" stroke="#aecbe4" strokeWidth="0.5" opacity="0.92" />;
    });
    content = <g><path d={d} fill="#eef6fd" stroke="#c9ddef" strokeWidth="1" opacity="0.95" />{ics}</g>;
  } else if (kind === 'garland') {
    const items = [];
    let d = '';
    for (let a = 26; a <= 154; a += 4) {
      const [x, y] = P(Rout - 6 + 5 * Math.sin(a * 0.5), a);
      d += (d ? ' L ' : 'M ') + x.toFixed(1) + ' ' + y.toFixed(1);
    }
    items.push(<path key="vine" d={d} fill="none" stroke="#5d7a33" strokeWidth="3" strokeLinecap="round" opacity="0.9" />);
    for (let a = 30, i = 0; a <= 150; a += 11, i++) {
      const [x, y] = P(Rout - 4 + 6 * rndS(i * 13), a);
      if (i % 2 === 0) {
        const petal = i % 4 === 0 ? '#f2c4d2' : '#f8ecef';
        items.push(<g key={'f' + i} transform={`translate(${x} ${y}) rotate(${a})`}>
          {[0, 72, 144, 216, 288].map((p) => <ellipse key={p} cx="0" cy="-5" rx="2.8" ry="4.4" transform={`rotate(${p})`} fill={petal} stroke="#b0486a" strokeWidth="0.5" />)}
          <circle r="2.4" fill="#e8c84f" stroke="#8a6218" strokeWidth="0.5" />
        </g>);
      } else {
        items.push(<g key={'l' + i} transform={`translate(${x} ${y}) rotate(${a + 90})`} fill="#7fa04a" stroke="#42551f" strokeWidth="0.5">
          <path d="M 0 0 Q -7 -3 -9 -9 Q -2 -8 0 0 Z" />
          <path d="M 0 0 Q 7 -3 9 -9 Q 2 -8 0 0 Z" />
        </g>);
      }
    }
    content = <g>{items}</g>;
  } else if (kind === 'tavern') {
    const bundle = (a, key) => {
      const [x, y] = P(Rout + 2, a);
      const dy = 34;
      return (
        <g key={key}>
          <line x1={x} y1={y} x2={x} y2={y + dy} stroke="#6b4423" strokeWidth="1.5" />
          {[-38, -19, 0, 19, 38].map((rot, i) => (
            <g key={i} transform={`translate(${x} ${y + dy}) rotate(${rot + 180})`}>
              <path d="M 0 -4 Q 6.5 2.5 0 13 Q -6.5 2.5 0 -4 Z" fill={['#c9701e', '#a8481a', '#b8860e', '#8f5c2c', '#c9701e'][i]} stroke="#5c2410" strokeWidth="0.6" />
            </g>
          ))}
          <circle cx={x} cy={y + dy} r="3" fill="#6e4020" stroke="#3c1e0c" strokeWidth="0.6" />
        </g>
      );
    };
    const pile = [];
    for (let i = 0; i < 12; i++) {
      const a = 55 + i * 6 + 3 * rndS(i);
      const [x, y] = P(Rout + 2 + 5 * rndS(i + 40), a);
      pile.push(<g key={'p' + i} transform={`translate(${x} ${y}) rotate(${rndS(i + 7) * 360})`} opacity="0.9">
        <path d="M 0 -6 Q 5 -1 0 6.5 Q -5 -1 0 -6 Z" fill={['#c9701e', '#a8481a', '#b8860e'][i % 3]} stroke="#5c2410" strokeWidth="0.5" />
      </g>);
    }
    content = <g>{bundle(-142, 'b1')}{bundle(-38, 'b2')}{pile}</g>;
  }
  return <svg className="ring-dressing" width={S} height={S} viewBox={`0 0 ${S} ${S}`}>{content}</svg>;
}

function Bunting() {
  const colors = ['#a84a6e', '#8ca64b', '#cb9e3f', '#7a8ea2'];
  const swag = (x0, x1, sag, n, off, key) => {
    const pts = [];
    for (let i = 0; i <= n; i++) {
      const t = i / n;
      pts.push([x0 + (x1 - x0) * t, 8 + sag * Math.sin(Math.PI * t)]);
    }
    return (
      <g key={key}>
        <path d={'M ' + pts.map((p) => p[0].toFixed(1) + ' ' + p[1].toFixed(1)).join(' L ')} fill="none" stroke="#6b4423" strokeWidth="2" />
        {pts.slice(0, -1).map(([x, y], i) => {
          const [x2, y2] = pts[i + 1];
          const mx = (x + x2) / 2, my = (y + y2) / 2;
          return <polygon key={i} points={`${x},${y} ${x2},${y2} ${mx},${my + 24}`} fill={colors[(i + off) % colors.length]} stroke="#3a2412" strokeWidth="0.6" opacity="0.92" />;
        })}
      </g>
    );
  };
  return (
    <svg className="bunting" viewBox="0 0 1600 80" preserveAspectRatio="none">
      {swag(-20, 470, 30, 7, 0, 's1')}
      {swag(1130, 1620, 30, 7, 2, 's2')}
    </svg>
  );
}

function FloorLeaves() {
  const rndS = (i) => { const x = Math.sin(i * 91.7 + 47.3) * 24634.62; return x - Math.floor(x); };
  return (
    <svg className="floor-leaves" viewBox="0 0 1600 80" preserveAspectRatio="none">
      {Array.from({ length: 22 }).map((_, i) => {
        const x = 30 + 1540 * rndS(i);
        const y = 30 + 44 * rndS(i + 50);
        const s = 0.8 + 0.9 * rndS(i + 90);
        return (
          <g key={i} transform={`translate(${x} ${y}) rotate(${rndS(i + 20) * 360}) scale(${s})`} opacity={0.55 + 0.35 * rndS(i + 70)}>
            <path d="M 0 -7 Q 5.5 -1 0 7.5 Q -5.5 -1 0 -7 Z" fill={['#b8641c', '#96451c', '#a07714'][i % 3]} stroke="#4c2410" strokeWidth="0.5" />
          </g>
        );
      })}
    </svg>
  );
}

// ====== SCENY TŁA ======
function SceneBackdrop({ theme }) {
  if (theme === 'legacy') return null;
  const rndS = (i) => { const x = Math.sin(i * 73.9 + 17.3) * 10467.53; return x - Math.floor(x); };
  let scene = null;
  if (theme === 'summer') scene = (
    <g>
      <defs>
        <linearGradient id="skySum" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stopColor="#5aa6d4" /><stop offset="70%" stopColor="#b6e0f2" /><stop offset="100%" stopColor="#d8eef8" /></linearGradient>
        <linearGradient id="seaSum" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stopColor="#2e7ba6" /><stop offset="100%" stopColor="#4a9cc4" /></linearGradient>
        <radialGradient id="rayFade" gradientUnits="userSpaceOnUse" cx="1270" cy="150" r="1500"><stop offset="0%" stopColor="#fff" /><stop offset="100%" stopColor="#fff" stopOpacity="0" /></radialGradient>
        <mask id="rayMask"><rect width="1600" height="900" fill="url(#rayFade)" /></mask>
      </defs>
      <rect width="1600" height="900" fill="url(#skySum)" />
      <g mask="url(#rayMask)" style={{ mixBlendMode: 'screen' }}>
        {Array.from({ length: 40 }).map((_, i) => {
          const a1 = ((112 + i * 9) * Math.PI) / 180, a2 = ((112 + i * 9 + 2.4) * Math.PI) / 180, R = 1700;
          return <path key={'ray' + i} d={`M 1270 150 L ${1270 + R * Math.cos(a1)} ${150 + R * Math.sin(a1)} L ${1270 + R * Math.cos(a2)} ${150 + R * Math.sin(a2)} Z`} fill="#ffd778" opacity="0.07" />;
        })}
      </g>
      <circle cx="1270" cy="150" r="105" fill="#ffe9a3" opacity="0.35" />
      <circle cx="1270" cy="150" r="58" fill="#ffedad" />
      <path d="M 320 190 q 14 -12 28 0 q 14 -12 28 0" fill="none" stroke="#3a5a70" strokeWidth="3" strokeLinecap="round" opacity="0.7" />
      <path d="M 430 150 q 11 -9 22 0 q 11 -9 22 0" fill="none" stroke="#3a5a70" strokeWidth="2.5" strokeLinecap="round" opacity="0.6" />
      <rect y="590" width="1600" height="130" fill="url(#seaSum)" />
      {Array.from({ length: 9 }).map((_, i) => <path key={i} d={`M ${60 + i * 180} ${615 + (i % 3) * 28} q 22 -7 44 0`} fill="none" stroke="#cfe8f2" strokeWidth="2.5" opacity="0.6" strokeLinecap="round" />)}
      <path d="M 0 690 Q 400 640 800 675 T 1600 660 L 1600 900 L 0 900 Z" fill="#e6cf96" />
      <path d="M 0 720 Q 500 680 1000 710 T 1600 700 L 1600 900 L 0 900 Z" fill="#d8bc7e" opacity="0.7" />
    </g>
  );
  else if (theme === 'winter') scene = (
    <g>
      <defs>
        <linearGradient id="skyWin" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stopColor="#070d18" /><stop offset="70%" stopColor="#16283e" /><stop offset="100%" stopColor="#24384f" /></linearGradient>
      </defs>
      <rect width="1600" height="900" fill="url(#skyWin)" />
      {Array.from({ length: 60 }).map((_, i) => <circle key={i} cx={rndS(i) * 1600} cy={rndS(i + 100) * 520} r={0.8 + rndS(i + 200) * 1.4} fill="#dce8f8" opacity={0.3 + rndS(i + 300) * 0.7} />)}
      <circle cx="1240" cy="150" r="86" fill="#e8f0f8" opacity="0.18" />
      <circle cx="1240" cy="150" r="46" fill="#e9f1f9" />
      <circle cx="1222" cy="136" r="9" fill="#c8d8e8" opacity="0.5" /><circle cx="1252" cy="162" r="6" fill="#c8d8e8" opacity="0.4" />
      {[80, 190, 320, 455, 1165, 1290, 1420, 1530].map((x, i) => {
        const h = 110 + rndS(i + 40) * 80, base = 765 + rndS(i + 61) * 25;
        return (
          <g key={i}>
            <polygon points={`${x},${base - h} ${x - 42},${base} ${x + 42},${base}`} fill="#14222e" />
            <polygon points={`${x},${base - h - 46} ${x - 30},${base - h * 0.45} ${x + 30},${base - h * 0.45}`} fill="#182a38" />
            <polygon points={`${x},${base - h - 44} ${x - 16},${base - h - 8} ${x + 16},${base - h - 8}`} fill="#e8f0fa" opacity="0.85" />
          </g>
        );
      })}
      <path d="M 0 740 Q 400 660 900 730 T 1600 700 L 1600 900 L 0 900 Z" fill="#dfe9f4" />
      <path d="M 0 790 Q 500 730 1100 790 T 1600 770 L 1600 900 L 0 900 Z" fill="#c4d5e6" />
    </g>
  );
  else if (theme === 'spring') scene = (
    <g>
      <defs>
        <linearGradient id="skySpr" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stopColor="#8ec8e8" /><stop offset="100%" stopColor="#d4ecf8" /></linearGradient>
      </defs>
      <rect width="1600" height="900" fill="url(#skySpr)" />
      {[[240, 150], [700, 100], [1240, 170]].map(([x, y], i) => (
        <g key={i} fill="#ffffff" opacity="0.85">
          <ellipse cx={x} cy={y} rx="70" ry="24" /><ellipse cx={x + 46} cy={y + 8} rx="52" ry="18" /><ellipse cx={x - 50} cy={y + 10} rx="46" ry="16" />
        </g>
      ))}
      <path d="M 0 660 Q 400 600 800 650 T 1600 620 L 1600 900 L 0 900 Z" fill="#83ac52" />
      <path d="M 0 730 Q 500 680 1000 730 T 1600 710 L 1600 900 L 0 900 Z" fill="#6a9440" />
      {[[1375, 586, 1], [1190, 617, 0.75], [160, 575, 0.95], [330, 590, 0.65], [1520, 598, 0.8]].map(([tx, ty, ts], i) => (
        <g key={'tr' + i} transform={`translate(${tx} ${ty}) scale(${ts})`}>
          <rect x="-7" y="0" width="14" height="84" rx="5" fill="#6b4423" />
          <circle cx="0" cy="-32" r="52" fill={i % 2 === 0 ? '#eebcd0' : '#f0c8d8'} />
          <circle cx="-45" cy="-8" r="34" fill="#f2cddc" />
          <circle cx="45" cy="-10" r="36" fill="#e8aec6" />
        </g>
      ))}
      {Array.from({ length: 34 }).map((_, i) => {
        const x = 20 + rndS(i + 11) * 1560, y = 690 + rndS(i + 55) * 180;
        const ccc = ['#f2c4d2', '#fff2f6', '#e8c84f', '#d86a8a'][i % 4];
        return <g key={'fl' + i}><circle cx={x} cy={y} r={3 + rndS(i + 77) * 2.5} fill={ccc} /><circle cx={x} cy={y} r="1.2" fill="#8a6218" /></g>;
      })}
    </g>
  );
  else if (theme === 'autumn') {
    const tree = (x, base, s, key) => (
      <g key={key} transform={`translate(${x} ${base}) scale(${s})`}>
        <path d="M -8 0 L -5 -70 Q 0 -80 5 -70 L 8 0 Z" fill="#3c2210" />
        <path d="M -4 -40 L -26 -58" stroke="#3c2210" strokeWidth="5" />
        <path d="M 4 -44 L 28 -64" stroke="#3c2210" strokeWidth="5" />
        <circle cx="0" cy="-92" r="34" fill="#b8641c" />
        <circle cx="-30" cy="-72" r="26" fill="#96451c" />
        <circle cx="30" cy="-76" r="27" fill="#a07714" />
        <circle cx="0" cy="-64" r="24" fill="#8f3d18" />
      </g>
    );
    scene = (
      <g>
        <defs>
          <linearGradient id="skyAut" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stopColor="#d78f42" /><stop offset="55%" stopColor="#a85423" /><stop offset="100%" stopColor="#6e3014" /></linearGradient>
        </defs>
        <rect width="1600" height="900" fill="url(#skyAut)" />
        <circle cx="800" cy="565" r="90" fill="#f5c96a" opacity="0.5" />
        <circle cx="800" cy="565" r="55" fill="#f8d98a" opacity="0.8" />
        <path d="M 0 640 Q 400 590 800 630 T 1600 600 L 1600 900 L 0 900 Z" fill="#54260f" />
        <path d="M 0 720 Q 500 670 1000 720 T 1600 690 L 1600 900 L 0 900 Z" fill="#3c1a0a" />
        {tree(140, 700, 1.5, 't1')}{tree(280, 680, 0.9, 't2')}{tree(1460, 700, 1.7, 't3')}{tree(1330, 675, 1.0, 't4')}
      </g>
    );
  }
  return <svg className="scene" viewBox="0 0 1600 900" preserveAspectRatio="xMidYMax slice">{scene}</svg>;
}

// ====== KOŁO ======
function Wheel({ rotation, members, size = 720, wt, spinning = false, onPhotoClick }) {
  const N = members.length;
  const seg = 360 / N;
  const VB = 720;
  const cx = VB / 2;
  const cy = VB / 2;
  const ringR = VB / 2 - 24;
  const labelR = ringR - 70;
  const photoR = ringR - 175;
  const innerR = 70;

  // Zbuduj segmenty: alternujące odcienie drewna
  const woodA = '#5a3a1f';
  const woodB = '#6e4828';

  const arcPath = (i) => {
    const a0 = (i * seg - 90) * Math.PI / 180;
    const a1 = ((i + 1) * seg - 90) * Math.PI / 180;
    const x0 = cx + ringR * Math.cos(a0);
    const y0 = cy + ringR * Math.sin(a0);
    const x1 = cx + ringR * Math.cos(a1);
    const y1 = cy + ringR * Math.sin(a1);
    return `M ${cx} ${cy} L ${x0} ${y0} A ${ringR} ${ringR} 0 0 1 ${x1} ${y1} Z`;
  };

  return (
    <div className="wheel-wrap" style={{ width: size, height: size }}>
      {/* zewnętrzna obręcz - kute żelazo */}
      <div className="wheel-ironring" style={{ width: size, height: size }} />

      {/* obrotowe koło — w okrągłym klipie, by obracany kwadrat nie poszerzał strony */}
      <div className="wheel-clip">
      <svg
        className="wheel-svg"
        width={size}
        height={size}
        viewBox={`0 0 ${VB} ${VB}`}
        style={{ transform: `rotate(${rotation}deg)` }}
      >
        <defs>
          <radialGradient id="woodGrad" cx="50%" cy="50%" r="60%">
            <stop offset="0%"  stopColor={wt.grad[0]} />
            <stop offset="60%" stopColor={wt.grad[1]} />
            <stop offset="100%" stopColor={wt.grad[2]} />
          </radialGradient>
          <pattern id="woodGrain" patternUnits="userSpaceOnUse" width="160" height="160" patternTransform="rotate(0)">
            <rect width="160" height="160" fill="url(#woodGrad)" />
            {Array.from({ length: 30 }).map((_, i) => (
              <path key={i}
                d={`M 0 ${i * 6 + Math.sin(i) * 3} Q 80 ${i * 6 + 3 + Math.cos(i*1.7)*4} 160 ${i * 6 + Math.sin(i+1)*3}`}
                stroke={i % 3 === 0 ? wt.grain1 : wt.grain2}
                strokeWidth={i % 4 === 0 ? 0.9 : 0.4}
                strokeOpacity="0.55"
                fill="none"
              />
            ))}
          </pattern>
          <radialGradient id="centerBoss" cx="50%" cy="40%" r="60%">
            <stop offset="0%"  stopColor={wt.boss[0]} />
            <stop offset="55%" stopColor={wt.boss[1]} />
            <stop offset="100%" stopColor={wt.boss[2]} />
          </radialGradient>
          <filter id="emboss" x="-20%" y="-20%" width="140%" height="140%">
            <feGaussianBlur in="SourceAlpha" stdDeviation="0.6" />
            <feSpecularLighting result="spec" specularExponent="20" lightingColor="#fff5d8">
              <feDistantLight azimuth="45" elevation="55" />
            </feSpecularLighting>
            <feComposite in="spec" in2="SourceAlpha" operator="in" result="specOnFill"/>
            <feMerge>
              <feMergeNode in="SourceGraphic" />
              <feMergeNode in="specOnFill" />
            </feMerge>
          </filter>
        </defs>

        {/* tło drewniane */}
        <circle cx={cx} cy={cy} r={ringR} fill="url(#woodGrain)" />

        {/* segmenty - delikatne tonowanie naprzemienne */}
        {members.map((m, i) => (
          <path key={m.id} d={arcPath(i)}
            fill={i % 2 === 0 ? 'rgba(0,0,0,0.0)' : 'rgba(0,0,0,0.18)'}
          />
        ))}

        {/* kreski oddzielające - wyryte */}
        {members.map((_, i) => {
          const a = (i * seg - 90) * Math.PI / 180;
          const x1 = cx + 30 * Math.cos(a);
          const y1 = cy + 30 * Math.sin(a);
          const x2 = cx + ringR * Math.cos(a);
          const y2 = cy + ringR * Math.sin(a);
          return (
            <g key={i}>
              <line x1={x1} y1={y1} x2={x2} y2={y2} stroke={wt.line} strokeWidth="3" strokeOpacity="0.7" />
              <line x1={x1} y1={y1+1.2} x2={x2} y2={y2+1.2} stroke={wt.lineHi} strokeWidth="0.8" strokeOpacity="0.55" />
            </g>
          );
        })}

        {/* sezonowe zdobienia */}
        {wt.frost && [22, 68, 131, 197, 258, 312].map((a, i) => {
          const rad = (a - 90) * Math.PI / 180;
          const dx = cx + (ringR - 40) * Math.cos(rad);
          const dy = cy + (ringR - 40) * Math.sin(rad);
          return <ellipse key={`frost-${i}`} cx={dx} cy={dy} rx={34 + (i % 3) * 10} ry={16 + (i % 2) * 8} fill="#eaf4fd" opacity="0.07" transform={`rotate(${a} ${dx} ${dy})`} />;
        })}
        {wt.decoRing && (
          <circle cx={cx} cy={cy} r={ringR - 14} fill="none" stroke={wt.decoRing} strokeWidth="1" strokeDasharray="2 7" opacity="0.45" />
        )}
        {wt.deco && members.map((_, i) => {
          const a = i * seg - 90;
          const rad = a * Math.PI / 180;
          const dx = cx + (ringR - 26) * Math.cos(rad);
          const dy = cy + (ringR - 26) * Math.sin(rad);
          return (
            <g key={`deco-${i}`} transform={`translate(${dx} ${dy}) rotate(${a + 90})`}>
              <DecoMotif kind={wt.deco} idx={i} />
            </g>
          );
        })}

        {wt.deco2 && members.map((_, i) => {
          const a = i * seg + seg / 2 - 90;
          const rad = a * Math.PI / 180;
          const rr = wt.deco2 === 'icicle' ? ringR - 8 : ringR - 26;
          const dx = cx + rr * Math.cos(rad);
          const dy = cy + rr * Math.sin(rad);
          return (
            <g key={`deco2-${i}`} transform={`translate(${dx} ${dy}) rotate(${a + 90})`}>
              <DecoMotif2 kind={wt.deco2} idx={i} />
            </g>
          );
        })}
        {wt.deco && Array.from({ length: 8 }).map((_, i) => {
          const a = i * 45 - 90;
          const rad = a * Math.PI / 180;
          const dx = cx + (innerR + 24) * Math.cos(rad);
          const dy = cy + (innerR + 24) * Math.sin(rad);
          return (
            <g key={`wreath-${i}`} transform={`translate(${dx} ${dy}) rotate(${a + 90}) scale(0.55)`} opacity="0.9">
              <DecoMotif kind={wt.deco} idx={i} />
            </g>
          );
        })}

        {/* zdjęcia + imiona */}
        {members.map((m, i) => {
          const angle = i * seg + seg / 2 - 90; // w stopniach
          const rad = angle * Math.PI / 180;
          const px = cx + photoR * Math.cos(rad);
          const py = cy + photoR * Math.sin(rad);
          const lx = cx + labelR * Math.cos(rad);
          const ly = cy + labelR * Math.sin(rad);
          // tekst obrócony tak by był wzdłuż promienia (czytelny od środka)
          const textRot = angle + 90;
          return (
            <g key={m.id}
               onClick={() => onPhotoClick && onPhotoClick(m)}
               style={{ cursor: spinning ? 'default' : 'pointer', pointerEvents: spinning ? 'none' : 'auto' }}>
              {/* okrągła ramka pod zdjęciem - mosiądz */}
              <circle cx={px} cy={py} r={42} fill={wt.deep} />
              <circle cx={px} cy={py} r={40} fill={wt.frame} />
              <circle cx={px} cy={py} r={37} fill={wt.deep} />
              <clipPath id={`clip-${m.id}`}>
                <circle cx={px} cy={py} r={36} />
              </clipPath>
              <image href={m.img} x={px - 36} y={py - 36} width="72" height="72"
                clipPath={`url(#clip-${m.id})`} preserveAspectRatio="xMidYMid slice" />
              {/* lekki blask na ramce */}
              <circle cx={px} cy={py} r={40} fill="none" stroke={wt.frameHi} strokeWidth="1" opacity="0.6" />

              {/* etykieta */}
              <g transform={`translate(${lx} ${ly}) rotate(${textRot})`}>
                <text
                  textAnchor="middle"
                  fill={wt.text}
                  fontFamily="'IM Fell English SC', 'UnifrakturCook', serif"
                  fontSize="22"
                  fontWeight="700"
                  style={{ paintOrder: 'stroke', stroke: wt.stroke, strokeWidth: 4, strokeLinejoin: 'round' }}
                >{m.label}</text>
              </g>
            </g>
          );
        })}

        {/* nity na obwodzie */}
        {Array.from({ length: 24 }).map((_, i) => {
          const a = (i * 360 / 24 - 90) * Math.PI / 180;
          const rx = cx + (ringR + 10) * Math.cos(a);
          const ry = cy + (ringR + 10) * Math.sin(a);
          return (
            <g key={i}>
              <circle cx={rx} cy={ry} r="6" fill={wt.rivetBg} />
              <circle cx={rx-1} cy={ry-1.5} r="2.5" fill={wt.rivet} opacity="0.85" />
            </g>
          );
        })}

        {/* środkowy guz z herbem GB */}
        <circle cx={cx} cy={cy} r={innerR + 8} fill={wt.deep} />
        <circle cx={cx} cy={cy} r={innerR} fill="url(#centerBoss)" />
        <circle cx={cx} cy={cy} r={innerR - 4} fill="none" stroke={wt.bossRing} strokeWidth="1.5" opacity="0.7" />
        <text x={cx} y={cy + 2}
          textAnchor="middle" dominantBaseline="middle"
          fill={wt.bossText}
          fontFamily="'UnifrakturCook', serif"
          fontSize="38"
          fontWeight="700"
          style={{ paintOrder: 'stroke', stroke: '#0a0500', strokeWidth: 3 }}
        >GB</text>
        <text x={cx} y={cy + 28}
          textAnchor="middle" dominantBaseline="middle"
          fill={wt.bossSub}
          fontFamily="'IM Fell English SC', serif"
          fontSize="11"
          letterSpacing="2"
        >ANNO MMXXVI</text>
      </svg>
      </div>
      <RingDressing kind={wt.dress} size={size} />
    </div>
  );
}

// ====== MIECZ-WSKAZÓWKA ======
function SwordSvg() {
  return (
    <svg width="86" height="280" viewBox="0 0 86 280">
      <defs>
        <linearGradient id="bladeGrad" x1="0" x2="1" y1="0" y2="0">
          <stop offset="0%"   stopColor="#888a8e" />
          <stop offset="48%"  stopColor="#e8eaef" />
          <stop offset="50%"  stopColor="#fafbfd" />
          <stop offset="52%"  stopColor="#e8eaef" />
          <stop offset="100%" stopColor="#5b5e64" />
        </linearGradient>
        <linearGradient id="hiltGrad" x1="0" x2="1" y1="0" y2="0">
          <stop offset="0%"   stopColor="#3a2412" />
          <stop offset="50%"  stopColor="#7a4a22" />
          <stop offset="100%" stopColor="#3a2412" />
        </linearGradient>
        <linearGradient id="goldGrad" x1="0" x2="0" y1="0" y2="1">
          <stop offset="0%"  stopColor="#ffe9a3" />
          <stop offset="50%" stopColor="#b88d3a" />
          <stop offset="100%" stopColor="#5a3f12" />
        </linearGradient>
      </defs>
      {/* gałka */}
      <circle cx="43" cy="22" r="14" fill="url(#goldGrad)" stroke="#3a2412" strokeWidth="1.5" />
      <circle cx="43" cy="22" r="6"  fill="#7a1a1a" stroke="#3a2412" strokeWidth="1" />
      {/* rękojeść */}
      <rect x="38" y="34" width="10" height="40" fill="url(#hiltGrad)" stroke="#1a0e04" strokeWidth="1" />
      {Array.from({ length: 6 }).map((_, i) => (
        <line key={i} x1="38" y1={38 + i*7} x2="48" y2={38 + i*7} stroke="#1a0e04" strokeWidth="0.6" opacity="0.7" />
      ))}
      {/* jelec (krzyżak) */}
      <rect x="6" y="72" width="74" height="11" fill="url(#goldGrad)" stroke="#3a2412" strokeWidth="1.2" />
      <circle cx="10" cy="77.5" r="3" fill="#7a1a1a" stroke="#3a2412" strokeWidth="0.8" />
      <circle cx="76" cy="77.5" r="3" fill="#7a1a1a" stroke="#3a2412" strokeWidth="0.8" />
      {/* ostrze */}
      <polygon points="38,84 48,84 60,260 43,272 26,260" fill="url(#bladeGrad)" stroke="#2a2c30" strokeWidth="1" />
      {/* zbrocze (rowek) */}
      <line x1="43" y1="90" x2="43" y2="252" stroke="#5b5e64" strokeWidth="1.5" opacity="0.7" />
      {/* lekkie połyski */}
      <line x1="40" y1="92" x2="44" y2="240" stroke="#fff" strokeWidth="0.6" opacity="0.55" />
    </svg>
  );
}

function Sword({ wobble }) {
  return (
    <div className="sword-pointer" style={{ transform: `translateX(-50%) rotate(${wobble}deg)` }}>
      <SwordSvg />
    </div>
  );
}

function FallingSword() {
  return (
    <div className="falling-sword">
      <SwordSvg />
    </div>
  );
}

// ====== ŚWIECE ======
function Candle({ style }) {
  return (
    <div className="candle" style={style}>
      <div className="flame">
        <div className="flame-core" />
      </div>
      <div className="wick" />
      <div className="wax" />
      <div className="holder" />
    </div>
  );
}

// ====== KARTKA ZWYCIĘZCY ======
function WinnerCard({ winner, onClose, onAgain, onExclude }) {
  if (!winner) return null;
  return (
    <div className="winner-overlay" onClick={onClose}>
      <div className="winner-card" onClick={(e) => e.stopPropagation()}>
        <div className="winner-frame">
          <div className="winner-frame-inner">
            <img src={winner.img} alt={winner.label} className="winner-photo" />
            <div className="winner-impact-flash" />
          </div>
          <div className="winner-corner tl" />
          <div className="winner-corner tr" />
          <div className="winner-corner bl" />
          <div className="winner-corner br" />
          <FallingSword />
        </div>
        <div className="winner-scroll">
          <div className="hop">Hop hop hop!</div>
          <div className="winner-name">{winner.label}</div>
          <div className="winner-sub">prowadzi dziś radę poranną</div>
        </div>
        <div className="winner-actions">
          <button className="btn btn-primary" onClick={onAgain}>Jeszcze raz, mości panie</button>
          <button className="btn btn-ghost" onClick={onExclude}>Już swoje uczynił (zdejmij z koła)</button>
        </div>
      </div>
    </div>
  );
}

// ====== ZWÓJ HISTORII ======
function HistoryScroll({ history, onRestoreAll }) {
  return (
    <div className="history-scroll">
      <div className="scroll-cap top" />
      <div className="scroll-body">
        <div className="scroll-title">Kronika dni minionych</div>
        <div className="scroll-hint">Kliknij w zdjęcie na kole, by wpisać osobę do kroniki.</div>
        {history.length === 0 ? (
          <div className="scroll-empty">— jeszcze nic nie zapisano —</div>
        ) : (
          <ol className="scroll-list">
            {history.map((h, i) => (
              <li key={i}>
                <span className="scroll-num">{romanize(history.length - i)}</span>
                <span className="scroll-name">{h.label}</span>
                <span className="scroll-date">{h.date}</span>
              </li>
            ))}
          </ol>
        )}
        {history.length > 0 && (
          <button className="scroll-restore" onClick={onRestoreAll}>↻ Przywróć wszystkich do koła</button>
        )}
      </div>
      <div className="scroll-cap bottom" />
    </div>
  );
}

function romanize(n) {
  const map = [['M',1000],['CM',900],['D',500],['CD',400],['C',100],['XC',90],['L',50],['XL',40],['X',10],['IX',9],['V',5],['IV',4],['I',1]];
  let s = '';
  for (const [r, v] of map) { while (n >= v) { s += r; n -= v; } }
  return s;
}

// ====== EFEKTY SEZONOWE ======
function SeasonEffects({ kind }) {
  const ref = useRef(null);
  useEffect(() => {
    if (!kind) return;
    const canvas = ref.current;
    const ctx = canvas.getContext('2d');
    let W = 0, H = 0, raf = 0, last = performance.now();
    const resize = () => { W = canvas.width = window.innerWidth; H = canvas.height = window.innerHeight; };
    resize();
    window.addEventListener('resize', resize);
    const rnd = (a, b) => a + Math.random() * (b - a);
    const LEAF = ['#c9701e', '#a8481a', '#96731c', '#7c3212'];
    const PETAL = ['#f2c4d2', '#eeb0c4', '#f8dce4', '#e89cb4'];
    const count = kind === 'fireflies' ? 34 : kind === 'snow' ? 90 : 55;
    const mk = (top) => {
      const p = { x: rnd(0, W), y: top ? rnd(-60, -10) : rnd(0, H), r: 2, vx: 0, vy: 20, rot: rnd(0, 6.28), vr: rnd(-1.6, 1.6), ph: rnd(0, 6.28), a: rnd(0.5, 0.95), c: '#fff' };
      if (kind === 'snow') { p.r = rnd(1, 3.2); p.vy = rnd(18, 55); p.vx = rnd(-8, 8); p.c = '#eef5ff'; }
      else if (kind === 'leaves') { p.r = rnd(5, 11); p.vy = rnd(28, 65); p.vx = rnd(-14, 14); p.c = LEAF[Math.floor(Math.random() * LEAF.length)]; }
      else if (kind === 'petals') { p.r = rnd(3, 6.5); p.vy = rnd(16, 40); p.vx = rnd(-12, 12); p.c = PETAL[Math.floor(Math.random() * PETAL.length)]; }
      else if (kind === 'fireflies') { p.r = rnd(1.3, 2.6); p.vx = rnd(-9, 9); p.vy = rnd(-7, 7); }
      return p;
    };
    const parts = Array.from({ length: count }, () => mk(false));
    const step = (now) => {
      const dt = Math.min(0.05, (now - last) / 1000);
      last = now;
      const t = now / 1000;
      ctx.clearRect(0, 0, W, H);
      for (let i = 0; i < parts.length; i++) {
        const p = parts[i];
        if (kind === 'fireflies') {
          p.vx = Math.max(-12, Math.min(12, p.vx + rnd(-8, 8) * dt));
          p.vy = Math.max(-9, Math.min(9, p.vy + rnd(-8, 8) * dt));
          p.x += p.vx * dt; p.y += p.vy * dt;
          if (p.x < -12) p.x = W + 12; else if (p.x > W + 12) p.x = -12;
          if (p.y < -12) p.y = H + 12; else if (p.y > H + 12) p.y = -12;
          const glow = Math.max(0, Math.sin(t * 1.5 + p.ph));
          ctx.save();
          ctx.shadowBlur = 14; ctx.shadowColor = 'rgba(215,255,130,0.9)';
          ctx.fillStyle = `rgba(225,255,150,${0.12 + 0.8 * glow})`;
          ctx.beginPath(); ctx.arc(p.x, p.y, p.r, 0, 6.29); ctx.fill();
          ctx.restore();
          continue;
        }
        p.x += (p.vx + Math.sin(t * 1.2 + p.ph) * 16) * dt;
        p.y += p.vy * dt;
        p.rot += p.vr * dt;
        if (p.y > H + 24) { Object.assign(p, mk(true)); continue; }
        if (p.x < -30) p.x = W + 28; else if (p.x > W + 30) p.x = -28;
        ctx.save();
        ctx.translate(p.x, p.y);
        ctx.rotate(p.rot);
        ctx.globalAlpha = p.a;
        ctx.fillStyle = p.c;
        if (kind === 'snow') {
          ctx.beginPath(); ctx.arc(0, 0, p.r, 0, 6.29); ctx.fill();
        } else if (kind === 'petals') {
          ctx.beginPath(); ctx.ellipse(0, 0, p.r, p.r * 0.55, 0, 0, 6.29); ctx.fill();
        } else {
          ctx.beginPath();
          ctx.moveTo(0, -p.r);
          ctx.quadraticCurveTo(p.r * 0.85, 0, 0, p.r);
          ctx.quadraticCurveTo(-p.r * 0.85, 0, 0, -p.r);
          ctx.fill();
        }
        ctx.restore();
      }
      raf = requestAnimationFrame(step);
    };
    raf = requestAnimationFrame(step);
    return () => { cancelAnimationFrame(raf); window.removeEventListener('resize', resize); ctx.clearRect(0, 0, W, H); };
  }, [kind]);
  if (!kind) return null;
  return <canvas ref={ref} className="fx-canvas" />;
}

function getWheelSize() {
  const vw = window.innerWidth;
  const vh = window.innerHeight;
  const narrow = vw <= 900;
  const maxW = narrow ? vw - 32 : vw - 624;
  const maxH = vh - (narrow ? 240 : 320);
  return Math.max(200, Math.min(720, maxW, maxH));
}

// ====== APLIKACJA ======
function App() {
  const [excluded, setExcluded] = useState(() => {
    try { return JSON.parse(localStorage.getItem('gb_excluded') || '[]'); } catch { return []; }
  });
  const [history, setHistory] = useState(() => {
    try { return JSON.parse(localStorage.getItem('gb_history') || '[]'); } catch { return []; }
  });
  const [rotation, setRotation] = useState(0);
  const [spinning, setSpinning] = useState(false);
  const [winner, setWinner] = useState(null);
  const [speedKey, setSpeedKey] = useState('rycerz');
  const [theme, setTheme] = useState(() => {
    try { const t = localStorage.getItem('gb_theme'); if (t && THEMES[t]) return t; } catch {}
    return seasonForMonth(new Date().getMonth());
  });
  const T = THEMES[theme];
  const [swordWobble, setSwordWobble] = useState(0);
  const [muted, setMuted] = useState(false);
  const [wheelSize, setWheelSize] = useState(getWheelSize);
  const [loaded, setLoaded] = useState(false);

  const tickAudioRef = useRef(null);
  const lastTickSegRef = useRef(0);
  const animRef = useRef(null);
  const rotationRef = useRef(0);
  const runIdRef = useRef(0);
  const spinStartRef = useRef(0);

  // członkowie aktywni
  const active = useMemo(() => MEMBERS.filter(m => !excluded.includes(m.id)), [excluded]);

  // Fetch shared state from KV on mount; localStorage values shown as instant cache until this resolves.
  useEffect(() => {
    fetch('/api/state')
      .then(r => r.ok ? r.json() : null)
      .then(d => {
        if (d) {
          setExcluded(Array.isArray(d.excluded) ? d.excluded : []);
          setHistory(Array.isArray(d.history) ? d.history : []);
          if (d.theme && THEMES[d.theme]) setTheme(d.theme);
        }
        setLoaded(true);
      })
      .catch(() => { setLoaded(true); });
  }, []);

  // Write through to localStorage every render; POST to KV only after the initial GET has landed.
  useEffect(() => {
    localStorage.setItem('gb_excluded', JSON.stringify(excluded));
    localStorage.setItem('gb_history', JSON.stringify(history));
    localStorage.setItem('gb_theme', theme);
    if (!loaded) return;
    fetch('/api/state', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ excluded, history, theme }),
    }).catch(() => {});
  }, [excluded, history, theme, loaded]);

  useEffect(() => {
    const handle = () => setWheelSize(getWheelSize());
    window.addEventListener('resize', handle);
    return () => window.removeEventListener('resize', handle);
  }, []);

  // ticker - klikanie miecza o krawędź
  const playTick = useCallback(() => {
    if (muted) return;
    try {
      const ctx = tickAudioRef.current || new (window.AudioContext || window.webkitAudioContext)();
      tickAudioRef.current = ctx;
      if (ctx.state === 'suspended') ctx.resume();
      const t = ctx.currentTime;
      // szum
      const buf = ctx.createBuffer(1, ctx.sampleRate * 0.04, ctx.sampleRate);
      const ch = buf.getChannelData(0);
      for (let i = 0; i < ch.length; i++) ch[i] = (Math.random() * 2 - 1) * Math.exp(-i / (ctx.sampleRate * 0.008));
      const src = ctx.createBufferSource(); src.buffer = buf;
      const filter = ctx.createBiquadFilter();
      filter.type = 'bandpass'; filter.frequency.value = 2200; filter.Q.value = 4;
      const g = ctx.createGain(); g.gain.value = 0.18;
      src.connect(filter); filter.connect(g); g.connect(ctx.destination);
      src.start(t); src.stop(t + 0.05);
    } catch (e) {}
  }, [muted]);

  // animacja kręcenia
  const startAnimation = useCallback(({ fromRot, winnerIdx, preset }) => {
    const myRunId = ++runIdRef.current;
    const N = active.length;
    const seg = 360 / N;
    const targetAngle = 360 - (winnerIdx * seg + seg / 2);
    const totalRot = fromRot + Math.max(preset.turns, 3) * 360
                     + (((targetAngle - (fromRot % 360)) % 360 + 360) % 360);
    const start = performance.now();
    const dur = preset.duration * 1000;
    lastTickSegRef.current = Math.floor(((fromRot % 360) + 360) % 360 / seg);
    spinStartRef.current = start;
    const easeOut = (t) => 1 - Math.pow(1 - t, 3.6);
    const w = active[winnerIdx];
    const step = (now) => {
      if (myRunId !== runIdRef.current) return;
      const t = Math.min(1, (now - start) / dur);
      const cur = fromRot + (totalRot - fromRot) * easeOut(t);
      rotationRef.current = cur;
      setRotation(cur);
      const segIdx = Math.floor(((cur % 360) + 360) % 360 / seg);
      if (segIdx !== lastTickSegRef.current) {
        lastTickSegRef.current = segIdx;
        playTick();
        setSwordWobble(-6);
        setTimeout(() => setSwordWobble(0), 80);
      }
      if (t < 1) {
        animRef.current = requestAnimationFrame(step);
      } else {
        setSpinning(false);
        setTimeout(() => setWinner(w), 450);
      }
    };
    animRef.current = requestAnimationFrame(step);
  }, [active, playTick]);

  const spin = useCallback(() => {
    if (spinning || active.length === 0) return;
    setWinner(null);
    setSpinning(true);
    const preset = SPEED_PRESETS[speedKey];
    const winnerIdx = Math.floor(Math.random() * active.length);
    startAnimation({ fromRot: rotationRef.current, winnerIdx, preset });
  }, [spinning, active, speedKey, startAnimation]);

  const moreGas = useCallback(() => {
    if (!spinning) return;
    if (animRef.current) cancelAnimationFrame(animRef.current);
    setSwordWobble(0);
    const preset = SPEED_PRESETS[speedKey];
    const newWinnerIdx = Math.floor(Math.random() * active.length);
    startAnimation({ fromRot: rotationRef.current, winnerIdx: newWinnerIdx, preset });
  }, [spinning, active, speedKey, startAnimation]);

  const stop = useCallback(() => {
    if (!spinning) return;
    if (performance.now() - spinStartRef.current < 300) return;
    if (animRef.current) cancelAnimationFrame(animRef.current);
    runIdRef.current++;
    setSwordWobble(0);
    const N = active.length;
    const seg = 360 / N;
    const cur = rotationRef.current;
    const norm = (((-cur) % 360) + 360) % 360;
    const winnerIdx = Math.floor(norm / seg) % N;
    const w = active[winnerIdx];
    setSpinning(false);
    setTimeout(() => setWinner(w), 450);
  }, [spinning, active]);

  const onPhotoClick = useCallback((m) => {
    if (spinning) return;
    setHistory(h => [{ id: m.id, label: m.label, img: m.img, date: nowPL() }, ...h].slice(0, 20));
    setExcluded(e => e.includes(m.id) ? e : [...e, m.id]);
    setWinner(w => (w && w.id === m.id) ? null : w);
  }, [spinning]);

  useEffect(() => () => { if (animRef.current) cancelAnimationFrame(animRef.current); }, []);

  const onAgain = () => {
    setWinner(null);
  };

  const onExclude = () => {
    if (!winner) return;
    const todayLabel = nowPL();
    setHistory(h => [{ id: winner.id, label: winner.label, img: winner.img, date: todayLabel }, ...h].slice(0, 20));
    setExcluded(e => [...e, winner.id]);
    setWinner(null);
  };

  const restoreAll = () => {
    setExcluded([]);
    setHistory([]);
  };

  return (
    <div className="stage" data-theme={theme}>
      {/* tło: pergamin + drewniana podłoga + mgła */}
      <div className="bg-paper" />
      <SceneBackdrop theme={theme} />
      <div className="bg-floor" />
      <div className="bg-vignette" />
      <SeasonEffects kind={T.effect} key={T.effect || 'none'} />
      {theme === 'spring' && <Bunting />}
      {theme === 'autumn' && <FloorLeaves />}

      {/* świece w rogach — tylko w starej izbie */}
      {theme === 'legacy' && <React.Fragment>
        <Candle style={{ top: 60, left: 50 }} />
        <Candle style={{ top: 80, right: 70 }} />
        <Candle style={{ bottom: 90, left: 80 }} />
        <Candle style={{ bottom: 70, right: 60 }} />
      </React.Fragment>}

      {/* nagłówek */}
      <header className="hdr">
        <div className="hdr-eyebrow">— Rada Poranna —</div>
        <h1 className="hdr-title">Koło Fortuny</h1>
        <div className="hdr-team">
          <span className="hdr-flourish">❦</span>
          <span className="hdr-team-name">GIGA · BAJTY</span>
          <span className="hdr-flourish">❦</span>
        </div>
        <div className="hdr-sub">Któż dziś poprowadzi radę poranną?</div>
      </header>

      {/* lewa kolumna: kronika */}
      <aside className="left-col">
        <HistoryScroll history={history} onRestoreAll={restoreAll} />
      </aside>

      {/* prawa kolumna: sterowanie */}
      <aside className="right-col">
        <div className="control-panel">
          <div className="cp-title">Tempo obrotów</div>
          <div className="cp-presets">
            {Object.entries(SPEED_PRESETS).map(([k, v]) => (
              <button
                key={k}
                className={`preset ${speedKey === k ? 'active' : ''}`}
                onClick={() => setSpeedKey(k)}
                disabled={spinning}
              >
                <div className="preset-label">{v.label}</div>
                <div className="preset-sub">{v.sub}</div>
              </button>
            ))}
          </div>
          <div className="cp-actions">
            <button className="cp-action cp-action-boost" onClick={moreGas} disabled={!spinning}>
              ⚡ Niech bogowie znów rzucą
            </button>
            <button className="cp-action cp-action-stop" onClick={stop} disabled={!spinning}>
              ✋ Zatrzymaj koło
            </button>
          </div>
          <div className="cp-divider" />
          <div className="cp-subtitle">Pora roku</div>
          <div className="cp-themes">
            {THEME_ORDER.map((k) => (
              <button key={k} className={`theme-chip ${theme === k ? 'active' : ''}`} onClick={() => setTheme(k)}>
                <span>{THEMES[k].label}</span>
                <span className="theme-glyph">{THEMES[k].glyph}</span>
              </button>
            ))}
          </div>
          <div className="cp-divider" />
          <div className="cp-meta">
            <div className="cp-row">
              <span>Na kole:</span><strong>{active.length} z {MEMBERS.length}</strong>
            </div>
            <div className="cp-row">
              <span>Wykluczonych:</span><strong>{excluded.length}</strong>
            </div>
          </div>
          <button className="mute-toggle" onClick={() => setMuted(m => !m)}>
            {muted ? '🔇 Cisza wieczna' : '🔔 Klikanie miecza'}
          </button>
        </div>
      </aside>

      {/* środek: koło + miecz + przycisk */}
      <div className="wheel-stage">
        <Sword wobble={swordWobble} />
        {active.length === 0 ? (
          <div className="empty-wheel" style={{ width: wheelSize, height: wheelSize }}>
            <div className="empty-title">Wszyscy już swoje odbyli</div>
            <button className="btn btn-primary" onClick={restoreAll}>Przywróć wszystkich</button>
          </div>
        ) : (
          <Wheel rotation={rotation} members={active} size={wheelSize} wt={T.wheel} spinning={spinning} onPhotoClick={onPhotoClick} />
        )}
        <button
          className={`spin-btn ${spinning ? 'is-spinning' : ''}`}
          onClick={spin}
          disabled={spinning || active.length === 0}
        >
          <span className="spin-btn-inner">
            {spinning ? 'KOŁO W RUCHU…' : 'ZAKRĘĆ KOŁEM FORTUNY'}
          </span>
        </button>
      </div>

      {/* zwycięzca */}
      <WinnerCard
        winner={winner}
        onClose={onAgain}
        onAgain={onAgain}
        onExclude={onExclude}
      />
    </div>
  );
}

function nowPL() {
  const months = ['stycznia','lutego','marca','kwietnia','maja','czerwca','lipca','sierpnia','września','października','listopada','grudnia'];
  const d = new Date();
  return `${d.getDate()} ${months[d.getMonth()]} A.D. ${d.getFullYear()}`;
}

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