// Live Calls · 8 scenarios. Editorial showcase + featured live player
// One auto-cycling featured panel, then a gallery of all eight calls.
// Adapted for the GuestMaker voice page:
//   - Forced dark theme, no Tweaks panel.
//   - Brand fuchsia #ED4D86 in LIGHT (not used at runtime; dark forced).
//   - Audio playback via AudioContext, master clock for visual reveal so
//     transcript stays in sync with speech.
//   - Single instance per page = naturally only one conversation plays.

const { useState: useStateLC, useEffect: useEffectLC, useRef: useRefLC, useMemo: useMemoLC } = React;

const LC_AUDIO_BASE = "/marketing/voice-calls";
// Per-call turn durations (seconds) measured from generated WAVs.
const LC_DUR = {
  continuation: [3.44, 9.85, 4.14, 5.95, 2.00],
  match:        [6.00, 9.76, 3.35, 11.11, 3.54],
  kb:           [4.09, 8.41, 2.19, 6.79, 3.03, 6.97],
  tasks:        [3.82, 6.79, 3.68, 11.57, 3.58, 5.81],
  group:        [7.02, 7.76, 9.76, 2.56, 3.86],
  loyalty:      [3.26, 6.51, 0.94, 9.90],
  disruption:   [3.77, 11.20, 6.56, 1.45],
  itinerary:    [4.37, 9.81, 6.93, 4.33, 5.67],
};
const LC_TURN_GAP = 0.35;
const LC_PRE_GAP  = 0.6;
const LC_POST_GAP = 1.4;

// ---- theme tokens (light + dark) ----
const LIGHT = {
  paper: "#fbfaf6",
  cream: "#f6f4ef",
  surface: "#ffffff",
  ink: "#1a1a1a",
  inkSoft: "#322c27",
  muted: "#6b6358",
  faint: "#a99fa3",
  rule: "rgba(0,0,0,0.10)",
  ruleSoft: "rgba(0,0,0,0.06)",
  fuchsia: "#ED4D86",
  fuchsiaDeep: "#B8336A",
  fuchsiaSoft: "rgba(237,77,134,0.10)",
  fuchsiaBorder: "rgba(237,77,134,0.32)",
  gold: "#C9A24A",
  sage: "#7A8E6A",
  warn: "#8a6d24",
  warnSoft: "rgba(201,162,74,0.16)",
  onInk: "#ffffff",
  onFuchsia: "#ffffff",
  onGold: "#ffffff",
};
const DARK = {
  paper: "#16130f",
  cream: "#0a0807",
  surface: "#1f1a16",
  ink: "#f4ecd9",
  inkSoft: "#e2d6bd",
  muted: "#9c8f7c",
  faint: "#6e6358",
  rule: "rgba(255,236,205,0.12)",
  ruleSoft: "rgba(255,236,205,0.06)",
  fuchsia: "#ED4D86",
  fuchsiaDeep: "#F58FB7",
  fuchsiaSoft: "rgba(237,77,134,0.16)",
  fuchsiaBorder: "rgba(237,77,134,0.40)",
  gold: "#E0BB5E",
  sage: "#A6BC8E",
  warn: "#E0BB5E",
  warnSoft: "rgba(224,187,94,0.18)",
  onInk: "#0a0807",
  onFuchsia: "#1a0a13",
  onGold: "#1a1408",
};
// mutable theme object. Components read from this at render
const T = { ...LIGHT };
function applyTheme(dark) {
  Object.assign(T, dark ? DARK : LIGHT);
}
// ---- (palette migrated to LIGHT/DARK/T above) ----

