包源代码 · What Are They Wearing?

CC BY 4.0 · 请注明 @joe · v1

← 游戏详情

拿走它,改编它,用它来教

下面的代码只是这个包里可玩的入口 HTML。请下载完整 ZIP,才能连同全部相对路径素材一起编辑或改编。

⬇ 下载入口 HTML ⬇ 下载完整包
AI 修改提示的开头
请为交互式白板修改这个课堂游戏。保留大的触摸目标和全班一起玩的方式。禁止追踪、登录、收集个人信息以及远程 API 调用。请使用下载的完整 ZIP,不要只用下面显示的入口 HTML,并保留可在浏览器中直接使用的文件夹结构。请保留教学目标。原创作者:ClassBoard Games 上的 @joe(CC BY 4.0)。
仅入口 HTML · index.html · 10707.6 KB
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>What Are They Wearing?</title>

  <!-- Modern Editorial Typography: Outfit & Plus Jakarta Sans -->
  <link rel="preconnect" href="https://fonts.googleapis.com">
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
  <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@600;700;800;900&family=Plus+Jakarta+Sans:wght@500;600;700;800&display=swap" rel="stylesheet">

  <!-- React & ReactDOM 18 -->
  <script src="https://unpkg.com/react@18/umd/react.production.min.js" crossorigin></script>
  <script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js" crossorigin></script>
  <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>

  <!-- Pre-rendered Commanding British Male Voice Library (en-GB-RyanNeural -8Hz Baritone) -->
  <script src="assets/audio_ryan_uk.js"></script>

  <style>
    :root {
      --font-display: 'Outfit', system-ui, -apple-system, sans-serif;
      --font-sans: 'Plus Jakarta Sans', system-ui, -apple-system, sans-serif;
      --bg-app: #0F172A;
      --card-bg: rgba(255, 255, 255, 0.97);
      --card-border: rgba(226, 232, 240, 0.95);
      --text-main: #0F172A;
      --text-muted: #475569;
    }

    * {
      box-sizing: border-box;
      margin: 0;
      padding: 0;
    }

    html, body {
      width: 100vw;
      height: 100vh;
      overflow: hidden;
      font-family: var(--font-sans);
      background: radial-gradient(120% 120% at 50% 15%, #1E293B 0%, #0F172A 65%, #090D16 100%);
      color: var(--text-main);
      -webkit-font-smoothing: antialiased;
      user-select: none;
    }

    @keyframes subtleBreathe {
      0%, 100% { transform: translateY(0px); }
      50% { transform: translateY(-3px); }
    }

    @keyframes quickShuffle {
      0% { transform: scale(0.97) translateY(3px); filter: blur(1.2px); opacity: 0.85; }
      50% { transform: scale(1.015) translateY(-3px); filter: blur(1.8px); opacity: 0.95; }
      100% { transform: scale(0.97) translateY(3px); filter: blur(1.2px); opacity: 0.85; }
    }

    @keyframes eyeBlink {
      0%, 94%, 100% { transform: scaleY(1); }
      97% { transform: scaleY(0.08); }
    }

    @keyframes slideUpFade {
      0% { opacity: 0; transform: translateY(8px); }
      100% { opacity: 1; transform: translateY(0); }
    }

    @keyframes arrowPulseLeft {
      0%, 100% { transform: translateX(0px); }
      50% { transform: translateX(8px); }
    }

    @keyframes arrowPulseRight {
      0%, 100% { transform: translateX(0px); }
      50% { transform: translateX(-8px); }
    }

    @keyframes targetRingPing {
      0% { r: 8px; opacity: 0.95; }
      100% { r: 26px; opacity: 0; }
    }

    .person-idle {
      animation: subtleBreathe 3.8s ease-in-out infinite;
    }

    .person-spinning {
      animation: quickShuffle 0.14s ease-in-out infinite;
    }

    .eye-blink-group {
      transform-origin: center 82px;
      animation: eyeBlink 5.2s infinite;
    }

    .garment-hotspot {
      cursor: pointer;
      transition: filter 0.18s ease, opacity 0.18s ease;
    }

    .garment-hotspot:hover {
      filter: brightness(1.08) drop-shadow(0 0 8px rgba(37, 99, 235, 0.4));
    }

    .arrow-nudge-left {
      animation: arrowPulseLeft 0.75s ease-in-out infinite;
    }

    .arrow-nudge-right {
      animation: arrowPulseRight 0.75s ease-in-out infinite;
    }

    .target-ping-circle {
      animation: targetRingPing 1.1s cubic-bezier(0, 0, 0.2, 1) infinite;
    }

    .fade-in-panel {
      animation: slideUpFade 0.25s cubic-bezier(0.22, 1, 0.36, 1) forwards;
    }

    /* App Layout */
    .app-shell {
      width: 100vw;
      height: 100vh;
      max-width: 1460px;
      margin: 0 auto;
      padding: 12px 16px;
      display: flex;
      flex-direction: column;
      gap: 12px;
    }

    .top-nav {
      height: 64px;
      background: rgba(15, 23, 42, 0.82);
      backdrop-filter: blur(16px);
      border: 1px solid rgba(255, 255, 255, 0.14);
      border-radius: 16px;
      padding: 0 18px;
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 12px;
      color: #F8FAFC;
      flex-shrink: 0;
    }

    .workspace-grid {
      flex: 1;
      min-height: 0;
      display: grid;
      grid-template-columns: minmax(360px, 480px) 1fr;
      gap: 14px;
    }

    @media (max-width: 920px) {
      .workspace-grid {
        grid-template-columns: 1fr;
        grid-template-rows: 1fr 1fr;
      }
      .hide-mobile {
        display: none !important;
      }
    }

    .studio-card {
      background: var(--card-bg);
      border: 1px solid var(--card-border);
      border-radius: 20px;
      box-shadow: 0 18px 42px -10px rgba(0, 0, 0, 0.38);
      display: flex;
      flex-direction: column;
      overflow: hidden;
      position: relative;
    }

    .btn-modern {
      font-family: var(--font-sans);
      font-weight: 800;
      border: none;
      cursor: pointer;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      gap: 8px;
      transition: all 0.16s ease;
    }

    .btn-modern:active:not(:disabled) {
      transform: scale(0.96);
    }

    /* Huge Spin Button Styling */
    .btn-mega-spin {
      background: linear-gradient(135deg, #10B981 0%, #059669 50%, #047857 100%);
      color: #FFFFFF;
      font-family: var(--font-display);
      font-weight: 900;
      letter-spacing: 0.02em;
      border-radius: 16px;
      border: 2px solid #6EE7B7;
      box-shadow: 0 6px 0 #065F46, 0 12px 24px rgba(16, 185, 129, 0.4);
      cursor: pointer;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      gap: 10px;
      transition: transform 0.14s ease, box-shadow 0.14s ease, filter 0.14s ease;
    }

    .btn-mega-spin:hover:not(:disabled) {
      filter: brightness(1.06);
      transform: translateY(-2px);
      box-shadow: 0 8px 0 #065F46, 0 16px 28px rgba(16, 185, 129, 0.5);
    }

    .btn-mega-spin:active:not(:disabled) {
      transform: translateY(4px);
      box-shadow: 0 2px 0 #065F46, 0 6px 12px rgba(16, 185, 129, 0.3);
    }

    .custom-scroll::-webkit-scrollbar {
      width: 6px;
      height: 6px;
    }
    .custom-scroll::-webkit-scrollbar-thumb {
      background: #CBD5E1;
      border-radius: 999px;
    }
  </style>
</head>
<body>
  <div id="root"></div>

  <script type="text/babel">
    const { useEffect, useMemo, useState, useCallback, useRef } = React;

    // ==================== CLOTHING, COLOR & ANATOMY POOLS ====================
    const clothingPools = {
      tops: [
        { name: 'dress', singular: true },
        { name: 'jacket', singular: true },
        { name: 'sweater', singular: true },
        { name: 'raincoat', singular: true },
        { name: 'shirt', singular: true },
        { name: 't-shirt', singular: true },
        { name: 'hoodie', singular: true },
      ],
      bottoms: [
        { name: 'pants', singular: false },
        { name: 'jeans', singular: false },
        { name: 'shorts', singular: false },
        { name: 'skirt', singular: true },
      ],
      accessories: [
        { name: 'belt', singular: true },
        { name: 'boots', singular: false },
        { name: 'shoes', singular: false },
        { name: 'mittens', singular: false },
        { name: 'big hat', singular: true },
        { name: 'small hat', singular: true },
        { name: 'cap', singular: true },
        { name: 'scarf', singular: true },
        { name: 'sunglasses', singular: false },
      ],
    };

    const colors = [
      { name: 'red', hex: '#DC2626', dark: '#991B1B', light: '#FEE2E2', highlight: '#F87171' },
      { name: 'blue', hex: '#2563EB', dark: '#1E40AF', light: '#DBEAFE', highlight: '#60A5FA' },
      { name: 'green', hex: '#16A34A', dark: '#166534', light: '#DCFCE7', highlight: '#4ADE80' },
      { name: 'yellow', hex: '#EAB308', dark: '#A16207', light: '#FEF9C3', highlight: '#FDE047' },
      { name: 'purple', hex: '#7C3AED', dark: '#5B21B6', light: '#EDE9FE', highlight: '#A78BFA' },
      { name: 'orange', hex: '#EA580C', dark: '#9A3412', light: '#FFEDD5', highlight: '#FB923C' },
      { name: 'pink', hex: '#DB2777', dark: '#9D174D', light: '#FCE7F3', highlight: '#F472B6' },
      { name: 'brown', hex: '#78350F', dark: '#451A03', light: '#FEF3C7', highlight: '#B45309' },
    ];

    const skinPalettes = [
      { id: 'fair', base: '#FAD7B8', highlight: '#FFF0E3', shadow: '#D9A47E', deep: '#B37A54', lip: '#D97B78', blush: '#F4978E' },
      { id: 'warm-beige', base: '#ECC096', highlight: '#FCE2C8', shadow: '#C58E62', deep: '#99643C', lip: '#C96D68', blush: '#E8887A' },
      { id: 'olive-tan', base: '#D89E6E', highlight: '#F0C299', shadow: '#B07244', deep: '#834D25', lip: '#B65B56', blush: '#D67464' },
      { id: 'amber-brown', base: '#B97846', highlight: '#D69A6A', shadow: '#8F5326', deep: '#633412', lip: '#9B4745', blush: '#B85A4A' },
      { id: 'espresso', base: '#7E4B28', highlight: '#9D6740', shadow: '#593014', deep: '#3A1C08', lip: '#6E3234', blush: '#8A3E36' },
    ];

    const hairPalettes = [
      { name: 'Espresso', base: '#1E1815', highlight: '#4A3C35', dark: '#0B0908' },
      { name: 'Chestnut', base: '#3B2416', highlight: '#6E4730', dark: '#1F1109' },
      { name: 'Auburn', base: '#633B1F', highlight: '#9A633A', dark: '#381E0D' },
      { name: 'Golden Brown', base: '#8C5B32', highlight: '#C28B58', dark: '#543216' },
      { name: 'Raven', base: '#2B1D28', highlight: '#563D50', dark: '#140C12' },
    ];

    const eyeColors = ['#4A3222', '#2C5E7A', '#3D6B48', '#63472B', '#2B2623'];

    const getRandom = (arr) => arr[Math.floor(Math.random() * arr.length)];
    const getArticle = (colorName) =>
      ['a', 'e', 'i', 'o', 'u'].includes(colorName[0].toLowerCase()) ? 'an ' : 'a ';

    const formatItemPhrase = (item) => {
      const art = item.clothing.singular ? getArticle(item.color.name) : '';
      return `${art}${item.color.name} ${item.clothing.name}`;
    };

    const formatFullSentence = (state) => {
      if (!state) return '';
      const pronoun = state.gender === 'he' ? 'He' : 'She';
      const parts = state.items.map(formatItemPhrase);
      const listStr =
        parts.length > 1
          ? `${parts.slice(0, -1).join(', ')}, and ${parts[parts.length - 1]}`
          : parts[0];
      return `${pronoun} is wearing ${listStr}.`;
    };

    /**
     * Exact anatomical anchor coordinates for arrows pointing to every garment or accessory on the person.
     */
    const getGarmentAnchor = (clothingName, hasDress = false) => {
      switch (clothingName) {
        case 'big hat':
        case 'small hat':
        case 'cap':
          return { side: 'right', tx: 226, ty: 32, sx: 342, sy: 24 };
        case 'sunglasses':
          return { side: 'right', tx: 228, ty: 83, sx: 342, sy: 76 };
        case 'scarf':
          return { side: 'right', tx: 222, ty: 152, sx: 342, sy: 145 };
        case 'dress':
          return { side: 'left', tx: 136, ty: 238, sx: 34, sy: 225 };
        case 'jacket':
        case 'sweater':
        case 'raincoat':
        case 'shirt':
        case 't-shirt':
        case 'hoodie':
          return { side: 'left', tx: 134, ty: 214, sx: 34, sy: 200 };
        case 'belt':
          return {
            side: 'right',
            tx: hasDress ? 232 : 238,
            ty: hasDress ? 258 : 298,
            sx: 342,
            sy: hasDress ? 250 : 288,
          };
        case 'mittens':
          return { side: 'right', tx: 298, ty: 336, sx: 368, sy: 318 };
        case 'shorts':
        case 'skirt':
          return { side: 'left', tx: 140, ty: 376, sx: 34, sy: 370 };
        case 'pants':
        case 'jeans':
          return { side: 'left', tx: 145, ty: 410, sx: 34, sy: 404 };
        case 'boots':
          return { side: 'right', tx: 235, ty: 522, sx: 342, sy: 512 };
        case 'shoes':
        default:
          return { side: 'right', tx: 242, ty: 568, sx: 342, sy: 556 };
      }
    };

    const generateRandomState = (forcedGender = null) => {
      const gender = forcedGender || (Math.random() > 0.5 ? 'he' : 'she');
      const skin = getRandom(skinPalettes);
      const hair = getRandom(hairPalettes);
      const eyeColor = getRandom(eyeColors);
      const hairStyle = Math.floor(Math.random() * 2);

      const topPool = gender === 'he'
        ? clothingPools.tops.filter((item) => item.name !== 'dress')
        : clothingPools.tops;
      const top = getRandom(topPool);
      const accessory = getRandom(clothingPools.accessories);

      const items = [{ clothing: top, color: getRandom(colors) }];

      if (top.name !== 'dress') {
        const bottomPool = gender === 'he'
          ? clothingPools.bottoms.filter((item) => item.name !== 'skirt')
          : clothingPools.bottoms;
        const bottom = getRandom(bottomPool);
        items.push({ clothing: bottom, color: getRandom(colors) });
      }

      items.push({ clothing: accessory, color: getRandom(colors) });

      return {
        id: Math.random().toString(36).slice(2),
        gender,
        skin,
        hair,
        eyeColor,
        hairStyle,
        items,
      };
    };

    // ==================== COMMANDING BRITISH MALE VOICE ENGINE ====================
    class CommandingBritishVoiceEngine {
      constructor(onSpeakingChange, onSegmentIndexChange) {
        this.ctx = null;
        this.decodedCache = new Map();
        this.activeSource = null;
        this.timers = [];
        this.onSpeakingChange = onSpeakingChange;
        this.onSegmentIndexChange = onSegmentIndexChange;
        this.preferredVoice = null;
        this.initWebSpeech();
      }

      initWebSpeech() {
        if (!('speechSynthesis' in window)) return;
        const pickBritishMale = () => {
          const voices = window.speechSynthesis.getVoices() || [];
          const rankVoice = (v) => {
            const name = v.name.toLowerCase();
            const lang = (v.lang || '').toLowerCase();
            let score = 0;
            if (lang.includes('en-gb') || lang.includes('en_gb')) score += 50;
            if (name.includes('ryan')) score += 100;
            if (name.includes('google uk english male')) score += 95;
            if (name.includes('daniel')) score += 90;
            if (name.includes('arthur') || name.includes('oliver') || name.includes('george')) score += 85;
            if (name.includes('male') || name.includes('man')) score += 40;
            if (name.includes('female') || name.includes('woman') || name.includes('hazel') || name.includes('libby') || name.includes('sonia') || name.includes('serena') || name.includes('kate')) score -= 120;
            return score;
          };
          const sorted = [...voices].sort((a, b) => rankVoice(b) - rankVoice(a));
          if (sorted.length > 0 && rankVoice(sorted[0]) > 0) {
            this.preferredVoice = sorted[0];
          }
        };
        pickBritishMale();
        window.speechSynthesis.onvoiceschanged = pickBritishMale;
      }

      ensureContext() {
        if (!this.ctx) {
          const AudioCtx = window.AudioContext || window.webkitAudioContext;
          this.ctx = new AudioCtx();
        }
        if (this.ctx.state === 'suspended') {
          this.ctx.resume();
        }
      }

      stop() {
        this.timers.forEach((t) => clearTimeout(t));
        this.timers = [];
        if (this.activeSource) {
          try {
            this.activeSource.stop();
          } catch (_) {}
          this.activeSource = null;
        }
        if ('speechSynthesis' in window) {
          window.speechSynthesis.cancel();
        }
        this.onSpeakingChange?.(false);
        this.onSegmentIndexChange?.(-1);
      }

      async decodeClip(key) {
        const map = window.RYAN_UK_AUDIO;
        if (!map || !map[key]) return null;
        if (this.decodedCache.has(key)) return this.decodedCache.get(key);

        this.ensureContext();
        const dataUri = map[key];
        const base64 = dataUri.split(',')[1];
        const binary = atob(base64);
        const bytes = new Uint8Array(binary.length);
        for (let i = 0; i < binary.length; i++) {
          bytes[i] = binary.charCodeAt(i);
        }
        const audioBuffer = await this.ctx.decodeAudioData(bytes.buffer.slice(0));
        const trimmed = this.trimSilence(audioBuffer);
        this.decodedCache.set(key, trimmed);
        return trimmed;
      }

      trimSilence(buffer) {
        const channel = buffer.getChannelData(0);
        const len = channel.length;
        const threshold = 0.009;
        let start = 0;
        let end = len - 1;
        while (start < len && Math.abs(channel[start]) < threshold) start++;
        while (end > start && Math.abs(channel[end]) < threshold) end--;

        const pad = Math.floor(buffer.sampleRate * 0.025);
        start = Math.max(0, start - pad);
        end = Math.min(len - 1, end + pad);

        const newLen = Math.max(1, end - start);
        const out = this.ctx.createBuffer(buffer.numberOfChannels, newLen, buffer.sampleRate);
        for (let ch = 0; ch < buffer.numberOfChannels; ch++) {
          out.getChannelData(ch).set(buffer.getChannelData(ch).subarray(start, end));
        }
        return out;
      }

      async playClipSequence(keys, fallbackText, segmentIndices = []) {
        this.stop();
        const map = window.RYAN_UK_AUDIO;
        if (map && keys.every((k) => Boolean(map[k]))) {
          try {
            this.ensureContext();
            this.onSpeakingChange?.(true);
            const buffers = await Promise.all(keys.map((k) => this.decodeClip(k)));
            const pauseSamples = Math.floor(this.ctx.sampleRate * 0.055);
            const totalLength =
              buffers.reduce((acc, b) => acc + b.length, 0) + pauseSamples * Math.max(0, buffers.length - 1);

            const combined = this.ctx.createBuffer(
              buffers[0].numberOfChannels,
              totalLength,
              this.ctx.sampleRate
            );

            let offset = 0;
            const offsetsSec = [];
            buffers.forEach((buf) => {
              offsetsSec.push(offset / this.ctx.sampleRate);
              for (let ch = 0; ch < combined.numberOfChannels; ch++) {
                const srcChan = buf.getChannelData(Math.min(ch, buf.numberOfChannels - 1));
                combined.getChannelData(ch).set(srcChan, offset);
              }
              offset += buf.length + pauseSamples;
            });

            offsetsSec.forEach((sec, idx) => {
              const segIdx = segmentIndices[idx] ?? -1;
              const timer = setTimeout(() => {
                this.onSegmentIndexChange?.(segIdx);
              }, sec * 1000);
              this.timers.push(timer);
            });

            const source = this.ctx.createBufferSource();
            source.buffer = combined;
            source.connect(this.ctx.destination);
            this.activeSource = source;
            source.onended = () => {
              if (this.activeSource === source) {
                this.activeSource = null;
                this.onSpeakingChange?.(false);
                this.onSegmentIndexChange?.(-1);
              }
            };
            source.start(0);
            return;
          } catch (err) {
            console.warn('Studio audio fallback to WebSpeech:', err);
          }
        }
        this.speakFallback(fallbackText, segmentIndices[0] ?? -1);
      }

      speakFallback(text, segIdx = -1) {
        if (!('speechSynthesis' in window) || !text) return;
        this.stop();
        const utterance = new SpeechSynthesisUtterance(text);
        if (this.preferredVoice) {
          utterance.voice = this.preferredVoice;
        }
        utterance.lang = 'en-GB';
        utterance.pitch = 0.76;
        utterance.rate = 0.96;
        utterance.onstart = () => {
          this.onSpeakingChange?.(true);
          this.onSegmentIndexChange?.(segIdx);
        };
        utterance.onend = () => {
          this.onSpeakingChange?.(false);
          this.onSegmentIndexChange?.(-1);
        };
        window.speechSynthesis.speak(utterance);
      }

      speakOutfitSentence(gameState) {
        if (!gameState) return;
        const leadKey = gameState.gender === 'he' ? 'lead_he' : 'lead_she';
        const keys = [leadKey];
        const segIndices = [-1];

        gameState.items.forEach((item, idx) => {
          const isLast = idx === gameState.items.length - 1 && gameState.items.length > 1;
          const clipKey = isLast
            ? `and_${item.color.name}_${item.clothing.name}`
            : `pair_${item.color.name}_${item.clothing.name}`;
          keys.push(clipKey);
          segIndices.push(idx);
        });

        this.playClipSequence(keys, formatFullSentence(gameState), segIndices);
      }

      speakSingleKey(key, fallbackText, segIdx = -1) {
        this.playClipSequence([key], fallbackText, [segIdx]);
      }
    }

    // ==================== LIFELIKE HUMAN ILLUSTRATION + DYNAMIC ARROWS ====================
    function RealisticPerson({
      gender,
      skin,
      hair,
      eyeColor,
      hairStyle,
      items,
      activeSegIdx,
      inspectedItem,
      showAllArrows,
      onSelectItem,
    }) {
      const get = (name) => items.find((i) => i.clothing.name === name);

      const shoeItem = get('shoes');
      const bootsItem = get('boots');
      const scarfItem = get('scarf');
      const mittensItem = get('mittens');
      const beltItem = get('belt');
      const bigHatItem = get('big hat');
      const smallHatItem = get('small hat');
      const capItem = get('cap');
      const sunglassesItem = get('sunglasses');

      const topItem =
        get('dress') ||
        get('raincoat') ||
        get('jacket') ||
        get('hoodie') ||
        get('sweater') ||
        get('shirt') ||
        get('t-shirt');

      const pantsItem = get('pants');
      const jeansItem = get('jeans');
      const shortsItem = get('shorts');
      const skirtItem = get('skirt');
      const dressItem = get('dress');

      const topColor = topItem?.color || colors[0];
      const bottomItem = pantsItem || jeansItem || shortsItem || skirtItem;
      const bottomColor = bottomItem?.color || colors[1];

      const isFemale = gender === 'she';
      const LINE = '#1E293B';

      const handleHotspotClick = (e, item, idx) => {
        if (!item) return;
        e.stopPropagation();
        onSelectItem?.(item, idx);
      };

      // Determine which item(s) should have arrows pointing to them right now
      const focusedItem =
        activeSegIdx >= 0 && items[activeSegIdx]
          ? items[activeSegIdx]
          : inspectedItem;

      const arrowsToRender = useMemo(() => {
        if (focusedItem) {
          return [{ item: focusedItem, isPrimary: true }];
        }
        if (showAllArrows) {
          return items.map((it) => ({ item: it, isPrimary: false }));
        }
        return [];
      }, [focusedItem, showAllArrows, items]);

      return (
        <svg
          viewBox="-75 -12 530 650"
          xmlns="http://www.w3.org/2000/svg"
          style={{ width: '100%', height: '100%', overflow: 'visible' }}
        >
          <defs>
            <filter id="figureShadow" x="-20%" y="-10%" width="140%" height="130%">
              <feDropShadow dx="0" dy="10" stdDeviation="9" floodColor="#0F172A" floodOpacity="0.18" />
            </filter>
            <filter id="subtleShadow" x="-10%" y="-10%" width="120%" height="120%">
              <feDropShadow dx="0" dy="3" stdDeviation="3" floodColor="#0F172A" floodOpacity="0.15" />
            </filter>
            <filter id="arrowDropShadow" x="-30%" y="-30%" width="160%" height="160%">
              <feDropShadow dx="0" dy="4" stdDeviation="4" floodColor="#0F172A" floodOpacity="0.32" />
            </filter>

            <radialGradient id="pedestalShadow" cx="50%" cy="50%" r="50%">
              <stop offset="0%" stopColor="#0F172A" stopOpacity="0.28" />
              <stop offset="65%" stopColor="#0F172A" stopOpacity="0.08" />
              <stop offset="100%" stopColor="#0F172A" stopOpacity="0" />
            </radialGradient>

            <radialGradient id="faceSkinGrad" cx="45%" cy="38%" r="60%">
              <stop offset="0%" stopColor={skin.highlight} />
              <stop offset="55%" stopColor={skin.base} />
              <stop offset="92%" stopColor={skin.shadow} />
              <stop offset="100%" stopColor={skin.deep} />
            </radialGradient>

            <linearGradient id="neckSkinGrad" x1="0" y1="0" x2="0" y2="1">
              <stop offset="0%" stopColor={skin.deep} />
              <stop offset="32%" stopColor={skin.shadow} />
              <stop offset="100%" stopColor={skin.base} />
            </linearGradient>

            <linearGradient id="limbSkinLeft" x1="0" y1="0" x2="1" y2="0">
              <stop offset="0%" stopColor={skin.highlight} />
              <stop offset="60%" stopColor={skin.base} />
              <stop offset="100%" stopColor={skin.shadow} />
            </linearGradient>

            <linearGradient id="limbSkinRight" x1="1" y1="0" x2="0" y2="0">
              <stop offset="0%" stopColor={skin.highlight} />
              <stop offset="60%" stopColor={skin.base} />
              <stop offset="100%" stopColor={skin.shadow} />
            </linearGradient>

            <linearGradient id="hairGrad" x1="0" y1="0" x2="1" y2="1">
              <stop offset="0%" stopColor={hair.highlight} />
              <stop offset="45%" stopColor={hair.base} />
              <stop offset="100%" stopColor={hair.dark} />
            </linearGradient>

            <linearGradient id="topGrad" x1="0.2" y1="0" x2="0.8" y2="1">
              <stop offset="0%" stopColor={topColor.highlight} />
              <stop offset="45%" stopColor={topColor.hex} />
              <stop offset="100%" stopColor={topColor.dark} />
            </linearGradient>

            <linearGradient id="bottomGrad" x1="0.2" y1="0" x2="0.8" y2="1">
              <stop offset="0%" stopColor={bottomColor.highlight} />
              <stop offset="40%" stopColor={bottomColor.hex} />
              <stop offset="100%" stopColor={bottomColor.dark} />
            </linearGradient>
          </defs>

          {/* Studio Floor Pedestal Disc */}
          <ellipse cx="190" cy="602" rx="105" ry="18" fill="url(#pedestalShadow)" />

          <g filter="url(#figureShadow)">
            {/* 1. BACK HAIR LAYER */}
            {isFemale && (
              <g>
                {hairStyle === 0 ? (
                  <path
                    d="M 132 72 C 110 95, 104 145, 108 196 C 111 224, 125 238, 142 232 C 155 228, 225 228, 238 232 C 255 238, 269 224, 272 196 C 276 145, 270 95, 248 72 Z"
                    fill="url(#hairGrad)"
                    stroke={hair.dark}
                    strokeWidth="2"
                  />
                ) : (
                  <path
                    d="M 134 78 C 116 105, 115 152, 126 178 C 142 190, 238 190, 254 178 C 265 152, 264 105, 246 78 Z"
                    fill="url(#hairGrad)"
                    stroke={hair.dark}
                    strokeWidth="2"
                  />
                )}
              </g>
            )}

            {/* 2. HOOD BACK LAYER */}
            {(topItem?.clothing.name === 'hoodie' || topItem?.clothing.name === 'raincoat') && (
              <path
                d="M 132 142 C 122 115, 136 96, 190 96 C 244 96, 258 115, 248 142 Z"
                fill={topColor.dark}
                stroke={LINE}
                strokeWidth="2.2"
              />
            )}

            {/* 3. ANATOMICAL LEGS */}
            <g>
              <path
                d="M 146 330 C 144 375, 147 415, 150 442 C 146 472, 148 515, 154 558 L 172 558 C 176 515, 179 472, 176 442 C 179 415, 183 375, 184 330 Z"
                fill="url(#limbSkinLeft)"
                stroke={skin.deep}
                strokeWidth="1.8"
              />
              <path d="M 155 436 Q 163 443 171 436" fill="none" stroke={skin.shadow} strokeWidth="2" strokeLinecap="round" opacity="0.7" />
              <ellipse cx="163" cy="430" rx="7" ry="5" fill={skin.highlight} opacity="0.35" />

              <path
                d="M 196 330 C 197 375, 201 415, 204 442 C 201 472, 204 515, 208 558 L 226 558 C 232 515, 234 472, 230 442 C 233 415, 236 375, 234 330 Z"
                fill="url(#limbSkinRight)"
                stroke={skin.deep}
                strokeWidth="1.8"
              />
              <path d="M 209 436 Q 217 443 225 436" fill="none" stroke={skin.shadow} strokeWidth="2" strokeLinecap="round" opacity="0.7" />
              <ellipse cx="217" cy="430" rx="7" ry="5" fill={skin.highlight} opacity="0.35" />
            </g>

            {/* 4. SOCKS & DEFAULT LOAFERS */}
            {!bootsItem && (
              <g>
                <path d="M 152 528 L 173 528 L 172 562 L 153 562 Z" fill="#F8FAFC" stroke="#94A3B8" strokeWidth="1.6" />
                <line x1="152" y1="534" x2="173" y2="534" stroke="#CBD5E1" strokeWidth="2" />
                <path d="M 207 528 L 228 528 L 227 562 L 208 562 Z" fill="#F8FAFC" stroke="#94A3B8" strokeWidth="1.6" />
                <line x1="207" y1="534" x2="228" y2="534" stroke="#CBD5E1" strokeWidth="2" />
              </g>
            )}

            {!shoeItem && !bootsItem && (
              <g>
                <path d="M 151 556 C 144 562, 134 572, 134 581 C 134 587, 144 590, 162 590 C 172 590, 176 586, 175 576 L 173 556 Z" fill="#E2E8F0" stroke="#475569" strokeWidth="2" />
                <path d="M 207 556 L 205 576 C 204 586, 208 590, 218 590 C 236 590, 246 587, 246 581 C 246 572, 236 562, 229 556 Z" fill="#E2E8F0" stroke="#475569" strokeWidth="2" />
              </g>
            )}

            {/* 5. BOTTOMS */}
            <g className="garment-hotspot" onClick={(e) => handleHotspotClick(e, bottomItem, 1)}>
              {shortsItem && (
                <g>
                  <path
                    d="M 140 296 L 240 296 L 238 410 C 223 415, 206 415, 194 408 L 190 352 L 186 408 C 174 415, 157 415, 142 410 Z"
                    fill="url(#bottomGrad)"
                    stroke={LINE}
                    strokeWidth="2.4"
                    strokeLinejoin="round"
                  />
                  <line x1="190" y1="298" x2="190" y2="348" stroke={bottomColor.dark} strokeWidth="2" />
                  <path d="M 141 306 Q 156 320 144 342" fill="none" stroke={bottomColor.dark} strokeWidth="1.8" />
                  <path d="M 239 306 Q 224 320 236 342" fill="none" stroke={bottomColor.dark} strokeWidth="1.8" />
                </g>
              )}

              {pantsItem && (
                <g>
                  <path
                    d="M 140 295 L 240 295 C 241 365, 236 445, 231 548 C 221 553, 212 553, 204 548 C 201 462, 196 392, 190 350 C 184 392, 179 462, 176 548 C 168 553, 159 553, 149 548 C 144 445, 139 365, 140 295 Z"
                    fill="url(#bottomGrad)"
                    stroke={LINE}
                    strokeWidth="2.4"
                    strokeLinejoin="round"
                  />
                  <line x1="163" y1="340" x2="163" y2="542" stroke="#FFFFFF" strokeOpacity="0.22" strokeWidth="1.8" />
                  <line x1="217" y1="340" x2="217" y2="542" stroke="#FFFFFF" strokeOpacity="0.22" strokeWidth="1.8" />
                  <line x1="190" y1="296" x2="190" y2="346" stroke={bottomColor.dark} strokeWidth="2" />
                </g>
              )}

              {jeansItem && (
                <g>
                  <path
                    d="M 140 295 L 240 295 C 241 365, 236 445, 231 548 C 221 553, 212 553, 204 548 C 201 462, 196 392, 190 350 C 184 392, 179 462, 176 548 C 168 553, 159 553, 149 548 C 144 445, 139 365, 140 295 Z"
                    fill="url(#bottomGrad)"
                    stroke={LINE}
                    strokeWidth="2.4"
                    strokeLinejoin="round"
                  />
                  <ellipse cx="164" cy="395" rx="11" ry="48" fill="#FFFFFF" opacity="0.18" />
                  <ellipse cx="216" cy="395" rx="11" ry="48" fill="#FFFFFF" opacity="0.18" />
                  <path d="M 190 296 L 190 345 Q 182 344 181 328" fill="none" stroke="#F59E0B" strokeWidth="1.6" strokeDasharray="3 2" />
                  <rect x="147" y="535" width="31" height="14" rx="3" fill="#E2E8F0" stroke={LINE} strokeWidth="2" />
                  <rect x="202" y="535" width="31" height="14" rx="3" fill="#E2E8F0" stroke={LINE} strokeWidth="2" />
                </g>
              )}

              {skirtItem && (
                <g>
                  <path
                    d="M 142 294 L 238 294 C 248 342, 256 392, 260 434 C 215 446, 165 446, 120 434 C 124 392, 132 342, 142 294 Z"
                    fill="url(#bottomGrad)"
                    stroke={LINE}
                    strokeWidth="2.4"
                    strokeLinejoin="round"
                  />
                  <path d="M 162 302 L 148 437" stroke={bottomColor.dark} strokeWidth="2" opacity="0.55" />
                  <path d="M 198 302 L 204 440" stroke={bottomColor.dark} strokeWidth="2" opacity="0.55" />
                </g>
              )}
            </g>

            {/* 6. SHOES & BOOTS */}
            <g className="garment-hotspot" onClick={(e) => handleHotspotClick(e, shoeItem || bootsItem, items.length - 1)}>
              {shoeItem && (
                <g>
                  <path d="M 150 552 L 174 552 L 176 578 C 176 588, 168 592, 152 592 L 130 592 C 122 592, 120 582, 128 574 C 136 568, 146 562, 150 552 Z" fill={shoeItem.color.hex} stroke={LINE} strokeWidth="2.2" />
                  <path d="M 122 583 L 176 583 L 175 592 L 124 592 Z" fill="#FFFFFF" stroke={LINE} strokeWidth="1.8" />
                  <path d="M 142 565 L 152 569 M 145 560 L 155 564" stroke="#FFFFFF" strokeWidth="2.2" strokeLinecap="round" />

                  <path d="M 206 552 L 230 552 C 234 562, 244 568, 252 574 C 260 582, 258 592, 250 592 L 228 592 C 212 592, 204 588, 204 578 Z" fill={shoeItem.color.hex} stroke={LINE} strokeWidth="2.2" />
                  <path d="M 204 583 L 258 583 L 256 592 L 205 592 Z" fill="#FFFFFF" stroke={LINE} strokeWidth="1.8" />
                  <path d="M 238 565 L 228 569 M 235 560 L 225 564" stroke="#FFFFFF" strokeWidth="2.2" strokeLinecap="round" />
                </g>
              )}

              {bootsItem && (
                <g>
                  <path d="M 146 476 L 176 476 L 176 578 C 176 588, 168 592, 152 592 L 128 592 C 120 592, 118 580, 126 572 C 136 564, 144 556, 146 540 Z" fill={bootsItem.color.hex} stroke={LINE} strokeWidth="2.4" />
                  <path d="M 120 584 L 176 584 L 176 593 L 121 593 Z" fill="#1E293B" stroke={LINE} strokeWidth="1.5" />
                  <path d="M 204 476 L 234 476 L 234 540 C 236 556, 244 564, 254 572 C 262 580, 260 592, 252 592 L 228 592 C 212 592, 204 588, 204 578 Z" fill={bootsItem.color.hex} stroke={LINE} strokeWidth="2.4" />
                  <path d="M 204 584 L 260 584 L 259 593 L 204 593 Z" fill="#1E293B" stroke={LINE} strokeWidth="1.5" />
                </g>
              )}
            </g>

            {/* 7. ANATOMICAL ARMS & HANDS */}
            <g>
              <path d="M 134 166 C 116 182, 104 212, 98 244 C 93 272, 88 298, 86 320 L 103 323 C 108 300, 114 274, 120 248 C 126 222, 134 198, 144 180 Z" fill="url(#limbSkinLeft)" stroke={skin.deep} strokeWidth="1.8" />
              <path d="M 86 318 C 81 328, 79 342, 83 350 C 87 356, 96 355, 100 347 C 103 340, 104 332, 106 336 C 109 340, 113 336, 111 329 C 109 324, 105 320, 102 318 Z" fill="url(#limbSkinLeft)" stroke={skin.deep} strokeWidth="1.6" />

              <path d="M 246 166 C 264 182, 276 212, 282 244 C 287 272, 292 298, 294 320 L 277 323 C 272 300, 266 274, 260 248 C 254 222, 246 198, 236 180 Z" fill="url(#limbSkinRight)" stroke={skin.deep} strokeWidth="1.8" />
              <path d="M 294 318 C 299 328, 301 342, 297 350 C 293 356, 284 355, 280 347 C 277 340, 276 332, 274 336 C 271 340, 267 336, 269 329 C 271 324, 275 320, 278 318 Z" fill="url(#limbSkinRight)" stroke={skin.deep} strokeWidth="1.6" />
            </g>

            {/* 8. NECK, COLLARBONE & TORSO BASE */}
            <g>
              <path d="M 134 158 C 154 148, 226 148, 246 158 L 240 300 L 140 300 Z" fill="url(#faceSkinGrad)" stroke={skin.deep} strokeWidth="1.8" />
              <path d="M 173 116 L 171 154 C 178 162, 202 162, 209 154 L 207 116 Z" fill="url(#neckSkinGrad)" stroke={skin.deep} strokeWidth="1.6" />
              <path d="M 156 157 Q 172 163 184 159 M 224 157 Q 208 163 196 159" fill="none" stroke={skin.shadow} strokeWidth="1.6" strokeLinecap="round" opacity="0.65" />
            </g>

            {/* 9. TOPS & DRESS */}
            <g className="garment-hotspot" onClick={(e) => handleHotspotClick(e, topItem, 0)}>
              {topItem?.clothing.name === 't-shirt' && (
                <g>
                  <path
                    d="M 134 156 C 116 164, 104 188, 99 216 L 123 224 C 127 206, 133 192, 139 184 L 138 302 C 172 308, 208 308, 242 302 L 241 184 C 247 192, 253 206, 257 224 L 281 216 C 276 188, 264 164, 246 156 C 232 151, 218 148, 210 148 C 204 164, 176 164, 170 148 C 162 148, 148 151, 134 156 Z"
                    fill="url(#topGrad)"
                    stroke={LINE}
                    strokeWidth="2.4"
                    strokeLinejoin="round"
                  />
                  <path d="M 166 148 C 174 168, 206 168, 214 148 C 209 146, 208 159, 190 159 C 172 159, 171 146, 166 148 Z" fill={topColor.dark} stroke={LINE} strokeWidth="1.8" />
                </g>
              )}

              {topItem?.clothing.name === 'shirt' && (
                <g>
                  <path
                    d="M 134 155 C 114 166, 100 205, 86 308 L 107 312 C 117 244, 126 204, 139 184 L 138 304 C 172 312, 208 312, 242 304 L 241 184 C 254 204, 263 244, 273 312 L 294 308 C 280 205, 266 166, 246 155 L 212 146 L 190 164 L 168 146 Z"
                    fill="url(#topGrad)"
                    stroke={LINE}
                    strokeWidth="2.4"
                    strokeLinejoin="round"
                  />
                  <rect x="185" y="164" width="10" height="142" fill="#FFFFFF" fillOpacity="0.22" stroke={topColor.dark} strokeWidth="1.2" />
                  <path d="M 168 145 L 190 166 L 174 176 L 160 154 Z" fill="#FFFFFF" stroke={LINE} strokeWidth="2" />
                  <path d="M 212 145 L 190 166 L 206 176 L 220 154 Z" fill="#FFFFFF" stroke={LINE} strokeWidth="2" />
                  {[184, 212, 240, 268].map((y) => (
                    <circle key={y} cx="190" cy={y} r="3" fill="#F8FAFC" stroke={LINE} strokeWidth="1.4" />
                  ))}
                </g>
              )}

              {topItem?.clothing.name === 'sweater' && (
                <g>
                  <path
                    d="M 132 154 C 112 166, 98 205, 84 310 L 108 314 C 118 248, 126 206, 138 186 L 136 304 L 244 304 L 242 186 C 254 206, 262 248, 272 314 L 296 310 C 282 205, 268 166, 248 154 C 232 148, 214 146, 212 146 C 202 162, 178 162, 168 146 Z"
                    fill="url(#topGrad)"
                    stroke={LINE}
                    strokeWidth="2.4"
                    strokeLinejoin="round"
                  />
                  <rect x="136" y="290" width="108" height="14" rx="4" fill={topColor.dark} stroke={LINE} strokeWidth="2" />
                  <g opacity="0.32" stroke="#FFFFFF" strokeWidth="2" fill="none">
                    <path d="M 170 174 L 190 198 L 170 222 L 190 246 L 170 270" />
                    <path d="M 210 174 L 190 198 L 210 222 L 190 246 L 210 270" />
                  </g>
                </g>
              )}

              {topItem?.clothing.name === 'jacket' && (
                <g>
                  <path d="M 164 150 C 176 164, 204 164, 216 150 L 216 300 L 164 300 Z" fill="#F8FAFC" stroke={LINE} strokeWidth="1.8" />
                  <path d="M 132 154 C 112 166, 98 205, 84 312 L 108 316 C 118 248, 126 206, 138 186 L 136 304 L 174 304 L 174 160 L 166 146 Z" fill="url(#topGrad)" stroke={LINE} strokeWidth="2.4" />
                  <path d="M 248 154 C 268 166, 282 205, 296 312 L 272 316 C 262 248, 254 206, 242 186 L 244 304 L 206 304 L 206 160 L 214 146 Z" fill="url(#topGrad)" stroke={LINE} strokeWidth="2.4" />
                  <path d="M 166 146 L 152 178 L 174 188 Z" fill={topColor.dark} stroke={LINE} strokeWidth="2" />
                  <path d="M 214 146 L 228 178 L 206 188 Z" fill={topColor.dark} stroke={LINE} strokeWidth="2" />
                </g>
              )}

              {topItem?.clothing.name === 'hoodie' && (
                <g>
                  <path
                    d="M 132 154 C 112 166, 98 205, 84 310 L 108 314 C 118 248, 126 206, 138 186 L 136 306 L 244 306 L 242 186 C 254 206, 262 248, 272 314 L 296 310 C 282 205, 268 166, 248 154 L 220 144 C 204 162, 176 162, 160 144 Z"
                    fill="url(#topGrad)"
                    stroke={LINE}
                    strokeWidth="2.4"
                    strokeLinejoin="round"
                  />
                  <path d="M 152 242 L 228 242 L 236 292 L 144 292 Z" fill={topColor.dark} fillOpacity="0.4" stroke={LINE} strokeWidth="2" />
                  <path d="M 178 156 Q 175 182 177 202 M 202 156 Q 205 182 203 202" fill="none" stroke="#F8FAFC" strokeWidth="3" strokeLinecap="round" />
                </g>
              )}

              {topItem?.clothing.name === 'raincoat' && (
                <g>
                  <path
                    d="M 132 154 C 110 166, 96 205, 82 312 L 108 316 C 118 248, 126 206, 138 186 L 130 342 C 170 350, 210 350, 250 342 L 242 186 C 254 206, 262 248, 272 316 L 298 312 C 284 205, 270 166, 248 154 L 218 144 C 204 162, 176 162, 162 144 Z"
                    fill="url(#topGrad)"
                    stroke={LINE}
                    strokeWidth="2.4"
                    strokeLinejoin="round"
                  />
                  <rect x="183" y="156" width="14" height="188" fill={topColor.highlight} stroke={LINE} strokeWidth="1.8" />
                  {[178, 218, 258, 298].map((y) => (
                    <circle key={y} cx="190" cy={y} r="3.6" fill="#E2E8F0" stroke={LINE} strokeWidth="1.5" />
                  ))}
                </g>
              )}

              {dressItem && (
                <g>
                  <path
                    d="M 136 156 C 120 164, 108 184, 104 208 L 126 215 C 130 198, 136 186, 142 180 L 146 258 C 130 322, 118 386, 112 448 C 162 462, 218 462, 268 448 C 262 386, 250 322, 234 258 L 238 180 C 244 186, 250 198, 254 215 L 276 208 C 272 184, 260 164, 244 156 L 214 148 C 204 166, 176 166, 166 148 Z"
                    fill="url(#topGrad)"
                    stroke={LINE}
                    strokeWidth="2.4"
                    strokeLinejoin="round"
                  />
                  <path d="M 146 258 Q 190 266 234 258" fill="none" stroke={topColor.dark} strokeWidth="2.5" />
                </g>
              )}
            </g>

            {/* 10. BELT, MITTENS, SCARF */}
            <g className="garment-hotspot" onClick={(e) => handleHotspotClick(e, beltItem || mittensItem || scarfItem, items.length - 1)}>
              {beltItem && (
                <g>
                  <rect x={dressItem ? 145 : 139} y={dressItem ? 252 : 292} width={dressItem ? 90 : 102} height="13" rx="4" fill={beltItem.color.hex} stroke={LINE} strokeWidth="2.2" />
                  <rect x="180" y={dressItem ? 249 : 289} width="20" height="19" rx="3" fill="none" stroke="#FBBF24" strokeWidth="3.5" />
                </g>
              )}

              {mittensItem && (
                <g>
                  <path d="M 83 314 C 74 324, 72 344, 80 355 C 88 364, 102 360, 105 348 C 109 348, 115 342, 113 334 C 111 326, 106 322, 104 316 Z" fill={mittensItem.color.hex} stroke={LINE} strokeWidth="2.2" />
                  <path d="M 297 314 C 306 324, 308 344, 300 355 C 292 364, 278 360, 275 348 C 271 348, 265 342, 267 334 C 269 326, 274 322, 276 316 Z" fill={mittensItem.color.hex} stroke={LINE} strokeWidth="2.2" />
                </g>
              )}

              {scarfItem && (
                <g filter="url(#subtleShadow)">
                  <path d="M 152 136 C 172 148, 208 148, 228 136 C 234 148, 228 164, 190 166 C 152 164, 146 148, 152 136 Z" fill={scarfItem.color.hex} stroke={LINE} strokeWidth="2.2" />
                  <path d="M 198 158 C 204 188, 202 222, 206 248 L 226 246 C 224 218, 224 186, 216 154 Z" fill={scarfItem.color.highlight} stroke={LINE} strokeWidth="2.2" />
                </g>
              )}
            </g>

            {/* 11. REALISTIC SCULPTED HEAD & FACIAL FEATURES */}
            <g>
              <path d="M 145 76 C 136 74, 134 90, 141 100 C 144 104, 148 102, 149 98" fill={skin.base} stroke={skin.deep} strokeWidth="1.8" />
              <path d="M 235 76 C 244 74, 246 90, 239 100 C 236 104, 232 102, 231 98" fill={skin.base} stroke={skin.deep} strokeWidth="1.8" />

              <path
                d="M 146 68 C 146 36, 166 24, 190 24 C 214 24, 234 36, 234 68 C 234 86, 231 102, 222 114 C 212 126, 200 133, 190 133 C 180 133, 168 126, 158 114 C 149 102, 146 86, 146 68 Z"
                fill="url(#faceSkinGrad)"
                stroke={skin.deep}
                strokeWidth="2"
              />

              <ellipse cx="163" cy="95" rx="9" ry="5.5" fill={skin.blush} opacity="0.28" />
              <ellipse cx="217" cy="95" rx="9" ry="5.5" fill={skin.blush} opacity="0.28" />

              {isFemale ? (
                <g fill={hair.dark}>
                  <path d="M 158 71 Q 169 66 180 70 Q 169 69 158 73 Z" />
                  <path d="M 222 71 Q 211 66 200 70 Q 211 69 222 73 Z" />
                </g>
              ) : (
                <g fill={hair.dark}>
                  <path d="M 157 70 Q 169 66 181 70 L 180 73 Q 169 70 157 73 Z" />
                  <path d="M 223 70 Q 211 66 199 70 L 200 73 Q 211 70 223 73 Z" />
                </g>
              )}

              <g className="eye-blink-group">
                <path d="M 160 82 Q 170 75 180 82 Q 170 88 160 82 Z" fill="#FFFFFF" stroke={skin.deep} strokeWidth="1.2" />
                <circle cx="170" cy="82" r="4.8" fill={eyeColor} />
                <circle cx="170" cy="82" r="2.3" fill="#0F172A" />
                <circle cx="171.6" cy="80.4" r="1.3" fill="#FFFFFF" />
                <path d="M 159 82 Q 170 74 181 82" fill="none" stroke="#1E1815" strokeWidth={isFemale ? '2.4' : '1.9'} strokeLinecap="round" />

                <path d="M 200 82 Q 210 75 220 82 Q 210 88 200 82 Z" fill="#FFFFFF" stroke={skin.deep} strokeWidth="1.2" />
                <circle cx="210" cy="82" r="4.8" fill={eyeColor} />
                <circle cx="210" cy="82" r="2.3" fill="#0F172A" />
                <circle cx="211.6" cy="80.4" r="1.3" fill="#FFFFFF" />
                <path d="M 199 82 Q 210 74 221 82" fill="none" stroke="#1E1815" strokeWidth={isFemale ? '2.4' : '1.9'} strokeLinecap="round" />
              </g>

              <path d="M 188 78 C 187 86, 185 93, 185 96 C 186 99, 194 99, 195 96" fill="none" stroke={skin.deep} strokeWidth="1.6" strokeLinecap="round" />
              <circle cx="190" cy="94" r="2.2" fill={skin.highlight} opacity="0.55" />

              <path d="M 178 109 Q 185 106 190 108 Q 195 106 202 109 Q 190 113 178 109 Z" fill={skin.lip} />
              <path d="M 178 109 Q 190 113 202 109 Q 196 117 190 117 Q 184 117 178 109 Z" fill={skin.lip} opacity="0.85" />
              <path d="M 177 108.5 Q 190 114.5 203 108.5" fill="none" stroke={skin.deep} strokeWidth="1.6" strokeLinecap="round" />

              {/* 12. FRONT HAIR LAYER */}
              {!isFemale ? (
                <path
                  d="M 144 74 C 142 46, 156 20, 190 18 C 224 20, 238 46, 236 74 C 234 62, 228 50, 218 46 C 202 54, 174 56, 156 48 C 149 54, 146 64, 144 74 Z"
                  fill="url(#hairGrad)"
                  stroke={hair.dark}
                  strokeWidth="1.8"
                />
              ) : (
                <path
                  d="M 143 78 C 140 42, 160 18, 190 18 C 220 18, 240 42, 237 78 C 232 58, 214 44, 192 40 C 172 46, 152 58, 143 78 Z"
                  fill="url(#hairGrad)"
                  stroke={hair.dark}
                  strokeWidth="1.8"
                />
              )}

              {/* 13. EYEWEAR & HEADWEAR */}
              <g
                className="garment-hotspot"
                onClick={(e) =>
                  handleHotspotClick(e, sunglassesItem || bigHatItem || smallHatItem || capItem, items.length - 1)
                }
              >
                {sunglassesItem && (
                  <g filter="url(#subtleShadow)">
                    <rect x="153" y="74" width="32" height="19" rx="6" fill={sunglassesItem.color.hex} stroke={LINE} strokeWidth="2.2" />
                    <rect x="195" y="74" width="32" height="19" rx="6" fill={sunglassesItem.color.hex} stroke={LINE} strokeWidth="2.2" />
                    <path d="M 185 80 Q 190 77 195 80" fill="none" stroke={LINE} strokeWidth="2.8" />
                    <line x1="145" y1="79" x2="153" y2="80" stroke={LINE} strokeWidth="2.5" />
                    <line x1="227" y1="80" x2="235" y2="79" stroke={LINE} strokeWidth="2.5" />
                    <path d="M 159 78 L 168 78 L 163 89 L 156 89 Z M 201 78 L 210 78 L 205 89 L 198 89 Z" fill="#FFFFFF" opacity="0.32" />
                  </g>
                )}

                {bigHatItem && (
                  <g filter="url(#subtleShadow)">
                    <ellipse cx="190" cy="36" rx="82" ry="16" fill={bigHatItem.color.hex} stroke={LINE} strokeWidth="2.4" />
                    <path d="M 148 34 C 152 4, 168 -4, 190 -4 C 212 -4, 228 4, 232 34 Z" fill={bigHatItem.color.highlight} stroke={LINE} strokeWidth="2.4" />
                    <path d="M 149 27 Q 190 34 231 27 L 232 34 Q 190 41 148 34 Z" fill={bigHatItem.color.dark} />
                  </g>
                )}

                {smallHatItem && (
                  <g filter="url(#subtleShadow)">
                    <path d="M 144 48 C 144 16, 164 8, 190 8 C 216 8, 236 16, 236 48 Z" fill={smallHatItem.color.hex} stroke={LINE} strokeWidth="2.2" />
                    <rect x="141" y="38" width="98" height="14" rx="6" fill={smallHatItem.color.dark} stroke={LINE} strokeWidth="2.2" />
                  </g>
                )}

                {capItem && (
                  <g filter="url(#subtleShadow)">
                    <path d="M 144 46 C 144 16, 164 10, 190 10 C 216 10, 236 16, 236 46 Z" fill={capItem.color.hex} stroke={LINE} strokeWidth="2.2" />
                    <path d="M 144 44 Q 194 34 254 48 Q 236 60 192 52 Q 162 49 144 44 Z" fill={capItem.color.dark} stroke={LINE} strokeWidth="2.2" />
                  </g>
                )}
              </g>
            </g>
          </g>

          {/* ==================== 14. DYNAMIC POINTER ARROWS TO CLOTHES & ACCESSORIES ==================== */}
          {arrowsToRender.map(({ item, isPrimary }, idx) => {
            const anchor = getGarmentAnchor(item.clothing.name, Boolean(dressItem));
            const isLeft = anchor.side === 'left';
            const dx = anchor.tx - anchor.sx;
            const dy = anchor.ty - anchor.sy;
            const angleDeg = (Math.atan2(dy, dx) * 180) / Math.PI;
            const phrase = formatItemPhrase(item);
            const pillWidth = Math.max(108, phrase.length * 7.8 + 22);
            const pillX = isLeft ? anchor.sx - pillWidth + 12 : anchor.sx - 12;
            const pillY = anchor.sy - 34;

            return (
              <g
                key={`arrow-${item.clothing.name}-${idx}`}
                filter="url(#arrowDropShadow)"
                style={{ pointerEvents: 'none' }}
              >
                {/* Pulsing Bullseye Target Ring on the Garment */}
                {isPrimary && (
                  <circle
                    cx={anchor.tx}
                    cy={anchor.ty}
                    r="12"
                    fill="none"
                    stroke={item.color.hex}
                    strokeWidth="3.5"
                    className="target-ping-circle"
                  />
                )}
                <circle
                  cx={anchor.tx}
                  cy={anchor.ty}
                  r={isPrimary ? '7' : '5'}
                  fill={item.color.hex}
                  stroke="#FFFFFF"
                  strokeWidth="2.5"
                />

                {/* Animated Pointer Shaft & Arrowhead */}
                <g className={isLeft ? 'arrow-nudge-left' : 'arrow-nudge-right'}>
                  {/* White Contrast Outer Shaft */}
                  <line
                    x1={anchor.sx}
                    y1={anchor.sy}
                    x2={anchor.tx}
                    y2={anchor.ty}
                    stroke="#FFFFFF"
                    strokeWidth={isPrimary ? '10' : '7.5'}
                    strokeLinecap="round"
                  />
                  {/* Colored Inner Shaft */}
                  <line
                    x1={anchor.sx}
                    y1={anchor.sy}
                    x2={anchor.tx}
                    y2={anchor.ty}
                    stroke={item.color.hex}
                    strokeWidth={isPrimary ? '6' : '4.5'}
                    strokeLinecap="round"
                  />
                  {/* Arrowhead at Target (tx, ty) */}
                  <g transform={`translate(${anchor.tx}, ${anchor.ty}) rotate(${angleDeg})`}>
                    <polygon
                      points="0,0 -20,-11 -16,0 -20,11"
                      fill={item.color.hex}
                      stroke="#FFFFFF"
                      strokeWidth="2.5"
                      strokeLinejoin="round"
                    />
                  </g>

                  {/* Floating Callout Label Pill at Arrow Origin */}
                  <g>
                    <rect
                      x={pillX}
                      y={pillY}
                      width={pillWidth}
                      height="28"
                      rx="14"
                      fill="#0F172A"
                      stroke={item.color.hex}
                      strokeWidth="2.5"
                    />
                    <circle
                      cx={pillX + 14}
                      cy={pillY + 14}
                      r="6"
                      fill={item.color.hex}
                      stroke="#FFFFFF"
                      strokeWidth="1.5"
                    />
                    <text
                      x={pillX + 26}
                      y={pillY + 18.5}
                      fill="#FFFFFF"
                      fontFamily="Outfit, sans-serif"
                      fontWeight="800"
                      fontSize="13.5"
                    >
                      {phrase}
                    </text>
                  </g>
                </g>
              </g>
            );
          })}
        </svg>
      );
    }

    // ==================== MAIN APPLICATION ====================
    function App() {
      const [gameState, setGameState] = useState(() => generateRandomState());
      const [mode, setMode] = useState('reveal'); // 'reveal' | 'quiz' | 'wardrobe'
      const [isSpinning, setIsSpinning] = useState(false);
      const [showAnswer, setShowAnswer] = useState(false);
      const [autoSpeak, setAutoSpeak] = useState(true);
      const [isSpeaking, setIsSpeaking] = useState(false);
      const [speakingSegIdx, setSpeakingSegIdx] = useState(-1);
      const [inspectedItem, setInspectedItem] = useState(null);

      // Quiz State
      const [quizOptions, setQuizOptions] = useState([]);
      const [selectedQuizIdx, setSelectedQuizIdx] = useState(null);
      const [score, setScore] = useState(0);
      const [streak, setStreak] = useState(0);

      const voiceRef = useRef(null);
      if (!voiceRef.current) {
        voiceRef.current = new CommandingBritishVoiceEngine(
          (speaking) => setIsSpeaking(speaking),
          (idx) => setSpeakingSegIdx(idx)
        );
      }

      // Build multiple choice options whenever gameState changes
      useEffect(() => {
        if (!gameState) return;
        const correctSentence = formatFullSentence(gameState);
        const distractors = [];
        while (distractors.length < 3) {
          const fake = generateRandomState(gameState.gender);
          const s = formatFullSentence(fake);
          if (s !== correctSentence && !distractors.includes(s)) {
            distractors.push(s);
          }
        }
        const combined = [
          { text: correctSentence, isCorrect: true },
          ...distractors.map((d) => ({ text: d, isCorrect: false })),
        ].sort(() => Math.random() - 0.5);
        setQuizOptions(combined);
        setSelectedQuizIdx(null);
      }, [gameState]);

      const spinRoulette = useCallback(
        (forcedGender = null) => {
          if (isSpinning) return;
          voiceRef.current?.stop();
          setIsSpinning(true);
          setShowAnswer(false);
          setInspectedItem(null);

          let spins = 0;
          let finalState = null;
          const iv = setInterval(() => {
            finalState = generateRandomState(forcedGender);
            setGameState(finalState);
            spins += 1;
            if (spins >= 8) {
              clearInterval(iv);
              setIsSpinning(false);
              if (autoSpeak && finalState) {
                const qKey = finalState.gender === 'he' ? 'spin_he' : 'spin_she';
                const qText = `Look closely. What is ${finalState.gender} wearing?`;
                voiceRef.current?.speakSingleKey(qKey, qText, -1);
              }
            }
          }, 70);
        },
        [isSpinning, autoSpeak]
      );

      const handleRevealAnswer = useCallback(() => {
        if (isSpinning) return;
        setShowAnswer(true);
        setInspectedItem(null);
        if (autoSpeak) {
          voiceRef.current?.speakOutfitSentence(gameState);
        }
      }, [isSpinning, autoSpeak, gameState]);

      // Keyboard Shortcuts: Space = Spin, Enter = Reveal / Speak
      useEffect(() => {
        const onKey = (e) => {
          if (e.target.tagName === 'INPUT' || e.target.tagName === 'SELECT') return;
          if (e.code === 'Space') {
            e.preventDefault();
            spinRoulette();
          } else if (e.code === 'Enter') {
            e.preventDefault();
            if (!showAnswer) handleRevealAnswer();
            else voiceRef.current?.speakOutfitSentence(gameState);
          }
        };
        window.addEventListener('keydown', onKey);
        return () => window.removeEventListener('keydown', onKey);
      }, [spinRoulette, showAnswer, handleRevealAnswer, gameState]);

      const handleSelectGarment = (item, idx = -1) => {
        if (!item) return;
        setInspectedItem(item);
        const key = `pair_${item.color.name}_${item.clothing.name}`;
        voiceRef.current?.speakSingleKey(key, formatItemPhrase(item), idx);
      };

      const handleQuizPick = (opt, idx) => {
        if (selectedQuizIdx !== null) return;
        setSelectedQuizIdx(idx);
        if (opt.isCorrect) {
          setScore((s) => s + 10);
          setStreak((st) => st + 1);
          setShowAnswer(true);
          voiceRef.current?.speakSingleKey('quiz_correct', 'Spot on! Magnificent observation.', -1);
        } else {
          setStreak(0);
          voiceRef.current?.speakSingleKey('quiz_wrong', 'Not quite right. Inspect the outfit closely and try again.', -1);
          setTimeout(() => setSelectedQuizIdx(null), 1300);
        }
      };

      const updateWardrobeSlot = (slotIdx, newClothing, newColor) => {
        setGameState((prev) => {
          const nextItems = [...prev.items];
          if (slotIdx < nextItems.length) {
            nextItems[slotIdx] = {
              clothing: newClothing || nextItems[slotIdx].clothing,
              color: newColor || nextItems[slotIdx].color,
            };
          }
          const updated = { ...prev, items: nextItems };
          const changed = nextItems[slotIdx];
          if (changed) {
            setInspectedItem(changed);
            if (autoSpeak) {
              const key = `pair_${changed.color.name}_${changed.clothing.name}`;
              voiceRef.current?.speakSingleKey(key, formatItemPhrase(changed), slotIdx);
            }
          }
          return updated;
        });
      };

      const pronoun = gameState.gender === 'he' ? 'He' : 'She';

      return (
        <div className="app-shell">
          {/* ==================== TOP COMMAND BAR (NO VERSION BADGE, HUGE SPIN BUTTON) ==================== */}
          <nav className="top-nav">
            <div style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>
              <div
                style={{
                  width: 38,
                  height: 38,
                  borderRadius: 11,
                  background: 'linear-gradient(135deg, #3B82F6, #6366F1)',
                  display: 'flex',
                  alignItems: 'center',
                  justifyContent: 'center',
                  fontWeight: 800,
                  fontSize: 20,
                }}
              >
                👔
              </div>
              <span style={{ fontFamily: 'var(--font-display)', fontWeight: 900, fontSize: '21px', letterSpacing: '-0.01em' }}>
                What Are They Wearing?
              </span>
            </div>

            {/* Mode Segmented Switcher */}
            <div
              style={{
                display: 'flex',
                background: 'rgba(15, 23, 42, 0.9)',
                padding: '4px',
                borderRadius: '12px',
                border: '1px solid rgba(255,255,255,0.12)',
                gap: '4px',
              }}
            >
              {[
                { id: 'reveal', label: '👁️ Reveal' },
                { id: 'quiz', label: '🎯 Quiz' },
                { id: 'wardrobe', label: '🧥 Wardrobe' },
              ].map((tab) => (
                <button
                  key={tab.id}
                  onClick={() => {
                    setMode(tab.id);
                    if (tab.id === 'wardrobe') setShowAnswer(true);
                  }}
                  className="btn-modern"
                  style={{
                    padding: '7px 15px',
                    borderRadius: '9px',
                    fontSize: '14px',
                    background: mode === tab.id ? '#2563EB' : 'transparent',
                    color: mode === tab.id ? '#FFFFFF' : '#94A3B8',
                  }}
                >
                  {tab.label}
                </button>
              ))}
            </div>

            {/* Voice Status + Oversized Spin Button */}
            <div style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>
              <button
                onClick={() => setAutoSpeak((v) => !v)}
                className="btn-modern"
                title="Toggle Commanding British Male Voice Auto-Narration"
                style={{
                  background: autoSpeak ? 'rgba(16, 185, 129, 0.18)' : 'rgba(255,255,255,0.08)',
                  border: `1.5px solid ${autoSpeak ? '#10B981' : 'rgba(255,255,255,0.15)'}`,
                  color: autoSpeak ? '#6EE7B7' : '#94A3B8',
                  padding: '7px 14px',
                  borderRadius: '999px',
                  fontSize: '13px',
                }}
              >
                <span>🇬🇧 {isSpeaking ? '🔊 Speaking...' : 'UK Baritone'}</span>
                <span className="hide-mobile" style={{ opacity: 0.85 }}>
                  ({autoSpeak ? 'Auto' : 'Manual'})
                </span>
              </button>

              <button
                onClick={() => spinRoulette()}
                disabled={isSpinning}
                className="btn-mega-spin"
                style={{
                  padding: '10px 26px',
                  fontSize: '18px',
                }}
              >
                <span>🎲 SPIN</span>
              </button>
            </div>
          </nav>

          {/* ==================== 2-COLUMN STUDIO WORKSPACE ==================== */}
          <div className="workspace-grid">
            {/* LEFT PANEL: REALISTIC PERSON + DYNAMIC CALLOUT ARROWS */}
            <section
              className="studio-card"
              style={{
                background: 'radial-gradient(circle at 50% 42%, #FFFFFF 0%, #F1F5F9 72%, #E2E8F0 100%)',
                justifyContent: 'space-between',
                padding: '14px',
              }}
            >
              {/* Stage Top Bar */}
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', zIndex: 10 }}>
                <div style={{ display: 'flex', gap: '4px', background: '#E2E8F0', padding: '4px', borderRadius: '10px' }}>
                  {[
                    { label: 'Random', val: null },
                    { label: 'He ♂', val: 'he' },
                    { label: 'She ♀', val: 'she' },
                  ].map((g) => (
                    <button
                      key={g.label}
                      onClick={() => spinRoulette(g.val)}
                      className="btn-modern"
                      style={{
                        padding: '5px 11px',
                        borderRadius: '7px',
                        fontSize: '12.5px',
                        background: g.val && gameState.gender === g.val ? '#FFFFFF' : 'transparent',
                        color: '#0F172A',
                        boxShadow: g.val && gameState.gender === g.val ? '0 1px 3px rgba(0,0,0,0.12)' : 'none',
                      }}
                    >
                      {g.label}
                    </button>
                  ))}
                </div>

                <button
                  onClick={() => {
                    const qKey = gameState.gender === 'he' ? 'q_he' : 'q_she';
                    voiceRef.current?.speakSingleKey(qKey, `What is ${gameState.gender} wearing?`, -1);
                  }}
                  className="btn-modern"
                  style={{
                    background: '#FFFFFF',
                    border: '1.5px solid #CBD5E1',
                    borderRadius: '999px',
                    padding: '6px 13px',
                    fontSize: '12.5px',
                    color: '#0F172A',
                  }}
                >
                  🔊 Ask Question
                </button>
              </div>

              {/* Realistic Human Figure + Dynamic Arrows Viewport */}
              <div
                onClick={() => spinRoulette()}
                title="Click person to spin, or click any garment to point an arrow and hear its British pronunciation"
                style={{
                  flex: 1,
                  minHeight: 0,
                  display: 'flex',
                  alignItems: 'center',
                  justifyContent: 'center',
                  cursor: isSpinning ? 'wait' : 'pointer',
                  padding: '2px 0',
                }}
              >
                <div
                  className={isSpinning ? 'person-spinning' : 'person-idle'}
                  style={{ width: '100%', height: '100%', maxHeight: '510px', aspectRatio: '530 / 650' }}
                >
                  <RealisticPerson
                    gender={gameState.gender}
                    skin={gameState.skin}
                    hair={gameState.hair}
                    eyeColor={gameState.eyeColor}
                    hairStyle={gameState.hairStyle}
                    items={gameState.items}
                    activeSegIdx={speakingSegIdx}
                    inspectedItem={inspectedItem}
                    showAllArrows={showAnswer}
                    onSelectItem={handleSelectGarment}
                  />
                </div>
              </div>

              {/* Giant Stage Spin Button Bar */}
              <div style={{ display: 'flex', gap: '10px', alignItems: 'center' }}>
                <button
                  onClick={() => spinRoulette()}
                  disabled={isSpinning}
                  className="btn-mega-spin"
                  style={{
                    width: '100%',
                    padding: '15px 24px',
                    fontSize: 'clamp(20px, 2.2vw, 26px)',
                  }}
                >
                  <span>🎲 SPIN NEW OUTFIT</span>
                </button>
              </div>
            </section>

            {/* RIGHT PANEL: QUESTION, MASSIVE SENTENCE & INTERACTIVE MODES */}
            <section className="studio-card" style={{ padding: '20px 26px', gap: '16px', justifyContent: 'space-between' }}>
              {/* 1. Question Header */}
              <div
                style={{
                  display: 'flex',
                  alignItems: 'center',
                  justifyContent: 'space-between',
                  background: '#F8FAFC',
                  border: '2px solid #E2E8F0',
                  borderRadius: '16px',
                  padding: '14px 22px',
                  gap: '14px',
                  flexWrap: 'wrap',
                }}
              >
                <h1
                  style={{
                    fontFamily: 'var(--font-display)',
                    fontSize: 'clamp(28px, 3.2vw, 42px)',
                    fontWeight: 900,
                    color: '#0F172A',
                    letterSpacing: '-0.02em',
                  }}
                >
                  What is{' '}
                  <span
                    style={{
                      color: gameState.gender === 'he' ? '#2563EB' : '#DB2777',
                      background: gameState.gender === 'he' ? '#DBEAFE' : '#FCE7F3',
                      padding: '2px 16px',
                      borderRadius: '999px',
                    }}
                  >
                    {gameState.gender}
                  </span>{' '}
                  wearing?
                </h1>

                <div style={{ display: 'flex', gap: '10px' }}>
                  <button
                    onClick={() => {
                      setShowAnswer(true);
                      setInspectedItem(null);
                      voiceRef.current?.speakOutfitSentence(gameState);
                    }}
                    className="btn-modern"
                    style={{
                      background: '#0F172A',
                      color: '#FFFFFF',
                      padding: '12px 20px',
                      borderRadius: '12px',
                      fontSize: '15px',
                    }}
                  >
                    <span>🔊 Speak Full Sentence</span>
                  </button>
                </div>
              </div>

              {/* 2. Dynamic Mode Body */}
              <div
                className="custom-scroll"
                style={{
                  flex: 1,
                  minHeight: 0,
                  overflowY: 'auto',
                  display: 'flex',
                  flexDirection: 'column',
                  justifyContent: 'center',
                }}
              >
                {mode === 'reveal' && (
                  <div style={{ display: 'flex', flexDirection: 'column', gap: '18px' }}>
                    {!showAnswer ? (
                      <div
                        style={{
                          background: 'linear-gradient(135deg, #EFF6FF 0%, #EEF2FF 100%)',
                          border: '3px dashed #93C5FD',
                          borderRadius: '20px',
                          padding: '44px 28px',
                          textAlign: 'center',
                          display: 'flex',
                          flexDirection: 'column',
                          alignItems: 'center',
                          gap: '20px',
                        }}
                      >
                        <p style={{ fontSize: 'clamp(18px, 2vw, 24px)', fontWeight: 700, color: '#334155', maxWidth: '540px' }}>
                          Look closely at the person on the left. Click any garment to point an arrow and hear it, or reveal the complete sentence!
                        </p>
                        <button
                          onClick={handleRevealAnswer}
                          disabled={isSpinning}
                          className="btn-modern"
                          style={{
                            background: 'linear-gradient(135deg, #2563EB 0%, #4F46E5 100%)',
                            color: '#FFFFFF',
                            padding: '20px 44px',
                            borderRadius: '18px',
                            fontSize: 'clamp(22px, 2.5vw, 30px)',
                            fontFamily: 'var(--font-display)',
                            fontWeight: 900,
                            boxShadow: '0 8px 0 #1E3A8A, 0 16px 32px rgba(37, 99, 235, 0.4)',
                          }}
                        >
                          👁️ REVEAL ANSWER
                        </button>
                      </div>
                    ) : (
                      <div className="fade-in-panel" style={{ display: 'flex', flexDirection: 'column', gap: '18px' }}>
                        {/* MASSIVE CLASSROOM-SIZED SENTENCE CARD */}
                        <div
                          style={{
                            background: '#FFFFFF',
                            border: '3px solid #CBD5E1',
                            borderRadius: '22px',
                            padding: '26px 30px',
                            boxShadow: '0 12px 30px -8px rgba(15, 23, 42, 0.08)',
                          }}
                        >
                          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '12px' }}>
                            <span style={{ fontSize: '13px', fontWeight: 800, color: '#059669', textTransform: 'uppercase', letterSpacing: '0.06em' }}>
                              ✓ Complete Sentence — Click Any Garment to Point Arrow & Hear
                            </span>
                            <button
                              onClick={() => {
                                setShowAnswer(false);
                                setInspectedItem(null);
                              }}
                              className="btn-modern"
                              style={{
                                background: '#F1F5F9',
                                color: '#475569',
                                padding: '6px 14px',
                                borderRadius: '8px',
                                fontSize: '13px',
                              }}
                            >
                              Hide Answer
                            </button>
                          </div>

                          <p
                            style={{
                              fontFamily: 'var(--font-display)',
                              fontSize: 'clamp(36px, 4.3vw, 60px)',
                              fontWeight: 900,
                              lineHeight: 1.35,
                              color: '#0F172A',
                              letterSpacing: '-0.015em',
                            }}
                          >
                            {pronoun} is wearing{' '}
                            {gameState.items.map((item, i) => {
                              const isCurrentSpoken =
                                speakingSegIdx === i ||
                                (inspectedItem && inspectedItem.clothing.name === item.clothing.name);
                              const suffix =
                                i < gameState.items.length - 2
                                  ? ', '
                                  : i === gameState.items.length - 2
                                  ? ', and '
                                  : '.';
                              return (
                                <React.Fragment key={`${item.clothing.name}-${i}`}>
                                  <span
                                    onClick={() => handleSelectGarment(item, i)}
                                    onMouseEnter={() => setInspectedItem(item)}
                                    style={{
                                      cursor: 'pointer',
                                      padding: '2px 10px',
                                      margin: '0 2px',
                                      borderRadius: '14px',
                                      background: isCurrentSpoken ? item.color.light : 'transparent',
                                      boxShadow: isCurrentSpoken ? `0 0 0 3px ${item.color.hex}` : 'none',
                                      transition: 'all 0.15s ease',
                                      display: 'inline',
                                    }}
                                  >
                                    {item.clothing.singular ? getArticle(item.color.name) : ''}
                                    <span style={{ color: item.color.hex, fontWeight: 900 }}>{item.color.name}</span>{' '}
                                    <span
                                      style={{
                                        fontWeight: 900,
                                        color: '#0F172A',
                                        borderBottom: `5px solid ${item.color.hex}`,
                                      }}
                                    >
                                      {item.clothing.name}
                                    </span>
                                  </span>
                                  <span>{suffix}</span>
                                </React.Fragment>
                              );
                            })}
                          </p>
                        </div>

                        {/* Large Interactive Garment Cards (Hover or Click to Point Arrow on Person) */}
                        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(210px, 1fr))', gap: '12px' }}>
                          {gameState.items.map((item, i) => {
                            const isHighlighted =
                              speakingSegIdx === i ||
                              (inspectedItem && inspectedItem.clothing.name === item.clothing.name);
                            return (
                              <button
                                key={`${item.clothing.name}-card-${i}`}
                                onClick={() => handleSelectGarment(item, i)}
                                onMouseEnter={() => setInspectedItem(item)}
                                className="btn-modern"
                                style={{
                                  background: item.color.light,
                                  border: `3px solid ${isHighlighted ? '#0F172A' : item.color.hex}`,
                                  borderRadius: '16px',
                                  padding: '15px 18px',
                                  justifyContent: 'space-between',
                                  textAlign: 'left',
                                  transform: isHighlighted ? 'translateY(-3px)' : 'none',
                                  boxShadow: isHighlighted ? '0 10px 20px rgba(0,0,0,0.12)' : 'none',
                                }}
                              >
                                <div style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>
                                  <span
                                    style={{
                                      width: 22,
                                      height: 22,
                                      borderRadius: '50%',
                                      background: item.color.hex,
                                      border: '2px solid rgba(0,0,0,0.2)',
                                      flexShrink: 0,
                                    }}
                                  />
                                  <div>
                                    <div style={{ fontSize: '11.5px', fontWeight: 800, color: '#475569', textTransform: 'uppercase' }}>
                                      {i === 0 ? '↖ Top / Main' : i === 1 && gameState.items.length === 3 ? '↙ Bottom' : '↗ Accessory'}
                                    </div>
                                    <div style={{ fontSize: 'clamp(17px, 1.6vw, 21px)', fontWeight: 900, color: '#0F172A' }}>
                                      {formatItemPhrase(item)}
                                    </div>
                                  </div>
                                </div>
                                <span style={{ fontSize: '20px' }}>🔊</span>
                              </button>
                            );
                          })}
                        </div>
                      </div>
                    )}
                  </div>
                )}

                {mode === 'quiz' && (
                  <div className="fade-in-panel" style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
                    <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '4px' }}>
                      <span style={{ fontSize: '16px', fontWeight: 800, color: '#334155' }}>
                        Select the exact sentence matching the outfit:
                      </span>
                      <div style={{ display: 'flex', gap: '8px' }}>
                        <span style={{ background: '#FEF3C7', color: '#92400E', padding: '5px 12px', borderRadius: '999px', fontSize: '14px', fontWeight: 800 }}>
                          🔥 Streak: {streak}
                        </span>
                        <span style={{ background: '#DBEAFE', color: '#1E40AF', padding: '5px 12px', borderRadius: '999px', fontSize: '14px', fontWeight: 800 }}>
                          ⭐ Score: {score}
                        </span>
                      </div>
                    </div>

                    {quizOptions.map((opt, idx) => {
                      const isPicked = selectedQuizIdx === idx;
                      const bg = isPicked ? (opt.isCorrect ? '#DCFCE7' : '#FEE2E2') : '#F8FAFC';
                      const border = isPicked ? (opt.isCorrect ? '#16A34A' : '#DC2626') : '#CBD5E1';

                      return (
                        <button
                          key={idx}
                          onClick={() => handleQuizPick(opt, idx)}
                          className="btn-modern"
                          style={{
                            width: '100%',
                            textAlign: 'left',
                            justifyContent: 'space-between',
                            padding: '16px 20px',
                            borderRadius: '14px',
                            background: bg,
                            border: `2.5px solid ${border}`,
                            color: '#0F172A',
                            fontSize: 'clamp(17px, 1.8vw, 22px)',
                            fontWeight: 800,
                          }}
                        >
                          <span>{opt.text}</span>
                          {isPicked && <span style={{ fontSize: '22px' }}>{opt.isCorrect ? '✅' : '❌'}</span>}
                        </button>
                      );
                    })}
                  </div>
                )}

                {mode === 'wardrobe' && (
                  <div className="fade-in-panel" style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
                    {gameState.items.map((slot, slotIdx) => {
                      const pool =
                        slotIdx === 0
                          ? clothingPools.tops
                          : slotIdx === 1 && gameState.items.length === 3
                          ? clothingPools.bottoms
                          : clothingPools.accessories;
                      const label =
                        slotIdx === 0 ? 'Top / Outfit' : slotIdx === 1 && gameState.items.length === 3 ? 'Bottoms' : 'Accessory';

                      return (
                        <div
                          key={slotIdx}
                          style={{
                            background: '#F8FAFC',
                            border: '1.5px solid #CBD5E1',
                            borderRadius: '14px',
                            padding: '12px 16px',
                            display: 'flex',
                            flexDirection: 'column',
                            gap: '8px',
                          }}
                        >
                          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: '8px' }}>
                            <span style={{ fontSize: '14px', fontWeight: 800, color: '#334155', textTransform: 'uppercase' }}>
                              {label}: <strong style={{ color: slot.color.hex, fontSize: '16px' }}>{formatItemPhrase(slot)}</strong>
                            </span>
                            <div style={{ display: 'flex', gap: '6px' }}>
                              {colors.map((c) => (
                                <button
                                  key={c.name}
                                  onClick={() => updateWardrobeSlot(slotIdx, null, c)}
                                  title={c.name}
                                  style={{
                                    width: 22,
                                    height: 22,
                                    borderRadius: '50%',
                                    background: c.hex,
                                    border: slot.color.name === c.name ? '3px solid #0F172A' : '1px solid rgba(0,0,0,0.2)',
                                    cursor: 'pointer',
                                  }}
                                />
                              ))}
                            </div>
                          </div>

                          <div style={{ display: 'flex', gap: '6px', flexWrap: 'wrap' }}>
                            {pool.map((g) => (
                              <button
                                key={g.name}
                                onClick={() => updateWardrobeSlot(slotIdx, g, null)}
                                className="btn-modern"
                                style={{
                                  padding: '6px 13px',
                                  borderRadius: '999px',
                                  fontSize: '13.5px',
                                  background: slot.clothing.name === g.name ? '#0F172A' : '#FFFFFF',
                                  color: slot.clothing.name === g.name ? '#FFFFFF' : '#334155',
                                  border: '1.5px solid #CBD5E1',
                                }}
                              >
                                {g.name}
                              </button>
                            ))}
                          </div>
                        </div>
                      );
                    })}
                  </div>
                )}
              </div>

              {/* 3. Bottom Action Footer */}
              <div
                style={{
                  display: 'flex',
                  alignItems: 'center',
                  justifyContent: 'space-between',
                  paddingTop: '12px',
                  borderTop: '1.5px solid #E2E8F0',
                  gap: '12px',
                }}
              >
                <div style={{ fontSize: '13px', color: '#64748B', fontWeight: 700 }}>
                  <kbd style={{ background: '#F1F5F9', padding: '3px 8px', borderRadius: '6px', border: '1px solid #CBD5E1' }}>Space</kbd> Spin Outfit •{' '}
                  <kbd style={{ background: '#F1F5F9', padding: '3px 8px', borderRadius: '6px', border: '1px solid #CBD5E1' }}>Enter</kbd> Reveal & Speak
                </div>

                <button
                  onClick={() => spinRoulette()}
                  disabled={isSpinning}
                  className="btn-mega-spin"
                  style={{
                    padding: '12px 30px',
                    fontSize: '18px',
                  }}
                >
                  🎲 SPIN AGAIN
                </button>
              </div>
            </section>
          </div>
        </div>
      );
    }

    const root = ReactDOM.createRoot(document.getElementById('root'));
    root.render(<App />);
  </script>
</body>
</html>