// =============================================================
//  CALL DATA · 8 scenarios
// =============================================================
const CALLS = [
  // 1. Returning guest, continuation
  {
    id: "continuation",
    no: "01",
    tag: "RETURNING GUEST · CONTINUATION",
    title: "Picks up where she left off.",
    caller: { name: "Daniel Marchetti", city: "Milan, IT", loyalty: "OLEA CIRCLE · GOLD", initials: "DM" },
    duration: "02:14",
    outcome: { label: "BOOKING CONTINUED", tone: "ok" },
    panelKind: "continuation",
    dialog: [
      { who: "agent", text: "Welcome back, Daniel. It's lovely to hear from you again." },
      { who: "agent", text: "Last Thursday we were looking at the Aphrodite Suite at Olea Cliffside, May 16 to 18. Shall we pick up there or start fresh?" },
      { who: "guest", text: "Pick up from there. My partner liked the Cliff Suite better, actually." },
      { who: "agent", text: "Switching to the Cliff Suite. Same dates, two adults. Holding it now." },
      { who: "guest", text: "Perfect. Let's confirm." },
    ],
    panel: {
      lastSession: { when: "Thu · 4 days ago", durationMin: 11 },
      hotel: "Olea Cliffside · Tenerife",
      from: { suite: "Aphrodite Suite", rate: "€1,180/night" },
      to:   { suite: "Cliff Suite",     rate: "€820/night"   },
      dates: "May 16 to 18, 2026",
      guests: "2 adults",
    },
  },

  // 2. Greek Islands match
  {
    id: "match",
    no: "02",
    tag: "PORTFOLIO MATCH · GREEK ISLANDS",
    title: "Thirty-four hotels, narrowed to three.",
    caller: { name: "Marisa Whitaker", city: "London, UK", loyalty: "FIRST-TIME GUEST", initials: "MW" },
    duration: "06:38",
    outcome: { label: "3 PROPERTIES SHORTLISTED", tone: "ok" },
    panelKind: "match",
    dialog: [
      { who: "guest", text: "I'm planning a Greek-islands trip with my husband. We love quiet, no kids around, maybe seven nights." },
      { who: "agent", text: "Lovely. We have thirty-four hotels across the Cyclades, Dodecanese and Ionian. Adult-only narrows it to seventeen." },
      { who: "guest", text: "We want sea views, real food, and a proper hammam." },
      { who: "agent", text: "That brings us to three: Olea Folegandros, Olea Patmos, and Aurea Symi. Folegandros has the strongest culinary program. Michelin-recommended." },
      { who: "guest", text: "Folegandros sounds wonderful. Tell me more about Patmos too." },
    ],
    panel: {
      funnel: [
        { label: "Greek-island portfolio",      n: 34 },
        { label: "Adult-only filter applied",   n: 17 },
        { label: "Sea view + hammam + dining",  n: 7  },
        { label: "Top match score ≥ 0.92",      n: 3  },
      ],
      shortlist: [
        { name: "Olea Folegandros", island: "Folegandros · Cyclades", score: 0.96, note: "Michelin-recommended kitchen" },
        { name: "Olea Patmos",      island: "Patmos · Dodecanese",    score: 0.93, note: "Cliffside hammam, monastery views" },
        { name: "Aurea Symi",       island: "Symi · Dodecanese",      score: 0.92, note: "Most secluded of the three" },
      ],
    },
  },

  // 3. Hotel KB Q&A
  {
    id: "kb",
    no: "03",
    tag: "KNOWLEDGE BASE · HOTEL Q&A",
    title: "Six fast answers, six sources cited.",
    caller: { name: "Jonas Berg", city: "Stockholm, SE", loyalty: "OLEA CIRCLE · SILVER", initials: "JB" },
    duration: "04:21",
    outcome: { label: "6 ANSWERS · BOOKING NUDGE SENT", tone: "ok" },
    panelKind: "kb",
    dialog: [
      { who: "guest", text: "A few questions before I book Olea Cliffside. Vegan options at the restaurants?" },
      { who: "agent", text: "Yes. Verde serves a full vegan tasting, eight courses, refreshed seasonally. €145 per person." },
      { who: "guest", text: "Parking on site? Cost?" },
      { who: "agent", text: "Valet €28 a night, self-park €18. Both bays are EV-ready." },
      { who: "guest", text: "Are dogs welcome? And is the pool heated in November?" },
      { who: "agent", text: "Dogs under 10 kilos, €40 a night. Pool is heated to 28°C through November." },
    ],
    panel: {
      qa: [
        { q: "Vegan options at the restaurants?",      src: "verde_menu_q2.pdf",        conf: 0.96, label: "F&B" },
        { q: "Parking on site? Cost?",                  src: "parking_policy_2026.pdf",  conf: 0.99, label: "FACILITIES" },
        { q: "Are dogs welcome?",                       src: "pet_policy_v3.pdf",        conf: 0.95, label: "POLICIES" },
        { q: "Pool heated year-round?",                 src: "pool_ops_winter.pdf",      conf: 0.93, label: "FACILITIES" },
        { q: "Late check-out fee?",                     src: "checkout_tiers.pdf",       conf: 0.97, label: "POLICIES" },
        { q: "Airport transfer included?",              src: "transfers_q2.pdf",         conf: 0.91, label: "TRANSPORT" },
      ],
    },
  },

  // 4. Special requests
  {
    id: "tasks",
    no: "04",
    tag: "SPECIAL REQUESTS · ARRIVAL & CELEBRATION",
    title: "A cot, a cake, a candlelit room.",
    caller: { name: "Priya & Ravi Krishnan", city: "Bangalore, IN", loyalty: "OLEA CIRCLE · GOLD", initials: "PR" },
    duration: "03:47",
    outcome: { label: "2 TASKS CREATED · CONFIRMED", tone: "ok" },
    panelKind: "tasks",
    dialog: [
      { who: "guest", text: "We arrive Friday with our 11-month-old. We'd need a cot in the room please." },
      { who: "agent", text: "Of course. Logging a cot for room 412, set up before your 16:00 check-in." },
      { who: "guest", text: "And it's my husband's birthday on Saturday. Anything you can arrange?" },
      { who: "agent", text: "We can dress the room with rose petals, chill a bottle of Veuve, and bake a custom cake. Three options: chocolate praline, vanilla & rose, or a vegan citrus tart." },
      { who: "guest", text: "Vanilla and rose, please. Can we have it brought up at 7 PM?" },
      { who: "agent", text: "Done. I'll have the team there at 18:55, dressed before you walk in." },
    ],
    panel: {
      property: "Olea Cliffside · Tenerife · Room 412",
      tasks: [
        { team: "HOUSEKEEPING", title: "Set up infant cot", when: "Fri · before 16:00", priority: "Standard", icon: "cot" },
        { team: "F&B · GUEST EXPERIENCE", title: "Birthday turndown · vanilla & rose cake, Veuve, rose petals", when: "Sat · 18:55", priority: "VIP", icon: "cake" },
      ],
      pms: { synced: true, ref: "OLE-2026-44188" },
    },
  },

  // 5. Group booking escalation
  {
    id: "group",
    no: "05",
    tag: "GROUP BOOKING · HUMAN HANDOFF",
    title: "Eighty rooms, handed to the Group team.",
    caller: { name: "Hannah Reyes", city: "Singapore, SG", loyalty: "PULSE CAPITAL · CORPORATE", initials: "HR" },
    duration: "01:52",
    outcome: { label: "ESCALATED · CALLBACK < 2 H", tone: "warn" },
    panelKind: "handoff",
    dialog: [
      { who: "guest", text: "I'm organizing our annual offsite. We're looking at seventy-five to eighty-five rooms, three nights in late October, ideally Sicily." },
      { who: "agent", text: "Thank you, Hannah. Anything over twenty rooms goes through our Group team. They'll structure pricing and meeting space properly for you." },
      { who: "agent", text: "I have your details and your preference for Sicily. Marco from Group Sales will call you within two hours, today between 14:00 and 16:00 SGT." },
      { who: "guest", text: "Perfect. I'll have my COO on the line as well." },
      { who: "agent", text: "Noted. Marco will dial in expecting two attendees." },
    ],
    panel: {
      reason: "Group size > 20 rooms · requires negotiated rate + meeting space",
      brief: {
        rooms: "75 to 85",
        nights: 3,
        when: "Late October 2026",
        region: "Sicily preferred",
        meeting: "Boardroom · 80-cap plenary · 4 breakouts",
        attendees: "2 (incl. COO)",
      },
      handoff: {
        team: "GROUP SALES · EMEA",
        owner: "Marco Lombardi",
        slot: "Today · 14:00 to 16:00 SGT",
        ticket: "GS-26-0944",
      },
    },
  },

  // 6. Loyalty redemption
  {
    id: "loyalty",
    no: "06",
    tag: "LOYALTY · POINTS REDEMPTION",
    title: "Twelve thousand points, one suite upgrade.",
    caller: { name: "Aiko Tanaka", city: "Tokyo, JP", loyalty: "OLEA CIRCLE · PLATINUM", initials: "AT" },
    duration: "02:55",
    outcome: { label: "UPGRADE APPLIED", tone: "ok" },
    panelKind: "loyalty",
    dialog: [
      { who: "guest", text: "I'd like to use my points for an upgrade on our August stay, please." },
      { who: "agent", text: "You have eighteen thousand four hundred points. Sea View to Cliff Suite is twelve thousand for the seven-night window." },
      { who: "guest", text: "Let's do it." },
      { who: "agent", text: "Applied. Your balance is six thousand four hundred. Stay upgraded August 12 to 19. Confirmation in your inbox in a moment." },
    ],
    panel: {
      tier: "PLATINUM",
      before: 18400,
      cost: 12000,
      after: 6400,
      stay: { hotel: "Olea Cliffside · Tenerife", from: "Aug 12", to: "Aug 19", room_from: "Sea View", room_to: "Cliff Suite" },
    },
  },

  // 7. Travel disruption
  {
    id: "disruption",
    no: "07",
    tag: "TRAVEL DISRUPTION · RESCUE",
    title: "Flight cancelled. Stay saved.",
    caller: { name: "Mateo Álvarez", city: "Madrid, ES", loyalty: "OLEA CIRCLE · SILVER", initials: "MA" },
    duration: "02:18",
    outcome: { label: "DATES SHIFTED · ROOM HELD", tone: "ok" },
    panelKind: "disruption",
    dialog: [
      { who: "guest", text: "My Iberia flight tomorrow was just cancelled. I'm rebooked for Saturday morning." },
      { who: "agent", text: "I'm sorry to hear that. Let me move things for you. Holding your room one extra night, no charge. That's our flexibility guarantee on direct bookings." },
      { who: "agent", text: "Your airport transfer is rebooked for Saturday at 11:40, meeting you at arrivals with a name card." },
      { who: "guest", text: "You're a lifesaver, thank you." },
    ],
    panel: {
      policyHit: "Direct-Book Flexibility · 1 free reschedule",
      changes: [
        { kind: "stay",     from: "Fri 8 May → Mon 11 May", to: "Sat 9 May → Tue 12 May", note: "1 night gifted" },
        { kind: "transfer", from: "Fri · IB6253 · 09:15",   to: "Sat · IB6841 · 11:40",   note: "Sedan · driver Costa" },
      ],
      pms: "PMS · synced 12s ago",
    },
  },

  // 8. Honeymoon multi-stay
  {
    id: "itinerary",
    no: "08",
    tag: "MULTI-STAY · HONEYMOON",
    title: "Santorini, then Crete, joined by speedboat.",
    caller: { name: "Lina & Sébastien Cole", city: "Paris, FR", loyalty: "FIRST-TIME GUESTS", initials: "LC" },
    duration: "08:12",
    outcome: { label: "ITINERARY DRAFTED · EMAIL SENT", tone: "ok" },
    panelKind: "itinerary",
    dialog: [
      { who: "guest", text: "We're getting married in June. I'd love a honeymoon split between Santorini and Crete." },
      { who: "agent", text: "Beautiful. I'd suggest four nights at Olea Caldera in Santorini for the first half, then four at Olea Elounda on the calmer coast of Crete." },
      { who: "agent", text: "I can coordinate the inter-island transfer: a private speedboat or the Hellenic ferry, whichever you prefer." },
      { who: "guest", text: "Speedboat please. And could one room have a private plunge pool?" },
      { who: "agent", text: "The Caldera Cave Suite has one carved into the rock. I'll hold it for you." },
    ],
    panel: {
      legs: [
        { hotel: "Olea Caldera",  city: "Santorini", from: "Jun 14", to: "Jun 18", suite: "Caldera Cave Suite · plunge pool", nights: 4 },
        { hotel: "Olea Elounda",  city: "Crete",     from: "Jun 18", to: "Jun 22", suite: "Sea Garden Villa",                  nights: 4 },
      ],
      transfer: { mode: "Private speedboat", duration: "1 h 50 min", note: "Skipper: Captain Stelios" },
    },
  },
];

// =============================================================
//  ATOMS
// =============================================================
function MonoLabel({ children, color = T.muted, size = 10.5 }) {
  return (
    <span style={{
      fontFamily: "'JetBrains Mono', monospace",
      fontSize: size, color, letterSpacing: "0.10em", fontWeight: 600,
    }}>{children}</span>
  );
}

function Avatar({ initials, size = 44, ring = false }) {
  return (
    <div style={{
      width: size, height: size, borderRadius: "50%",
      background: T.ink, color: T.onInk,
      display: "flex", alignItems: "center", justifyContent: "center",
      fontFamily: "'Instrument Serif', 'Source Serif 4', serif",
      // 400: Instrument Serif ships no other cut, so 500 was the browser
      // faking it. The template literal in boxShadow below is why the
      // sweep's brace matcher skipped this one.
      fontSize: size * 0.42, fontWeight: 400, letterSpacing: "0.02em",
      flexShrink: 0,
      boxShadow: ring ? `0 0 0 4px ${T.fuchsiaSoft}` : "none",
    }}>{initials}</div>
  );
}

function OutcomeStamp({ outcome }) {
  const ok = outcome.tone !== "warn";
  return (
    <div style={{
      display: "inline-flex", alignItems: "center", gap: 8,
      padding: "8px 14px",
      borderRadius: 999,
      background: ok ? "rgba(122,142,106,0.18)" : T.warnSoft,
      color: ok ? T.sage : T.warn,
      fontFamily: "'JetBrains Mono', monospace",
      fontSize: 10.5, fontWeight: 700, letterSpacing: "0.10em",
    }}>
      {ok ? (
        <svg width="11" height="11" viewBox="0 0 24 24" fill="none"
          stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round">
          <path d="M5 12l5 5L20 7" />
        </svg>
      ) : (
        <svg width="11" height="11" viewBox="0 0 24 24" fill="none"
          stroke="currentColor" strokeWidth="2.6" strokeLinecap="round" strokeLinejoin="round">
          <path d="M5 12h12M13 6l6 6-6 6" />
        </svg>
      )}
      {outcome.label}
    </div>
  );
}

function PortIcon({ kind, size = 14, color = T.ink }) {
  const stroke = color;
  const sw = 1.7;
  const props = { width: size, height: size, viewBox: "0 0 24 24", fill: "none",
    stroke, strokeWidth: sw, strokeLinecap: "round", strokeLinejoin: "round" };
  switch (kind) {
    case "phone": return (<svg {...props}><path d="M22 16.9v3a2 2 0 0 1-2.2 2 19.8 19.8 0 0 1-8.6-3.1 19.5 19.5 0 0 1-6-6 19.8 19.8 0 0 1-3.1-8.7A2 2 0 0 1 4.1 2h3a2 2 0 0 1 2 1.7 12.8 12.8 0 0 0 .7 2.8 2 2 0 0 1-.4 2.1L8 9.9a16 16 0 0 0 6 6l1.3-1.3a2 2 0 0 1 2.1-.4 12.8 12.8 0 0 0 2.8.7 2 2 0 0 1 1.7 2z"/></svg>);
    case "doc":   return (<svg {...props}><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/><path d="M9 14h6M9 17h4"/></svg>);
    case "users": return (<svg {...props}><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.9"/><path d="M16 3.1a4 4 0 0 1 0 7.8"/></svg>);
    case "spark": return (<svg {...props}><path d="M12 2l2.4 5.4L20 8.2l-4 3.9.9 5.6L12 15l-4.9 2.7.9-5.6-4-3.9 5.6-.8z"/></svg>);
    case "clock": return (<svg {...props}><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></svg>);
    case "arrow": return (<svg {...props}><path d="M5 12h14M13 6l6 6-6 6"/></svg>);
    case "pin":   return (<svg {...props}><path d="M21 10c0 7-9 13-9 13S3 17 3 10a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/></svg>);
    case "memory":return (<svg {...props}><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/><path d="M3 12a9 9 0 0 0 9 9"/></svg>);
    case "boat":  return (<svg {...props}><path d="M3 18c2 1 3 1 5 0s3-1 5 0 3 1 5 0M5 14l1-6h12l1 6"/><path d="M12 4v4"/></svg>);
    case "plane": return (<svg {...props}><path d="M2 12l8-2 5-7 2 1-3 7 7 2-1 2-7-1-2 5h-2l-1-4-3-1z"/></svg>);
    case "cake":  return (<svg {...props}><path d="M3 21h18M5 21V11a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v10"/><path d="M3 15h18M9 9V6M12 9V6M15 9V6"/></svg>);
    case "cot":   return (<svg {...props}><rect x="3" y="9" width="18" height="9" rx="2"/><path d="M3 13h18M7 9V5h10v4"/></svg>);
    case "key":   return (<svg {...props}><circle cx="8" cy="14" r="4"/><path d="M11 11l9-9M16 7l3 3M14 9l3 3"/></svg>);
    case "diamond": return (<svg {...props}><path d="M6 3h12l3 5-9 13L3 8z"/><path d="M3 8h18M9 3l-3 5 6 13M15 3l3 5-6 13"/></svg>);
    default: return null;
  }
}

// =============================================================
//  RIGHT-PANEL RENDERERS · one per call type
// =============================================================
function RightPanel({ kind, data, t }) {
  switch (kind) {
    case "continuation": return <ContinuationPanel d={data} t={t} />;
    case "match":        return <MatchPanel d={data} t={t} />;
    case "kb":           return <KbPanel d={data} t={t} />;
    case "tasks":        return <TaskPanel d={data} t={t} />;
    case "handoff":      return <HandoffPanel d={data} t={t} />;
    case "loyalty":      return <LoyaltyPanel d={data} t={t} />;
    case "disruption":   return <DisruptionPanel d={data} t={t} />;
    case "itinerary":    return <ItineraryPanel d={data} t={t} />;
    default: return null;
  }
}

// 1 · CONTINUATION
function ContinuationPanel({ d, t }) {
  return (
    <PanelShell title="Memory recall">
      <div style={{
        padding: "12px 14px", borderRadius: 12,
        background: T.surface, border: `1px solid ${T.rule}`,
        marginBottom: 14,
      }}>
        <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 8 }}>
          <PortIcon kind="memory" size={13} color={T.fuchsia} />
          <MonoLabel color={T.fuchsia}>LAST SESSION · {d.lastSession.when.toUpperCase()}</MonoLabel>
        </div>
        <div style={{ fontFamily: "'Instrument Serif', 'Source Serif 4', serif", fontSize: 17, color: T.ink, marginBottom: 4 }}>
          {d.hotel}
        </div>
        <div style={{ fontSize: 12, color: T.muted }}>
          Session lasted {d.lastSession.durationMin} min · viewed Aphrodite Suite
        </div>
      </div>

      <MonoLabel>SWITCHED TO</MonoLabel>
      <div style={{ marginTop: 10, display: "flex", alignItems: "stretch", gap: 0,
        border: `1px solid ${T.rule}`, borderRadius: 12, overflow: "hidden", background: T.surface }}>
        <div style={{ flex: 1, padding: "12px 14px", opacity: 0.55 }}>
          <MonoLabel size={9.5}>FROM</MonoLabel>
          <div style={{ fontFamily: "'Instrument Serif', 'Source Serif 4', serif", fontSize: 15, color: T.ink, marginTop: 4,
            textDecoration: "line-through", textDecorationColor: T.muted }}>{d.from.suite}</div>
          <div style={{ fontSize: 11, color: T.muted }}>{d.from.rate}</div>
        </div>
        <div style={{ width: 32, display: "flex", alignItems: "center", justifyContent: "center",
          background: T.paper, borderLeft: `1px solid ${T.rule}`, borderRight: `1px solid ${T.rule}` }}>
          <PortIcon kind="arrow" size={14} color={T.fuchsia} />
        </div>
        <div style={{ flex: 1, padding: "12px 14px", background: T.fuchsiaSoft }}>
          <MonoLabel size={9.5} color={T.fuchsia}>TO</MonoLabel>
          <div style={{ fontFamily: "'Instrument Serif', 'Source Serif 4', serif", fontSize: 15, color: T.ink, marginTop: 4 }}>{d.to.suite}</div>
          <div style={{ fontSize: 11, color: T.fuchsiaDeep, fontWeight: 600 }}>{d.to.rate}</div>
        </div>
      </div>

      <div style={{ marginTop: 14, display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
        <PanelStat label="DATES" value={d.dates} />
        <PanelStat label="GUESTS" value={d.guests} />
      </div>

      <div style={{ marginTop: 14, padding: "10px 12px", borderRadius: 10,
        background: T.fuchsiaSoft, display: "flex", alignItems: "center", gap: 8 }}>
        <span style={{ width: 7, height: 7, borderRadius: "50%", background: T.fuchsia,
          animation: "lc-pulse 1.4s ease-in-out infinite" }} />
        <span style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: 10.5,
          color: T.fuchsiaDeep, fontWeight: 700, letterSpacing: "0.08em" }}>
          ROOM ON HOLD · 30 MIN
        </span>
      </div>
    </PanelShell>
  );
}

// 2 · MATCH (funnel + shortlist)
function MatchPanel({ d, t }) {
  const max = d.funnel[0].n;
  return (
    <PanelShell title="Portfolio search · funnel">
      <div style={{ display: "flex", flexDirection: "column", gap: 8, marginBottom: 18 }}>
        {d.funnel.map((f, i) => {
          const w = (f.n / max) * 100;
          const isLast = i === d.funnel.length - 1;
          return (
            <div key={i} style={{ display: "flex", alignItems: "center", gap: 10 }}>
              <div style={{ width: 18, fontFamily: "'JetBrains Mono', monospace",
                fontSize: 10, color: T.faint }}>{i+1}</div>
              <div style={{ flex: 1 }}>
                <div style={{ fontFamily: "Inter", fontSize: 12.5, color: T.ink, marginBottom: 4 }}>
                  {f.label}
                </div>
                <div style={{ height: 6, borderRadius: 3, background: T.paper,
                  border: `1px solid ${T.ruleSoft}`, overflow: "hidden" }}>
                  <div style={{
                    width: `${w}%`, height: "100%",
                    background: isLast ? T.fuchsia : T.ink,
                    transition: "width 1s cubic-bezier(.2,.8,.2,1)",
                  }} />
                </div>
              </div>
              <div style={{ minWidth: 36, textAlign: "right",
                fontFamily: "'Instrument Serif', 'Source Serif 4', serif",
                fontSize: 18, color: isLast ? T.fuchsia : T.ink,
                fontWeight: 400,
              }}>{f.n}</div>
            </div>
          );
        })}
      </div>

      <MonoLabel>SHORTLIST</MonoLabel>
      <div style={{ marginTop: 10, display: "flex", flexDirection: "column", gap: 8 }}>
        {d.shortlist.map((s, i) => (
          <div key={i} style={{
            display: "flex", alignItems: "center", gap: 12,
            padding: "10px 12px", borderRadius: 10,
            background: i === 0 ? T.fuchsiaSoft : T.surface,
            border: `1px solid ${i === 0 ? T.fuchsiaBorder : T.rule}`,
          }}>
            <PlaceholderImg w={44} h={44} radius={8} />
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ fontFamily: "'Instrument Serif', 'Source Serif 4', serif", fontSize: 14, color: T.ink,
                fontWeight: 400 }}>{s.name}</div>
              <div style={{ fontSize: 11, color: T.muted, marginTop: 1 }}>{s.island}</div>
              <div style={{ fontSize: 11, color: T.inkSoft, marginTop: 3, fontStyle: "italic" }}>
                "{s.note}"
              </div>
            </div>
            <div style={{ textAlign: "right" }}>
              <div style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: 9.5,
                color: T.muted, letterSpacing: "0.06em" }}>MATCH</div>
              <div style={{ fontFamily: "'Instrument Serif', 'Source Serif 4', serif", fontSize: 16,
                color: i === 0 ? T.fuchsia : T.ink, fontWeight: 400, marginTop: 2 }}>
                {s.score.toFixed(2)}
              </div>
            </div>
          </div>
        ))}
      </div>
    </PanelShell>
  );
}

// 3 · KB
function KbPanel({ d, t }) {
  return (
    <PanelShell title="Answers · with sources">
      <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
        {d.qa.map((row, i) => (
          <div key={i} style={{
            display: "grid", gridTemplateColumns: "20px 1fr auto",
            gap: 10, alignItems: "start",
            padding: "10px 12px", borderRadius: 10,
            background: T.surface, border: `1px solid ${T.rule}`,
          }}>
            <div style={{ fontFamily: "'JetBrains Mono', monospace",
              fontSize: 10, color: T.faint, marginTop: 2 }}>0{i+1}</div>
            <div style={{ minWidth: 0 }}>
              <div style={{ fontFamily: "Inter", fontSize: 12.5, color: T.ink, marginBottom: 4 }}>
                {row.q}
              </div>
              <div style={{ display: "flex", alignItems: "center", gap: 6 }}>
                <PortIcon kind="doc" size={11} color={T.fuchsia} />
                <span style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: 9.5,
                  color: T.fuchsia, letterSpacing: "0.06em" }}>{row.src}</span>
                <span style={{ width: 3, height: 3, borderRadius: "50%", background: T.faint }} />
                <MonoLabel size={9.5}>{row.label}</MonoLabel>
              </div>
            </div>
            <div style={{ textAlign: "right" }}>
              <ConfidenceDial value={row.conf} />
            </div>
          </div>
        ))}
      </div>
      <div style={{ marginTop: 14, padding: "10px 12px", borderRadius: 10,
        background: T.fuchsiaSoft, display: "flex", alignItems: "center", gap: 10 }}>
        <PortIcon kind="spark" size={14} color={T.fuchsiaDeep} />
        <span style={{ fontFamily: "Inter", fontSize: 12, color: T.ink }}>
          Personalised booking link sent. Pre-filled with vegan menu and parking add-on.
        </span>
      </div>
    </PanelShell>
  );
}

function ConfidenceDial({ value }) {
  const pct = Math.round(value * 100);
  const r = 13;
  const c = 2 * Math.PI * r;
  const off = c * (1 - value);
  return (
    <div style={{ display: "inline-flex", flexDirection: "column", alignItems: "center" }}>
      <svg width="32" height="32" viewBox="0 0 32 32">
        <circle cx="16" cy="16" r={r} fill="none" stroke={T.ruleSoft} strokeWidth="2.4" />
        <circle cx="16" cy="16" r={r} fill="none" stroke={T.fuchsia} strokeWidth="2.4"
          strokeDasharray={c} strokeDashoffset={off}
          strokeLinecap="round"
          transform="rotate(-90 16 16)"
          style={{ transition: "stroke-dashoffset 800ms cubic-bezier(.2,.8,.2,1)" }} />
      </svg>
      <span style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: 9.5,
        color: T.muted, marginTop: 2 }}>{pct}%</span>
    </div>
  );
}

// 4 · TASKS
function TaskPanel({ d, t }) {
  return (
    <PanelShell title="Tasks created">
      <div style={{
        marginBottom: 14, padding: "10px 12px",
        borderRadius: 10, background: T.surface, border: `1px solid ${T.rule}`,
        display: "flex", alignItems: "center", gap: 10,
      }}>
        <PortIcon kind="key" size={14} color={T.ink} />
        <span style={{ fontFamily: "Inter", fontSize: 12, color: T.ink }}>{d.property}</span>
      </div>

      <div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
        {d.tasks.map((tk, i) => (
          <div key={i} style={{
            position: "relative",
            padding: "14px 16px", borderRadius: 12,
            background: T.surface, border: `1px solid ${T.rule}`,
            display: "flex", alignItems: "flex-start", gap: 12,
          }}>
            <div style={{
              width: 36, height: 36, borderRadius: 10,
              background: tk.priority === "VIP" ? T.fuchsia : T.paper,
              border: tk.priority === "VIP" ? "none" : `1px solid ${T.rule}`,
              display: "flex", alignItems: "center", justifyContent: "center",
              flexShrink: 0,
            }}>
              <PortIcon kind={tk.icon} size={16} color={tk.priority === "VIP" ? T.onFuchsia : T.ink} />
            </div>
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 4 }}>
                <MonoLabel size={9.5}>{tk.team}</MonoLabel>
                {tk.priority === "VIP" && (
                  <span style={{
                    padding: "1px 6px", borderRadius: 3,
                    background: T.fuchsia, color: T.onFuchsia,
                    fontFamily: "'JetBrains Mono', monospace",
                    fontSize: 9, letterSpacing: "0.08em", fontWeight: 700,
                  }}>VIP</span>
                )}
              </div>
              <div style={{ fontFamily: "'Instrument Serif', 'Source Serif 4', serif", fontSize: 14.5,
                color: T.ink, marginBottom: 4, lineHeight: 1.3 }}>{tk.title}</div>
              <div style={{ display: "flex", alignItems: "center", gap: 6 }}>
                <PortIcon kind="clock" size={11} color={T.muted} />
                <span style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: 10,
                  color: T.muted }}>{tk.when.toUpperCase()}</span>
              </div>
            </div>
            <div style={{
              fontFamily: "'JetBrains Mono', monospace", fontSize: 9,
              color: T.sage, letterSpacing: "0.08em", fontWeight: 700,
              display: "flex", alignItems: "center", gap: 4,
            }}>
              <span style={{ width: 6, height: 6, borderRadius: "50%", background: T.sage }} />
              SENT
            </div>
          </div>
        ))}
      </div>

      <div style={{ marginTop: 14, display: "flex", alignItems: "center", justifyContent: "space-between",
        fontFamily: "'JetBrains Mono', monospace", fontSize: 10, color: T.muted }}>
        <span>PMS · SYNCED · {d.pms.ref}</span>
        <span style={{ display: "inline-flex", alignItems: "center", gap: 4 }}>
          <span style={{ width: 6, height: 6, borderRadius: "50%", background: T.sage }} />
          OPERA · ROOMSTAR · OK
        </span>
      </div>
    </PanelShell>
  );
}

// 5 · HANDOFF
function HandoffPanel({ d, t }) {
  return (
    <PanelShell title="Escalation · Group Sales">
      <div style={{
        padding: "12px 14px", borderRadius: 12,
        background: "rgba(201,162,74,0.10)", border: `1px solid rgba(201,162,74,0.32)`,
        marginBottom: 14, display: "flex", alignItems: "flex-start", gap: 10,
      }}>
        <div style={{
          width: 22, height: 22, borderRadius: "50%",
          background: T.surface, border: `1.5px solid ${T.gold}`,
          display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0,
        }}>
          <span style={{ color: T.gold, fontFamily: "'JetBrains Mono', monospace",
            fontSize: 12, fontWeight: 700 }}>!</span>
        </div>
        <div>
          <MonoLabel color={T.warn} size={10}>RULE TRIGGERED</MonoLabel>
          <div style={{ fontFamily: "Inter", fontSize: 12.5, color: T.inkSoft, marginTop: 4, lineHeight: 1.4 }}>
            {d.reason}
          </div>
        </div>
      </div>

      <MonoLabel>BRIEF CAPTURED</MonoLabel>
      <div style={{
        marginTop: 10, display: "grid", gridTemplateColumns: "1fr 1fr", gap: 1,
        background: T.ruleSoft, borderRadius: 10, overflow: "hidden",
        border: `1px solid ${T.rule}`,
      }}>
        {[
          ["ROOMS",   d.brief.rooms],
          ["NIGHTS",  d.brief.nights],
          ["WHEN",    d.brief.when],
          ["REGION",  d.brief.region],
          ["MEETING", d.brief.meeting],
          ["ATTENDS", d.brief.attendees],
        ].map(([k,v], i) => (
          <div key={k} style={{ background: T.surface, padding: "10px 12px" }}>
            <MonoLabel size={9.5}>{k}</MonoLabel>
            <div style={{ fontFamily: "'Instrument Serif', 'Source Serif 4', serif", fontSize: 13.5,
              color: T.ink, marginTop: 3, lineHeight: 1.25 }}>{v}</div>
          </div>
        ))}
      </div>

      <div style={{
        marginTop: 14, padding: "14px 16px", borderRadius: 12,
        background: T.ink, color: T.onInk,
      }}>
        <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 10 }}>
          <PortIcon kind="users" size={13} color={T.onInk} />
          <span style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: 10,
            color: T.onInk, letterSpacing: "0.10em", fontWeight: 700 }}>HANDOFF · {d.handoff.team}</span>
        </div>
        <div style={{ display: "flex", alignItems: "center", gap: 12 }}>
          <Avatar initials="ML" size={40} />
          <div style={{ flex: 1 }}>
            <div style={{ fontFamily: "'Instrument Serif', 'Source Serif 4', serif", fontSize: 16 }}>
              {d.handoff.owner}
            </div>
            <div style={{ fontSize: 11, opacity: 0.7, marginTop: 2 }}>
              {d.handoff.slot}
            </div>
          </div>
          <div style={{
            padding: "6px 10px", borderRadius: 6,
            background: "rgba(255,255,255,0.10)",
            fontFamily: "'JetBrains Mono', monospace", fontSize: 10,
            letterSpacing: "0.06em",
          }}>{d.handoff.ticket}</div>
        </div>
      </div>
    </PanelShell>
  );
}

// 6 · LOYALTY
function LoyaltyPanel({ d, t }) {
  // animate counter from before to after based on t (0..1 within call segment)
  const phase = Math.min(1, Math.max(0, (t - 0.55) * 2.4));
  const current = Math.round(d.before - (d.before - d.after) * phase);
  const cost = Math.round((d.before - d.after) * phase);

  return (
    <PanelShell title="Points redemption">
      <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 14 }}>
        <span style={{
          padding: "4px 10px", borderRadius: 999,
          background: `linear-gradient(135deg, ${T.gold}, ${T.gold})`,
          color: T.onGold,
          fontFamily: "'JetBrains Mono', monospace", fontSize: 10,
          fontWeight: 700, letterSpacing: "0.10em",
        }}>OLEA CIRCLE · {d.tier}</span>
      </div>

      <div style={{
        padding: "20px 22px", borderRadius: 14,
        background: T.surface, border: `1px solid ${T.rule}`,
        marginBottom: 14, position: "relative", overflow: "hidden",
      }}>
        <MonoLabel size={9.5}>BALANCE</MonoLabel>
        <div style={{ display: "flex", alignItems: "baseline", gap: 8, marginTop: 6 }}>
          <span style={{ fontFamily: "'Instrument Serif', 'Source Serif 4', serif", fontSize: 42,
            color: T.ink, fontWeight: 400, letterSpacing: "-0.01em",
            fontVariantNumeric: "tabular-nums" }}>
            {current.toLocaleString()}
          </span>
          <span style={{ fontFamily: "Inter", fontSize: 14, color: T.muted }}>pts</span>
        </div>
        <div style={{ marginTop: 12, height: 4, borderRadius: 2,
          background: T.paper, overflow: "hidden" }}>
          <div style={{ width: `${(current / d.before) * 100}%`, height: "100%",
            background: T.fuchsia, transition: "width 600ms" }} />
        </div>
        <div style={{ marginTop: 8, display: "flex", justifyContent: "space-between",
          fontFamily: "'JetBrains Mono', monospace", fontSize: 9.5, color: T.faint }}>
          <span>0</span>
          <span style={{ color: T.fuchsia }}>− {cost.toLocaleString()} APPLIED</span>
          <span>{d.before.toLocaleString()}</span>
        </div>
      </div>

      <div style={{
        padding: "14px 16px", borderRadius: 12,
        background: T.fuchsiaSoft, border: `1px solid rgba(206,81,136,0.30)`,
      }}>
        <MonoLabel color={T.fuchsiaDeep} size={9.5}>UPGRADE APPLIED</MonoLabel>
        <div style={{ marginTop: 6, fontFamily: "'Instrument Serif', 'Source Serif 4', serif",
          fontSize: 17, color: T.ink, fontWeight: 400 }}>
          {d.stay.hotel}
        </div>
        <div style={{
          marginTop: 8, display: "flex", alignItems: "center", gap: 10,
          fontFamily: "Inter", fontSize: 12.5, color: T.ink,
        }}>
          <span style={{ textDecoration: "line-through", color: T.muted }}>{d.stay.room_from}</span>
          <PortIcon kind="arrow" size={12} color={T.fuchsia} />
          <span style={{ fontWeight: 600, color: T.fuchsiaDeep }}>{d.stay.room_to}</span>
        </div>
        <div style={{ marginTop: 6, fontSize: 11.5, color: T.muted }}>
          {d.stay.from} → {d.stay.to}, 7 nights
        </div>
      </div>
    </PanelShell>
  );
}

// 7 · DISRUPTION
function DisruptionPanel({ d, t }) {
  return (
    <PanelShell title="Booking rescued">
      <div style={{
        padding: "10px 12px", borderRadius: 10,
        background: T.fuchsiaSoft,
        marginBottom: 14, display: "flex", alignItems: "center", gap: 8,
      }}>
        <PortIcon kind="spark" size={13} color={T.fuchsiaDeep} />
        <span style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: 10.5,
          color: T.fuchsiaDeep, fontWeight: 700, letterSpacing: "0.08em" }}>
          POLICY HIT · {d.policyHit.toUpperCase()}
        </span>
      </div>

      <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
        {d.changes.map((c, i) => (
          <div key={i} style={{
            padding: "12px 14px", borderRadius: 12,
            background: T.surface, border: `1px solid ${T.rule}`,
          }}>
            <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 8 }}>
              <PortIcon kind={c.kind === "stay" ? "key" : "plane"} size={13} color={T.ink} />
              <MonoLabel>{c.kind === "stay" ? "STAY DATES" : "AIRPORT TRANSFER"}</MonoLabel>
              <div style={{ flex: 1 }} />
              <span style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: 9.5,
                color: T.sage, fontWeight: 700, letterSpacing: "0.08em" }}>UPDATED</span>
            </div>
            <div style={{
              display: "grid", gridTemplateColumns: "1fr 22px 1fr", gap: 8, alignItems: "center",
            }}>
              <div style={{ padding: "8px 10px", borderRadius: 8,
                background: T.paper, opacity: 0.6 }}>
                <div style={{ fontFamily: "Inter", fontSize: 12,
                  color: T.ink, textDecoration: "line-through" }}>{c.from}</div>
              </div>
              <div style={{ display: "flex", justifyContent: "center" }}>
                <PortIcon kind="arrow" size={14} color={T.fuchsia} />
              </div>
              <div style={{ padding: "8px 10px", borderRadius: 8,
                background: T.fuchsiaSoft, border: `1px solid rgba(206,81,136,0.30)` }}>
                <div style={{ fontFamily: "Inter", fontSize: 12, color: T.ink,
                  fontWeight: 600 }}>{c.to}</div>
              </div>
            </div>
            <div style={{ marginTop: 8, fontSize: 11, color: T.muted, fontStyle: "italic" }}>
              {c.note}
            </div>
          </div>
        ))}
      </div>

      <div style={{ marginTop: 14, display: "flex", alignItems: "center", justifyContent: "space-between",
        padding: "8px 12px", borderRadius: 8, background: T.paper }}>
        <span style={{ display: "inline-flex", alignItems: "center", gap: 6 }}>
          <span style={{ width: 6, height: 6, borderRadius: "50%", background: T.sage,
            animation: "lc-pulse 1.4s ease-in-out infinite" }} />
          <MonoLabel size={9.5}>{d.pms.toUpperCase()}</MonoLabel>
        </span>
        <MonoLabel size={9.5}>NO CHARGE · GUEST NOTIFIED</MonoLabel>
      </div>
    </PanelShell>
  );
}

// 8 · ITINERARY
function ItineraryPanel({ d, t }) {
  return (
    <PanelShell title="Itinerary · two-leg">
      <div style={{ position: "relative" }}>
        {/* connecting line */}
        <div style={{
          position: "absolute", left: 27, top: 60, bottom: 60,
          width: 2, background: `linear-gradient(to bottom, ${T.fuchsia}, ${T.fuchsia})`,
          borderLeft: `2px dashed ${T.fuchsia}`, background: "transparent",
        }} />
        {d.legs.map((leg, i) => (
          <div key={i} style={{ position: "relative", display: "flex", gap: 14,
            marginBottom: i === 0 ? 14 : 0, alignItems: "stretch" }}>
            <div style={{
              width: 56, flexShrink: 0,
              display: "flex", flexDirection: "column", alignItems: "center", paddingTop: 12,
            }}>
              <div style={{
                width: 30, height: 30, borderRadius: "50%",
                background: T.fuchsia, color: T.onFuchsia,
                display: "flex", alignItems: "center", justifyContent: "center",
                fontFamily: "'Instrument Serif', 'Source Serif 4', serif", fontSize: 14, fontWeight: 400,
                position: "relative", zIndex: 2,
              }}>{i + 1}</div>
              {i === 0 && (
                <div style={{
                  marginTop: 14, padding: "8px 4px",
                  background: T.paper, borderRadius: 8,
                  border: `1px solid ${T.rule}`,
                  display: "flex", flexDirection: "column", alignItems: "center", gap: 4,
                  position: "relative", zIndex: 2,
                }}>
                  <PortIcon kind="boat" size={14} color={T.fuchsia} />
                  <span style={{ fontFamily: "'JetBrains Mono', monospace",
                    fontSize: 8, color: T.fuchsiaDeep, fontWeight: 700,
                    writingMode: "vertical-rl", letterSpacing: "0.10em" }}>
                    1H 50M
                  </span>
                </div>
              )}
            </div>
            <div style={{
              flex: 1, padding: "14px 16px", borderRadius: 12,
              background: T.surface, border: `1px solid ${T.rule}`,
              display: "flex", flexDirection: "column",
            }}>
              <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 6 }}>
                <PortIcon kind="pin" size={11} color={T.fuchsia} />
                <MonoLabel color={T.fuchsia} size={9.5}>{leg.city.toUpperCase()}</MonoLabel>
                <div style={{ flex: 1 }} />
                <span style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: 10,
                  color: T.muted }}>{leg.nights} NIGHTS</span>
              </div>
              <div style={{ fontFamily: "'Instrument Serif', 'Source Serif 4', serif", fontSize: 17,
                color: T.ink, fontWeight: 400, marginBottom: 4 }}>{leg.hotel}</div>
              <div style={{ fontSize: 11.5, color: T.inkSoft, fontStyle: "italic" }}>
                {leg.suite}
              </div>
              <div style={{ marginTop: 8, fontFamily: "'JetBrains Mono', monospace",
                fontSize: 10, color: T.muted, letterSpacing: "0.06em" }}>
                {leg.from.toUpperCase()} → {leg.to.toUpperCase()}
              </div>
            </div>
          </div>
        ))}
      </div>

      <div style={{
        marginTop: 14, padding: "10px 14px", borderRadius: 10,
        background: T.fuchsiaSoft, border: `1px solid rgba(206,81,136,0.25)`,
        display: "flex", alignItems: "center", gap: 10,
      }}>
        <PortIcon kind="boat" size={14} color={T.fuchsiaDeep} />
        <div style={{ flex: 1 }}>
          <div style={{ fontFamily: "Inter", fontSize: 12.5, color: T.ink,
            fontWeight: 600 }}>{d.transfer.mode}</div>
          <div style={{ fontSize: 11, color: T.muted, marginTop: 1 }}>
            {d.transfer.duration} · {d.transfer.note}
          </div>
        </div>
      </div>
    </PanelShell>
  );
}

// =============================================================
//  SHARED PANEL CHROME
// =============================================================
function PanelShell({ title, children }) {
  return (
    <div style={{ height: "100%", display: "flex", flexDirection: "column" }}>
      <div style={{
        display: "flex", alignItems: "center", gap: 8, marginBottom: 16,
      }}>
        <span style={{ width: 16, height: 1, background: T.fuchsia }} />
        <MonoLabel color={T.fuchsia}>{title.toUpperCase()}</MonoLabel>
      </div>
      <div style={{ flex: 1, minHeight: 0, overflow: "hidden" }}>
        {children}
      </div>
    </div>
  );
}

function PanelStat({ label, value }) {
  return (
    <div style={{
      padding: "10px 12px", borderRadius: 10,
      background: T.surface, border: `1px solid ${T.rule}`,
    }}>
      <MonoLabel size={9.5}>{label}</MonoLabel>
      <div style={{ fontFamily: "'Instrument Serif', 'Source Serif 4', serif", fontSize: 14,
        color: T.ink, marginTop: 4, fontWeight: 400 }}>{value}</div>
    </div>
  );
}

function PlaceholderImg({ w, h, radius = 6 }) {
  return (
    <div style={{
      width: w, height: h, borderRadius: radius, flexShrink: 0,
      background: `repeating-linear-gradient(135deg,
        ${T.paper} 0px, ${T.paper} 4px,
        ${T.cream} 4px, ${T.cream} 8px)`,
      border: `1px solid ${T.rule}`,
    }} />
  );
}

// =============================================================
//  FEATURED LIVE PLAYER
// =============================================================
function FeaturedPlayer({ activeIdx, setActiveIdx, paused, setPaused, soundOn, setSoundOn, hasInteracted, setHasInteracted }) {
  const call = CALLS[activeIdx];

  // Per-call audio-driven timeline. Real WAV durations live in LC_DUR.
  const turnDurs = LC_DUR[call.id] || call.dialog.map(() => 3.4);
  const turnStarts = useMemoLC(() => {
    const arr = [LC_PRE_GAP];
    for (let i = 0; i < turnDurs.length; i++) arr.push(arr[i] + turnDurs[i] + LC_TURN_GAP);
    return arr;
  }, [activeIdx]);
  const total = turnStarts[turnStarts.length - 1] - LC_TURN_GAP + LC_POST_GAP;

  // Visual clock (seconds since call started). Driven by RAF when not paused.
  const [t, setT] = useStateLC(0);
  const last = useRefLC(performance.now());

  // AudioContext + decoded buffers per call
  const audioCtxRef   = useRefLC(null);
  const buffersRef    = useRefLC({});  // { callId: AudioBuffer[] }
  const sourceRef     = useRefLC(null); // currently scheduled BufferSource
  const playingTurnRef = useRefLC(-1);  // which turn idx is currently scheduled

  // audioReady gates the visual clock when sound is on so it doesn't race
  // ahead of the async fetch+decode and start audio mid-message.
  const [audioReady, setAudioReady] = useStateLC(false);

  function ensureAudioCtx() {
    if (!audioCtxRef.current) {
      try { audioCtxRef.current = new (window.AudioContext || window.webkitAudioContext)(); } catch(_) {}
    }
    return audioCtxRef.current;
  }

  // Pre-fetch + decode the active call's audio. Only the FIRST turn's
  // buffer needs to be ready before the visual clock can start; the rest
  // decode in the background and arrive in time.
  useEffectLC(() => {
    const ctx = audioCtxRef.current;
    if (!ctx || !soundOn) return;
    if (buffersRef.current[call.id]) {
      setAudioReady(true);
      return;
    }
    let cancelled = false;
    (async () => {
      const arr = [];
      buffersRef.current[call.id] = arr; // commit reference early so the scheduler can read partial progress
      for (let i = 0; i < call.dialog.length; i++) {
        try {
          const r = await fetch(`${LC_AUDIO_BASE}/${call.id}/turn-${String(i).padStart(2, "0")}.wav`);
          const ab = await r.arrayBuffer();
          const buf = await ctx.decodeAudioData(ab);
          if (cancelled) return;
          arr[i] = buf;
          if (i === 0) setAudioReady(true); // first turn ready, unblock the clock
        } catch(_) { /* skip, visual falls through gracefully */ }
      }
    })();
    return () => { cancelled = true; };
  }, [activeIdx, soundOn]);

  // When call changes OR sound enables: reset clock, stop any audio
  // source, and clear audioReady so we wait for the new buffer to load
  // before the visual starts moving.
  useEffectLC(() => {
    setT(0);
    last.current = performance.now();
    if (sourceRef.current) {
      try { sourceRef.current.onended = null; sourceRef.current.stop(0); } catch(_) {}
      sourceRef.current = null;
    }
    playingTurnRef.current = -1;
    if (soundOn) {
      // Block the clock until the new call's first buffer arrives. If the
      // buffer is already cached (we've played this call before), the
      // load effect above will flip audioReady back to true synchronously.
      const cached = buffersRef.current[call.id];
      setAudioReady(!!(cached && cached[0]));
    } else {
      setAudioReady(true); // no audio to wait on
    }
  }, [activeIdx, soundOn]);

  // Visual RAF clock — pauses on `paused` OR while audio is loading.
  useEffectLC(() => {
    if (paused) return;
    if (soundOn && !audioReady) return;
    let raf;
    const tick = (now) => {
      const dt = (now - last.current) / 1000;
      last.current = now;
      setT(prev => {
        const next = prev + dt;
        if (next >= total) {
          setActiveIdx((activeIdx + 1) % CALLS.length);
          return 0;
        }
        return next;
      });
      raf = requestAnimationFrame(tick);
    };
    last.current = performance.now();
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [paused, activeIdx, total, soundOn, audioReady]);

  // Audio scheduler, fires the right turn's audio when t crosses its start.
  // Visual transcript reveal is already driven by `t`; audio plays on top.
  useEffectLC(() => {
    if (paused || !soundOn) return;
    const ctx = audioCtxRef.current;
    if (!ctx) return;
    if (ctx.state === "suspended") ctx.resume().catch(() => {});
    const buffers = buffersRef.current[call.id];
    if (!buffers) return;

    // Determine which turn t is currently within.
    let idx = -1;
    for (let i = 0; i < turnDurs.length; i++) {
      if (t >= turnStarts[i] && t < turnStarts[i] + turnDurs[i]) { idx = i; break; }
    }
    if (idx === -1 || playingTurnRef.current === idx) return;

    // Stop any prior source (e.g. user seek-paused/skipped).
    if (sourceRef.current) {
      try { sourceRef.current.onended = null; sourceRef.current.stop(0); } catch(_) {}
      sourceRef.current = null;
    }
    const buf = buffers[idx];
    if (!buf) return;
    const offset = Math.max(0, t - turnStarts[idx]);
    if (offset >= buf.duration) return;
    try {
      const src = ctx.createBufferSource();
      src.buffer = buf;
      src.connect(ctx.destination);
      src.onended = () => { if (sourceRef.current === src) { sourceRef.current = null; playingTurnRef.current = -1; } };
      src.start(0, offset);
      sourceRef.current = src;
      playingTurnRef.current = idx;
    } catch(_) {}
  }, [t, paused, soundOn, activeIdx]);

  // Pause behaviour: also suspend the AudioContext so audio actually pauses.
  useEffectLC(() => {
    const ctx = audioCtxRef.current;
    if (!ctx) return;
    if (paused) {
      ctx.suspend().catch(() => {});
      // Stop the live source so on resume we re-schedule from the new t.
      if (sourceRef.current) {
        try { sourceRef.current.onended = null; sourceRef.current.stop(0); } catch(_) {}
        sourceRef.current = null;
        playingTurnRef.current = -1;
      }
    } else if (soundOn) {
      ctx.resume().catch(() => {});
    }
  }, [paused, soundOn]);

  // First user gesture: enable audio (browsers require gesture to start ctx).
  function arm() {
    if (!hasInteracted) {
      setHasInteracted(true);
      ensureAudioCtx();
      setSoundOn(true);
    }
  }

  // Derived per-turn progress (drives transcript word reveal + waveform)
  let turnIdx = 0;
  for (let i = 0; i < turnDurs.length; i++) {
    if (t >= turnStarts[i]) turnIdx = i;
  }
  const turnProgress = Math.min(1, Math.max(0, (t - turnStarts[turnIdx]) / turnDurs[turnIdx]));
  const visibleTurns = call.dialog.slice(0, Math.max(0, turnIdx + 1));

  // typing characters for current turn
  const currentTurn = call.dialog[turnIdx];
  const charsRevealed = currentTurn ? Math.floor(currentTurn.text.length * Math.min(1, turnProgress * 1.4)) : 0;

  // panel time within call
  const panelT = t / total;

  // elapsed clock (mapped to displayed duration)
  const [m, s] = call.duration.split(":").map(Number);
  const totalDispSec = m * 60 + s;
  const dispElapsed = Math.floor((t / total) * totalDispSec);
  const mm = String(Math.floor(dispElapsed / 60)).padStart(2, "0");
  const ss = String(dispElapsed % 60).padStart(2, "0");

  const progressPct = (t / total) * 100;

  // waveform bars driven by t
  const bars = Array.from({ length: 36 }).map((_, i) => {
    const speaking = currentTurn && turnProgress < 0.95;
    const base = 6;
    const amp  = speaking ? 22 : 4;
    const h = base + Math.abs(Math.sin(t * 5 + i * 0.42)) * amp +
                     Math.abs(Math.sin(t * 2 + i * 0.21)) * (amp * 0.4);
    const isAgent = currentTurn?.who === "agent";
    return { h, color: isAgent ? T.fuchsia : T.ink };
  });

  return (
    <ScaleFrame nativeWidth={1100} style={{ maxWidth: 1100, margin: "0 auto" }}>
    <div style={{
      display: "grid", gridTemplateColumns: "1.15fr 1fr",
      background: T.surface, borderRadius: 22, overflow: "hidden",
      border: `1px solid ${T.rule}`,
      minHeight: 660,
    }}>
      {/* LEFT: caller + transcript */}
      <div style={{
        padding: "32px 36px 28px",
        display: "flex", flexDirection: "column",
        background: T.paper,
        borderRight: `1px solid ${T.rule}`,
        position: "relative",
      }}>
        {/* progress line at top */}
        <div style={{
          position: "absolute", left: 0, right: 0, top: 0, height: 2,
          background: T.ruleSoft,
        }}>
          <div style={{
            width: `${progressPct}%`, height: "100%", background: T.fuchsia,
            transition: "width 80ms linear",
          }} />
        </div>

        {/* tag row */}
        <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 22 }}>
          <span style={{
            display: "inline-flex", alignItems: "center", gap: 8,
            padding: "5px 11px", borderRadius: 999,
            background: T.fuchsiaSoft,
          }}>
            <span style={{ width: 7, height: 7, borderRadius: "50%", background: T.fuchsia,
              animation: "lc-pulse 1.2s ease-in-out infinite" }} />
            <span style={{ fontFamily: "'JetBrains Mono', monospace",
              fontSize: 10.5, color: T.fuchsiaDeep, letterSpacing: "0.10em",
              fontWeight: 700, fontVariantNumeric: "tabular-nums" }}>
              LIVE · {mm}:{ss}
            </span>
          </span>
          <span style={{ flex: 1 }} />
          <MonoLabel size={10}>CALL {call.no} OF 08</MonoLabel>
        </div>

        {/* caller card */}
        <div style={{ display: "flex", alignItems: "center", gap: 14, marginBottom: 20 }}>
          <Avatar initials={call.caller.initials} size={56} ring />
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ fontFamily: "'Instrument Serif', 'Source Serif 4', serif",
              fontSize: 24, color: T.ink, fontWeight: 400, letterSpacing: "-0.01em" }}>
              {call.caller.name}
            </div>
            <div style={{ display: "flex", alignItems: "center", gap: 8, marginTop: 4,
              fontFamily: "'JetBrains Mono', monospace", fontSize: 10.5, color: T.muted,
              letterSpacing: "0.06em" }}>
              <PortIcon kind="pin" size={11} color={T.muted} />
              {call.caller.city.toUpperCase()}
              <span style={{ width: 3, height: 3, borderRadius: "50%", background: T.faint }} />
              {call.caller.loyalty}
            </div>
          </div>
        </div>

        {/* scenario tag + headline */}
        <MonoLabel>{call.tag}</MonoLabel>
        <h3 style={{
          fontFamily: "'Instrument Serif', 'Source Serif 4', serif",
          fontSize: 30, color: T.ink, fontWeight: 400,
          margin: "10px 0 22px", letterSpacing: "-0.015em", lineHeight: 1.15,
        }}>
          <em style={{ fontStyle: "italic", color: T.fuchsiaDeep }}>{call.title}</em>
        </h3>

        {/* waveform */}
        <div style={{
          display: "flex", alignItems: "center", justifyContent: "flex-start",
          gap: 3, height: 36, marginBottom: 18, paddingLeft: 2,
        }}>
          {bars.map((b, i) => (
            <span key={i} style={{
              width: 3, height: b.h, background: b.color,
              borderRadius: 2, opacity: 0.85,
              transition: "height 80ms",
            }} />
          ))}
        </div>

        {/* transcript */}
        <div style={{
          flex: 1, minHeight: 0,
          display: "flex", flexDirection: "column", gap: 14,
          paddingTop: 4,
        }}>
          {visibleTurns.map((turn, i) => {
            const isLast = i === visibleTurns.length - 1;
            const text = isLast ? turn.text.slice(0, charsRevealed) : turn.text;
            const showCursor = isLast && charsRevealed < turn.text.length;
            const isAgent = turn.who === "agent";
            return (
              <div key={i} style={{
                display: "flex", gap: 12, alignItems: "flex-start",
                animation: "lc-slide-up .42s ease both",
              }}>
                <div style={{
                  width: 36, flexShrink: 0,
                  display: "flex", justifyContent: "flex-start",
                }}>
                  <span style={{
                    fontFamily: "'JetBrains Mono', monospace",
                    fontSize: 9.5, fontWeight: 700, letterSpacing: "0.10em",
                    color: isAgent ? T.fuchsia : T.muted,
                    paddingTop: 4,
                  }}>{isAgent ? "AGENT" : "GUEST"}</span>
                </div>
                <div style={{
                  flex: 1, fontFamily: isAgent ? "'Instrument Serif', 'Source Serif 4', serif" : "Inter",
                  fontSize: isAgent ? 16 : 14.5,
                  color: isAgent ? T.ink : T.inkSoft,
                  lineHeight: 1.5,
                  fontStyle: isAgent ? "italic" : "normal",
                }}>
                  {text}
                  {showCursor && (
                    <span style={{
                      display: "inline-block", width: 2, height: 14,
                      background: T.fuchsia, marginLeft: 2, verticalAlign: "-2px",
                      animation: "lc-blink 0.7s steps(1) infinite",
                    }} />
                  )}
                </div>
              </div>
            );
          })}
        </div>

        {/* outcome stamp + transport controls */}
        <div style={{ marginTop: 22, display: "flex", alignItems: "center", gap: 12 }}>
          <OutcomeStamp outcome={call.outcome} />
          <div style={{ flex: 1 }} />
          <button
            onClick={() => {
              arm();
              setSoundOn(v => !v);
            }}
            style={{
              border: `1px solid ${T.rule}`, background: soundOn ? T.fuchsia : T.surface,
              color: soundOn ? T.onFuchsia : T.ink,
              borderRadius: 999, padding: "7px 14px", cursor: "pointer",
              fontFamily: "'JetBrains Mono', monospace", fontSize: 10.5,
              letterSpacing: "0.08em", fontWeight: 600,
              display: "inline-flex", alignItems: "center", gap: 8,
              transition: "background 0.2s, color 0.2s",
            }}>
            {soundOn ? "🔊  SOUND ON" : "🔇  PLAY WITH SOUND"}
          </button>
          <button
            onClick={() => { arm(); setPaused(p => !p); }}
            style={{
              border: `1px solid ${T.rule}`, background: T.surface,
              borderRadius: 999, padding: "7px 14px", cursor: "pointer",
              fontFamily: "'JetBrains Mono', monospace", fontSize: 10.5,
              color: T.ink, letterSpacing: "0.08em", fontWeight: 600,
              display: "inline-flex", alignItems: "center", gap: 8,
            }}>
            {paused ? "▶  PLAY" : "❙❙  PAUSE"}
          </button>
        </div>
      </div>

      {/* RIGHT: AI behind the call */}
      <div style={{
        padding: "32px 36px 28px",
        display: "flex", flexDirection: "column",
        background: T.surface,
        position: "relative",
      }}>
        <RightPanel kind={call.panelKind} data={call.panel} t={panelT} />
      </div>
    </div>
    </ScaleFrame>
  );
}

// =============================================================
//  CALL CHIPS · selector under the player
// =============================================================
function CallChips({ activeIdx, onPick }) {
  return (
    <>
    {/* Eight chips across is ~40px each on a phone. These are independently
        readable labels rather than one fixed canvas, so they WRAP instead of
        being scaled; desktop keeps its exact 8-across row. */}
    <style>{`
      .vlc-chips { grid-template-columns: repeat(8, minmax(0, 1fr)); }
      @media (max-width: 1100px) { .vlc-chips { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
      @media (max-width: 640px)  { .vlc-chips { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
    `}</style>
    <div className="vlc-chips" style={{
      display: "grid",
      gap: 8, marginTop: 16,
    }}>
      {CALLS.map((c, i) => {
        const active = i === activeIdx;
        return (
          <button key={c.id} onClick={() => onPick(i)}
            style={{
              border: `1px solid ${active ? T.fuchsia : T.rule}`,
              background: active ? T.fuchsiaSoft : T.surface,
              borderRadius: 10, padding: "10px 10px 10px 12px",
              cursor: "pointer", textAlign: "left",
              transition: "all 200ms",
            }}>
            <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 4 }}>
              <span style={{
                fontFamily: "'JetBrains Mono', monospace", fontSize: 10,
                color: active ? T.fuchsiaDeep : T.faint,
                fontWeight: 700, letterSpacing: "0.10em",
              }}>{c.no}</span>
              {active && (
                <span style={{ width: 6, height: 6, borderRadius: "50%", background: T.fuchsia,
                  animation: "lc-pulse 1.2s ease-in-out infinite" }} />
              )}
            </div>
            <div style={{
              fontFamily: "Inter", fontSize: 11.5,
              color: active ? T.ink : T.inkSoft,
              fontWeight: 600, lineHeight: 1.3,
              display: "-webkit-box", WebkitLineClamp: 2, WebkitBoxOrient: "vertical",
              overflow: "hidden",
            }}>{c.tag.split(" · ")[0]}</div>
          </button>
        );
      })}
    </div>
    </>
  );
}

// =============================================================
//  GALLERY CARD (compact)
// =============================================================
function GalleryCard({ call, onListen, isPlaying }) {
  const tags = useMemoLC(() => {
    switch (call.panelKind) {
      case "continuation": return ["MEMORY RECALL", "ROOM HELD"];
      case "match":        return ["PORTFOLIO 34→3", "ADULT-ONLY · HAMMAM"];
      case "kb":           return ["6 KB SOURCES", "AVG CONF 0.95"];
      case "tasks":        return ["2 TASKS · PMS SYNC", "VIP TURNDOWN"];
      case "handoff":      return ["RULE > 20 ROOMS", "GROUP SALES · MARCO L."];
      case "loyalty":      return ["12K POINTS", "PLATINUM TIER"];
      case "disruption":   return ["FLEX POLICY", "TRANSFER REBOOKED"];
      case "itinerary":    return ["2 LEGS · 8 NIGHTS", "INTER-PROPERTY BOAT"];
      default: return [];
    }
  }, [call.panelKind]);

  // pick 2 most representative dialogue turns
  const snippet = [call.dialog[0], call.dialog[1] || call.dialog[0]];

  return (
    <article style={{
      background: T.surface, borderRadius: 18,
      border: `1px solid ${T.rule}`,
      padding: "22px 24px 20px",
      display: "flex", flexDirection: "column",
      position: "relative",
      transition: "border-color 200ms, transform 200ms",
    }}>
      {/* header */}
      <div style={{ display: "flex", alignItems: "flex-start", gap: 12, marginBottom: 16 }}>
        <span style={{
          fontFamily: "'Instrument Serif', 'Source Serif 4', serif", fontSize: 32,
          color: T.fuchsia, fontWeight: 400, lineHeight: 1, fontStyle: "italic",
          letterSpacing: "-0.01em", flexShrink: 0, marginTop: 2,
        }}>{call.no}</span>
        <div style={{ flex: 1, minWidth: 0 }}>
          <MonoLabel>{call.tag}</MonoLabel>
          <div style={{
            fontFamily: "'Instrument Serif', 'Source Serif 4', serif", fontSize: 19,
            color: T.ink, fontWeight: 400, marginTop: 6,
            letterSpacing: "-0.01em", lineHeight: 1.25,
          }}>{call.title}</div>
        </div>
      </div>

      {/* caller line */}
      <div style={{
        display: "flex", alignItems: "center", gap: 10,
        padding: "10px 12px", borderRadius: 10,
        background: T.paper, marginBottom: 14,
      }}>
        <Avatar initials={call.caller.initials} size={32} />
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ fontFamily: "Inter", fontSize: 13, color: T.ink,
            fontWeight: 600 }}>{call.caller.name}</div>
          <div style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: 9.5,
            color: T.muted, letterSpacing: "0.06em", marginTop: 1 }}>
            {call.caller.city.toUpperCase()}
            <span style={{ margin: "0 6px", opacity: 0.5 }}>·</span>
            {call.caller.loyalty}
          </div>
        </div>
        <MonoLabel size={9.5}>{call.duration}</MonoLabel>
      </div>

      {/* snippet */}
      <div style={{
        flex: 1, padding: "12px 14px", borderRadius: 10,
        background: T.paper, border: `1px solid ${T.ruleSoft}`,
        marginBottom: 14, display: "flex", flexDirection: "column", gap: 10,
      }}>
        {snippet.map((turn, i) => {
          const isAgent = turn.who === "agent";
          return (
            <div key={i} style={{ display: "flex", gap: 10 }}>
              <span style={{
                fontFamily: "'JetBrains Mono', monospace", fontSize: 9,
                color: isAgent ? T.fuchsia : T.muted,
                fontWeight: 700, letterSpacing: "0.10em",
                paddingTop: 2, minWidth: 38,
              }}>{isAgent ? "AGENT" : "GUEST"}</span>
              <span style={{
                fontFamily: isAgent ? "'Instrument Serif', 'Source Serif 4', serif" : "Inter",
                fontStyle: isAgent ? "italic" : "normal",
                fontSize: 13, color: T.inkSoft, lineHeight: 1.45,
                display: "-webkit-box", WebkitLineClamp: 2, WebkitBoxOrient: "vertical",
                overflow: "hidden",
              }}>{turn.text}</span>
            </div>
          );
        })}
      </div>

      {/* tags */}
      <div style={{ display: "flex", flexWrap: "wrap", gap: 6, marginBottom: 14 }}>
        {tags.map(t => (
          <span key={t} style={{
            padding: "4px 9px", borderRadius: 5,
            background: T.fuchsiaSoft,
            fontFamily: "'JetBrains Mono', monospace", fontSize: 9.5,
            color: T.fuchsiaDeep, fontWeight: 700, letterSpacing: "0.08em",
          }}>{t}</span>
        ))}
      </div>

      {/* footer */}
      <div style={{
        display: "flex", alignItems: "center", justifyContent: "space-between",
        paddingTop: 14, borderTop: `1px solid ${T.ruleSoft}`,
      }}>
        <OutcomeStamp outcome={call.outcome} />
        <button
          onClick={onListen}
          style={{
            border: "none", background: isPlaying ? T.fuchsia : "transparent",
            color: isPlaying ? T.onFuchsia : T.fuchsiaDeep,
            cursor: "pointer", padding: "7px 12px", borderRadius: 999,
            fontFamily: "'JetBrains Mono', monospace", fontSize: 10,
            fontWeight: 700, letterSpacing: "0.10em",
            display: "inline-flex", alignItems: "center", gap: 6,
            transition: "all 200ms",
          }}>
          {isPlaying ? (
            <>
              <span style={{ width: 6, height: 6, borderRadius: "50%", background: T.surface,
                animation: "lc-pulse 1.2s ease-in-out infinite" }} />
              PLAYING
            </>
          ) : (
            <>▶ LISTEN</>
          )}
        </button>
      </div>
    </article>
  );
}

// =============================================================
//  ROOT, dark theme forced, no Tweaks panel
// =============================================================
function LiveCalls() {
  const [activeIdx, setActiveIdx] = useStateLC(0);
  const [paused, setPaused] = useStateLC(false);
  const [soundOn, setSoundOn] = useStateLC(false);
  const [hasInteracted, setHasInteracted] = useStateLC(false);
  const sectionRef = useRefLC(null);

  // Force dark theme on mount
  useEffectLC(() => { applyTheme(true); }, []);
  applyTheme(true);

  // Auto-pause when section is off-screen so the demo doesn't run silently
  // when nobody's looking, and so audio doesn't play before user scrolls.
  useEffectLC(() => {
    if (!sectionRef.current) return;
    const obs = new IntersectionObserver(([entry]) => {
      // pause when fully off-screen (out-of-view); play when in view
      if (!entry.isIntersecting) setPaused(true);
    }, { threshold: 0.1 });
    obs.observe(sectionRef.current);
    return () => obs.disconnect();
  }, []);

  const handlePick = (i) => {
    setActiveIdx(i);
    setPaused(false);
  };

  return (
    <section ref={sectionRef} style={{ width: "100%", background: T.cream, padding: "100px 60px 120px" }}>
      <style>{`
        @keyframes lc-pulse { 0%, 100% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.6); opacity: 0.5; } }
        @keyframes lc-blink { 0%, 50% { opacity: 1; } 51%, 100% { opacity: 0; } }
        @keyframes lc-slide-up { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }
      `}</style>

      {/* HEADER */}
      <header style={{ maxWidth: "var(--rail)", margin: "0 auto 44px" }}>
        <div style={{ display: "flex", alignItems: "center", gap: 10,
          fontFamily: "'JetBrains Mono', monospace",
          fontSize: 11, color: T.muted, letterSpacing: "0.14em", marginBottom: 14 }}>
          <span style={{ width: 28, height: 1, background: T.fuchsia }} />
          EIGHT REAL CALLS · ONE AI CONCIERGE
        </div>
        <h2 style={{
          fontFamily: "'Instrument Serif', 'Source Serif 4', serif",
          fontSize: "clamp(30px, 4vw, 56px)", fontWeight: 400, color: T.ink,
          margin: 0, letterSpacing: "-0.02em", lineHeight: 1.02,
          maxWidth: 1000,
        }}>
          A guest calls. Something happens. <em style={{ color: T.fuchsia, fontStyle: "italic" }}>
          The agent handles it.</em>
        </h2>
        <p style={{
          fontFamily: "Inter", fontSize: 16, color: T.muted,
          marginTop: 18, maxWidth: 760, lineHeight: 1.55,
        }}>
          Returning guests, group escalations, knowledge-base questions, special requests, loyalty
          redemptions, and the occasional cancelled flight. Every call below is a real situation
          your front desk sees every week, and the AI now resolves end-to-end, with the right
          handoff when a human is needed.
        </p>
      </header>

      {/* FEATURED PLAYER */}
      <div style={{ maxWidth: "var(--rail)", margin: "0 auto" }}>
        <FeaturedPlayer
          activeIdx={activeIdx}
          setActiveIdx={setActiveIdx}
          paused={paused}
          setPaused={setPaused}
          soundOn={soundOn}
          setSoundOn={setSoundOn}
          hasInteracted={hasInteracted}
          setHasInteracted={setHasInteracted}
        />
        <CallChips activeIdx={activeIdx} onPick={handlePick} />
      </div>

      {/* GALLERY DIVIDER */}
      <div style={{
        maxWidth: "var(--rail)", margin: "80px auto 32px",
        display: "flex", alignItems: "flex-end", justifyContent: "space-between",
        paddingBottom: 14, borderBottom: `1px solid ${T.rule}`,
      }}>
        <div>
          <MonoLabel>THE CASE GALLERY</MonoLabel>
          <h3 style={{
            fontFamily: "'Instrument Serif', 'Source Serif 4', serif",
            fontSize: 34, fontWeight: 400, color: T.ink,
            margin: "10px 0 0", letterSpacing: "-0.015em", lineHeight: 1.1,
          }}>
            <em style={{ color: T.fuchsia, fontStyle: "italic" }}>All eight</em>, side by side.
          </h3>
        </div>
        <div style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: 10.5,
          color: T.muted, letterSpacing: "0.10em" }}>
          AVG CALL · 04:04 · CONTAINMENT 87% · CSAT 4.8 / 5
        </div>
      </div>

      <div style={{
        maxWidth: "var(--rail)", margin: "0 auto",
        display: "grid", gridTemplateColumns: "1fr 1fr",
        gap: 22,
      }}>
        {CALLS.map((c, i) => (
          <GalleryCard
            key={c.id}
            call={c}
            isPlaying={i === activeIdx}
            onListen={() => {
              handlePick(i);
              if (typeof window !== "undefined") {
                window.scrollTo({ top: 0, behavior: "smooth" });
              }
            }}
          />
        ))}
      </div>

      {/* FOOTNOTE */}
      <div style={{
        maxWidth: "var(--rail)", margin: "60px auto 0",
        display: "flex", justifyContent: "space-between", alignItems: "center",
        fontFamily: "'JetBrains Mono', monospace",
        fontSize: 10.5, color: T.muted, letterSpacing: "0.08em",
      }}>
        <span>OLEA HOTELS · LIVE PRODUCTION SAMPLE · 7 DAY WINDOW</span>
        <span>POWERED BY GUESTMAKER · END-TO-END ENCRYPTED</span>
      </div>
    </section>
  );
}

window.LiveCalls = LiveCalls;
