/* global React */
// MCP access demo — animated apaleo user-management walkthrough.
// Scene ported from the Claude Design handoff (MCP Access Demo.dc.html);
// the embedded player below replaces the handoff's fullscreen Stage.

(function () {
  const { useState, useEffect, useRef, useContext, createContext } = React;

  const easeIO = (p) => (p < 0.5 ? 4 * p * p * p : 1 - Math.pow(-2 * p + 2, 3) / 2);

  // piecewise keyframe value
  function kv(t, pts) {
    if (t <= pts[0][0]) return pts[0][1];
    const last = pts[pts.length - 1];
    if (t >= last[0]) return last[1];
    for (let i = 0; i < pts.length - 1; i++) {
      const [t0, v0] = pts[i], [t1, v1] = pts[i + 1];
      if (t >= t0 && t <= t1) {
        const p = (t - t0) / (t1 - t0);
        return v0 + (v1 - v0) * easeIO(p);
      }
    }
    return last[1];
  }

  const NAVY = '#19213B';
  const ORANGE = '#ED8C1F';
  const TXT = '#3E4457';
  const MUTED = '#70778C';
  const BORDER = '#D8DAE5';
  const CHIP_BG = '#EDEFF5';
  const FONT = "'Roboto','Helvetica Neue',Arial,sans-serif";
  const TARGET_ROLE = 'MCP Manage';

  const TimeContext = createContext(0);
  const useTime = () => useContext(TimeContext);

  const Icon = ({ name, size = 20, color = 'inherit', fill = 1, style }) => (
    <span
      className="material-symbols-rounded"
      style={{
        fontSize: size, color, lineHeight: 1, userSelect: 'none',
        fontVariationSettings: `'FILL' ${fill}, 'wght' 400, 'GRAD' 0, 'opsz' 20`,
        ...style,
      }}
    >{name}</span>
  );

  const Tag = ({ label }) => (
    <span style={{
      background: CHIP_BG, color: TXT, borderRadius: 4, padding: '5px 10px',
      font: `400 13px/16px ${FONT}`, whiteSpace: 'nowrap', display: 'inline-block',
    }}>{label}</span>
  );

  // The freshly-assigned tag on the final list view: pops in, brief accent ring, settles.
  const NewTag = ({ label, pop, ring, op }) => (
    <span style={{
      color: TXT, borderRadius: 4, padding: '5px 10px',
      font: `400 13px/16px ${FONT}`, whiteSpace: 'nowrap', display: 'inline-block',
      opacity: op,
      transform: `scale(${pop})`,
      transformOrigin: 'center left',
      background: `color-mix(in srgb, #FFE8C7 ${Math.round(ring * 100)}%, ${CHIP_BG})`,
      boxShadow: `0 0 0 1.5px rgba(237,140,31,${0.8 * ring})`,
    }}>{label}</span>
  );

  const Checkbox = ({ checked }) => (
    <span style={{
      width: 18, height: 18, borderRadius: 3, boxSizing: 'border-box', flex: 'none',
      border: checked ? 'none' : `2px solid ${MUTED}`,
      background: checked ? '#3E4457' : '#fff',
      display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
      transition: 'background 120ms',
    }}>{checked ? <Icon name="check" size={14} color="#fff" /> : null}</span>
  );

  // ── Sidebar + chrome ──────────────────────────────────────────────────
  function Sidebar() {
    const item = (icon, label, opts = {}) => (
      <div key={label} style={{
        display: 'flex', alignItems: 'center', gap: 14, height: 42,
        padding: opts.sub ? '0 24px 0 58px' : '0 24px',
        background: opts.active ? 'rgba(255,255,255,0.10)' : 'transparent',
        color: 'rgba(255,255,255,0.88)', font: `400 14px/1 ${FONT}`,
      }}>
        {opts.sub ? null : <Icon name={icon} size={19} color="rgba(255,255,255,0.75)" />}
        <span style={{ flex: 1 }}>{label}</span>
        {opts.caret ? <Icon name={opts.open ? 'keyboard_arrow_up' : 'keyboard_arrow_down'} size={18} color="rgba(255,255,255,0.5)" /> : null}
      </div>
    );
    return (
      <div style={{ position: 'absolute', left: 0, top: 0, bottom: 0, width: 230, background: NAVY, overflow: 'hidden' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '22px 24px 18px' }}>
          <svg width="30" height="30" viewBox="0 0 105 105" style={{ color: '#fff', flex: 'none' }}>
            <path d="M 32.407 94.63 C 27.84 94.63 24.157 98.366 20.873 101.54 C 18.657 103.682 15.64 105 12.315 105 C 5.514 105 0 99.486 0 92.685 C 0 85.884 5.514 80.37 12.315 80.37 C 15.64 80.37 18.657 81.688 20.873 83.83 C 24.157 87.005 27.84 90.741 32.407 90.741 C 36.975 90.741 40.658 87.005 43.942 83.83 C 46.158 81.688 49.175 80.37 52.5 80.37 C 59.301 80.37 64.815 85.884 64.815 92.685 C 64.815 99.486 59.301 105 52.5 105 C 49.175 105 46.158 103.682 43.942 101.54 C 40.658 98.366 36.975 94.63 32.407 94.63 Z" fill="currentColor" />
            <path d="M 105 92.685 C 105 99.486 99.486 105 92.685 105 C 85.884 105 80.37 99.486 80.37 92.685 C 80.37 85.884 85.884 80.37 92.685 80.37 C 99.486 80.37 105 85.884 105 92.685 Z" fill="currentColor" />
            <path d="M 72.593 54.444 C 77.16 54.444 80.843 58.181 84.127 61.355 C 86.343 63.497 89.36 64.815 92.685 64.815 C 99.486 64.815 105 59.301 105 52.5 C 105 45.699 99.486 40.185 92.685 40.185 C 89.36 40.185 86.343 41.503 84.127 43.645 C 80.843 46.819 77.16 50.556 72.593 50.556 C 68.025 50.556 64.342 46.819 61.058 43.645 C 58.842 41.503 55.825 40.185 52.5 40.185 C 49.175 40.185 46.158 41.503 43.942 43.645 C 40.658 46.819 36.975 50.556 32.407 50.556 C 27.84 50.556 24.157 46.819 20.873 43.645 C 18.657 41.503 15.64 40.185 12.315 40.185 C 5.514 40.185 0 45.699 0 52.5 C 0 59.301 5.514 64.815 12.315 64.815 C 15.64 64.815 18.657 63.497 20.873 61.355 C 24.157 58.181 27.84 54.444 32.407 54.444 C 36.975 54.444 40.658 58.181 43.942 61.355 C 46.158 63.497 49.175 64.815 52.5 64.815 C 55.825 64.815 58.842 63.497 61.058 61.355 C 64.342 58.181 68.025 54.444 72.593 54.444 Z" fill="currentColor" />
            <path d="M 72.593 14.259 C 77.16 14.259 80.843 17.995 84.127 21.17 C 86.343 23.312 89.36 24.63 92.685 24.63 C 99.486 24.63 105 19.116 105 12.315 C 105 5.514 99.486 0 92.685 0 C 89.36 0 86.343 1.318 84.127 3.46 C 80.843 6.634 77.16 10.37 72.593 10.37 C 68.025 10.37 64.342 6.634 61.058 3.46 C 58.842 1.318 55.825 0 52.5 0 C 45.699 0 40.185 5.514 40.185 12.315 C 40.185 19.116 45.699 24.63 52.5 24.63 C 55.825 24.63 58.842 23.312 61.058 21.17 C 64.342 17.995 68.025 14.259 72.593 14.259 Z" fill="currentColor" />
            <path d="M 0 12.315 C 0 5.514 5.514 0 12.315 0 C 19.116 0 24.63 5.514 24.63 12.315 C 24.63 19.116 19.116 24.63 12.315 24.63 C 5.514 24.63 0 19.116 0 12.315 Z" fill="currentColor" />
          </svg>
          <span style={{ font: `500 24px/1 ${FONT}`, color: '#fff', letterSpacing: '-0.02em' }}>apaleo</span>
        </div>
        <div style={{ margin: '6px 16px 18px', border: '1px solid rgba(255,255,255,0.35)', borderRadius: 4, position: 'relative', padding: '12px 14px' }}>
          <span style={{ position: 'absolute', top: -7, left: 10, background: NAVY, padding: '0 5px', font: `400 11px/1 ${FONT}`, color: 'rgba(255,255,255,0.6)' }}>Context</span>
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', color: '#fff', font: `400 15px/1 ${FONT}` }}>
            Account <Icon name="arrow_drop_down" size={20} color="rgba(255,255,255,0.6)" />
          </div>
        </div>
        {item('dashboard', 'Dashboard')}
        {item('domain', 'Properties')}
        {item('book', 'Reservations')}
        {item('content_paste_search', 'Audit / Logs', { caret: true })}
        {item('apps', 'Apps', { caret: true })}
        {item('savings', 'Payment', { caret: true })}
        {item('settings', 'Settings', { caret: true, open: true })}
        {item('', 'General', { sub: true })}
        {item('', 'Languages', { sub: true })}
        {item('', 'Market segments', { sub: true })}
        {item('', 'User management', { sub: true, active: true })}
      </div>
    );
  }

  function TopChrome({ crumbs }) {
    return (
      <div>
        <div style={{ position: 'absolute', top: 20, left: 40, display: 'flex', alignItems: 'center', gap: 8, font: `400 15px/1 ${FONT}`, color: TXT }}>
          <span>Ecosystem</span>
          <span style={{ background: '#FFF4E5', color: ORANGE, padding: '4px 8px', borderRadius: 3 }}>Dev account</span>
          {crumbs.map((c, i) => (
            <span key={i} style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
              <span style={{ color: MUTED }}>/</span><span>{c}</span>
            </span>
          ))}
        </div>
        <div style={{ position: 'absolute', top: 16, right: 34, display: 'flex', alignItems: 'center', gap: 20, color: TXT }}>
          <Icon name="palette" size={21} />
          <Icon name="code" size={21} fill={0} />
          <Icon name="widgets" size={21} />
          <Icon name="help" size={21} fill={0} />
          <Icon name="redeem" size={21} />
          <Icon name="account_circle" size={22} fill={0} />
          <span style={{ width: 1, height: 26, background: BORDER }} />
          <span style={{ display: 'flex', alignItems: 'center', gap: 7, font: `500 15px/1 ${FONT}`, color: NAVY }}>
            <Icon name="auto_awesome" size={18} color={NAVY} /> Copilot
          </span>
        </div>
      </div>
    );
  }

  // ── Screen 1: user list ───────────────────────────────────────────────
  function ListScreen({ hotelName, showNewRole, newTag, menu }) {
    const rows = [
      { name: 'Anna Meier', email: 'anna.meier@example.com', props: ['All properties'], roles: ['Account Admin'], mfa: true },
      { name: 'Ben Koch', email: 'ben.koch@example.com', props: [hotelName], roles: ['MCP Manage', 'Housekeeper'], mfa: false },
      { name: 'Clara Weber', email: 'clara.weber@example.com', props: [hotelName, 'Hotel London', '+4'], roles: ['MCP Read', 'Front Desk'], mfa: false },
      { name: 'David Roth', email: 'david.roth@example.com', props: [hotelName], roles: ['Front Desk'], mfa: false },
      { name: 'Elena Fischer', email: 'elena.fischer@example.com', props: [hotelName], roles: ['Front Desk'], mfa: true },
    ];
    const colStyle = { display: 'flex', alignItems: 'center' };
    const widths = { cb: 50, name: 235, email: 300, props: 290, roles: 340, mfa: 90 };
    const menuOn = menu && menu.op > 0.001;
    return (
      <div style={{ position: 'absolute', left: 230, top: 0, right: 0, bottom: 0, background: '#fff' }}>
        <TopChrome crumbs={['Users']} />
        <div style={{ position: 'absolute', top: 56, left: 40, font: `500 30px/1 ${FONT}`, color: NAVY, letterSpacing: '-0.01em' }}>User management</div>
        <div style={{ position: 'absolute', top: 64, right: 70, display: 'flex', alignItems: 'center', gap: 8, font: `500 16px/1 ${FONT}`, color: ORANGE }}>
          <Icon name="add" size={20} color={ORANGE} /> Invite new user
        </div>
        <Icon name="more_vert" size={22} color={TXT} style={{ position: 'absolute', top: 64, right: 32 }} />
        <div style={{ position: 'absolute', top: 118, left: 40, display: 'flex', gap: 44, font: `400 16px/1 ${FONT}`, color: TXT }}>
          {['Users', 'Invited', 'Deactivated', 'Roles', 'Access settings'].map((tab, i) => (
            <span key={tab} style={{
              paddingBottom: 14, color: i === 0 ? ORANGE : TXT,
              borderBottom: i === 0 ? `2px solid ${ORANGE}` : '2px solid transparent',
            }}>{tab}</span>
          ))}
        </div>
        <div style={{ position: 'absolute', top: 148, left: 0, right: 0, height: 1, background: '#E6E8F0' }} />
        <div style={{ position: 'absolute', top: 178, left: 40, width: 540, height: 48, border: `1px solid ${BORDER}`, borderRadius: 4, display: 'flex', alignItems: 'center', gap: 12, padding: '0 16px', boxSizing: 'border-box' }}>
          <Icon name="search" size={20} color={MUTED} />
          <span style={{ font: `400 15px/1 ${FONT}`, color: MUTED }}>Search by user first name, last name and email</span>
        </div>
        <div style={{ position: 'absolute', top: 192, right: 60, display: 'flex', alignItems: 'center', gap: 8, font: `400 15px/1 ${FONT}`, color: TXT }}>
          <Icon name="add" size={18} color={MUTED} /> Assign roles and properties
        </div>
        {/* header */}
        <div style={{ position: 'absolute', top: 254, left: 40, right: 32, height: 40, display: 'flex', alignItems: 'center', font: `500 13px/1 ${FONT}`, color: NAVY, borderBottom: '1px solid #E6E8F0' }}>
          <div style={{ ...colStyle, width: widths.cb }}><Checkbox checked={false} /></div>
          <div style={{ width: widths.name }}>Name</div>
          <div style={{ width: widths.email }}>Email</div>
          <div style={{ width: widths.props }}>Properties</div>
          <div style={{ width: widths.roles }}>Roles</div>
          <div style={{ width: widths.mfa }}>MFA</div>
        </div>
        {rows.map((r, i) => (
          <div key={r.name} style={{ position: 'absolute', top: 294 + 70 * i, left: 40, right: 32, height: 70, display: 'flex', alignItems: 'center', borderBottom: '1px solid #EDEFF5', font: `400 14px/1 ${FONT}`, color: TXT, background: i === 4 && menuOn ? '#F9FAFC' : 'transparent' }}>
            <div style={{ ...colStyle, width: widths.cb }}><Checkbox checked={false} /></div>
            <div style={{ width: widths.name, color: NAVY }}>{r.name}</div>
            <div style={{ width: widths.email }}>{r.email}</div>
            <div style={{ width: widths.props, display: 'flex', gap: 6 }}>{r.props.map((p) => <Tag key={p} label={p} />)}</div>
            <div style={{ width: widths.roles, display: 'flex', gap: 6, alignItems: 'center' }}>
              {r.roles.map((role) => <Tag key={role} label={role} />)}
              {i === 4 && showNewRole ? <NewTag label={TARGET_ROLE} pop={newTag.pop} ring={newTag.ring} op={newTag.op} /> : null}
            </div>
            <div style={{ width: widths.mfa }}>{r.mfa ? <Icon name="check" size={20} color={TXT} /> : null}</div>
            <div style={{ flex: 1 }} />
            <Icon name="more_vert" size={22} color={TXT} />
          </div>
        ))}
        {/* row action menu */}
        {menuOn ? (
          <div style={{
            position: 'absolute', top: 630, left: 1050, width: 260, background: '#fff', borderRadius: 4,
            boxShadow: '0 2px 8px rgba(9,30,66,0.24), 0 0 1px rgba(9,30,66,0.31)', padding: '6px 0', zIndex: 5,
            opacity: menu.op, transform: `translateY(${(1 - menu.op) * -6}px) scale(${0.98 + 0.02 * menu.op})`,
            transformOrigin: 'top right',
          }}>
            {[['edit', 'Edit'], ['domain', 'See properties and roles'], ['password', 'Reset password'], ['close', 'Deactivate']].map(([ic, label], i) => (
              <div key={label} style={{ display: 'flex', alignItems: 'center', gap: 14, height: 46, padding: '0 18px', font: `400 15px/1 ${FONT}`, color: label === 'Deactivate' ? '#C33325' : TXT, background: i === 0 && menu.hoverEdit ? 'rgba(0,0,0,0.05)' : 'transparent' }}>
                <Icon name={ic} size={19} color={label === 'Deactivate' ? '#C33325' : MUTED} fill={0} />
                {label}
              </div>
            ))}
          </div>
        ) : null}
        <div style={{ position: 'absolute', bottom: 26, right: 60, font: `400 14px/1 ${FONT}`, color: TXT }}>1 – 5 of 5</div>
      </div>
    );
  }

  // ── Screen 2: user edit ───────────────────────────────────────────────
  function Field({ x, y, w, h = 60, label, children, focused }) {
    return (
      <div style={{ position: 'absolute', left: x, top: y, width: w, height: h, border: focused ? `2px solid ${NAVY}` : `1px solid ${BORDER}`, borderRadius: 4, boxSizing: 'border-box', background: '#fff' }}>
        <span style={{ position: 'absolute', top: -8, left: 12, background: '#fff', padding: '0 5px', font: `400 12px/1 ${FONT}`, color: MUTED }}>{label}</span>
        <div style={{ display: 'flex', alignItems: 'center', height: '100%', padding: '0 14px', gap: 8 }}>{children}</div>
      </div>
    );
  }

  const RoleChip = ({ label, pop = 1, op = 1 }) => (
    <span style={{
      display: 'inline-flex', alignItems: 'center', gap: 8, border: `1px solid ${BORDER}`, borderRadius: 16,
      padding: '6px 8px 6px 14px', font: `400 14px/1 ${FONT}`, color: NAVY, whiteSpace: 'nowrap', background: '#fff',
      transform: `scale(${pop})`, opacity: op, transformOrigin: 'center left',
    }}>
      {label}
      <span style={{ width: 17, height: 17, borderRadius: '50%', background: '#3E4457', display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }}>
        <Icon name="close" size={12} color="#fff" />
      </span>
    </span>
  );

  function EditScreen({ hotelName, dd, searchText, caretOn, checked, chipPop, chipOp, hoverRead, hoverManage, savePressed }) {
    const filtered = searchText.length > 0;
    const items = filtered
      ? [{ label: 'MCP Read', checked: false, hover: hoverRead },
         { label: 'MCP Manage', checked, hover: hoverManage }]
      : [{ label: 'Property Admin' }, { label: 'Front Desk', checked: true }, { label: 'Senior Front Desk' }, { label: 'Reservation Manager' }, { label: 'Accountant' }];
    const ddOn = dd.op > 0.001;
    return (
      <div style={{ position: 'absolute', left: 230, top: 0, right: 0, bottom: 0, background: '#fff' }}>
        <TopChrome crumbs={['User', 'Users', 'Elena Fischer']} />
        <div style={{ position: 'absolute', top: 58, left: 40, font: `500 28px/1 ${FONT}`, color: NAVY, letterSpacing: '-0.01em' }}>
          Elena Fischer (elena.fischer@example.com)
        </div>
        <div style={{ position: 'absolute', top: 110, left: 0, right: 0, height: 1, background: '#E6E8F0' }} />
        <Field x={40} y={150} w={430} label="First name*"><span style={{ font: `400 16px/1 ${FONT}`, color: NAVY }}>Elena</span></Field>
        <Field x={510} y={150} w={430} label="Last name*"><span style={{ font: `400 16px/1 ${FONT}`, color: NAVY }}>Fischer</span></Field>
        {/* admin toggle */}
        <div style={{ position: 'absolute', left: 40, top: 248, display: 'flex', alignItems: 'center', gap: 14 }}>
          <span style={{ width: 52, height: 30, borderRadius: 15, background: '#EDEFF5', border: `1px solid ${BORDER}`, position: 'relative', display: 'inline-block', boxSizing: 'border-box' }}>
            <span style={{ position: 'absolute', left: 3, top: 2, width: 24, height: 24, borderRadius: '50%', background: '#70778C', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
              <Icon name="remove" size={15} color="#fff" />
            </span>
          </span>
          <span style={{ font: `400 15px/1 ${FONT}`, color: TXT }}>This user is Account Admin (full access to all properties and actions)</span>
        </div>
        <div style={{ position: 'absolute', left: 40, top: 302, font: `400 14px/1 ${FONT}`, color: MUTED }}>
          Selected properties apply only to property-level permissions. Account-level permissions always apply to all properties.
        </div>
        {/* Roles field */}
        <Field x={40} y={345} w={500} h={64} label="Roles*" focused={ddOn}>
          <RoleChip label="Front Desk" />
          {chipOp > 0.001 ? <RoleChip label={TARGET_ROLE} pop={chipPop} op={chipOp} /> : null}
          <div style={{ flex: 1 }} />
          <Icon name={ddOn ? 'arrow_drop_up' : 'arrow_drop_down'} size={24} color={MUTED} />
        </Field>
        {/* Property field */}
        <Field x={580} y={345} w={430} h={64} label="Property*">
          <RoleChip label={hotelName} />
          <div style={{ flex: 1 }} />
          <Icon name="arrow_drop_down" size={24} color={MUTED} />
        </Field>
        <div style={{ position: 'absolute', left: 40, top: 468, display: 'flex', alignItems: 'center', gap: 8, font: `500 15px/1 ${FONT}`, color: ORANGE }}>
          <Icon name="add" size={19} color={ORANGE} /> Add property/role definition
        </div>
        {/* Save / Cancel */}
        <div style={{ position: 'absolute', left: 40, top: 555, display: 'flex', alignItems: 'center', gap: 28 }}>
          <span style={{
            display: 'inline-flex', alignItems: 'center', justifyContent: 'center', width: 110, height: 44,
            background: savePressed ? '#D07A15' : ORANGE, color: '#fff', borderRadius: 4, font: `500 16px/1 ${FONT}`,
            boxShadow: '0 1px 2px rgba(0,0,0,0.15)',
          }}>Save</span>
          <span style={{ font: `500 16px/1 ${FONT}`, color: TXT }}>Cancel</span>
        </div>
        {/* Roles dropdown panel */}
        {ddOn ? (
          <div style={{
            position: 'absolute', left: 40, top: 413, width: 500, background: '#fff', borderRadius: 4,
            boxShadow: '0 4px 16px rgba(9,30,66,0.18), 0 0 1px rgba(9,30,66,0.31)', zIndex: 6,
            opacity: dd.op, transform: `translateY(${(1 - dd.op) * -6}px) scale(${0.985 + 0.015 * dd.op})`,
            transformOrigin: 'top center',
          }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 12, height: 52, padding: '0 16px', borderBottom: '1px solid #E6E8F0' }}>
              <Icon name="search" size={20} color={MUTED} />
              <span style={{ font: `400 16px/1 ${FONT}`, color: searchText ? NAVY : MUTED }}>
                {searchText || 'Search'}
                {caretOn ? <span style={{ display: 'inline-block', width: 1.5, height: 18, background: NAVY, verticalAlign: '-3px', marginLeft: 1 }} /> : null}
              </span>
              <div style={{ flex: 1 }} />
              {searchText ? <Icon name="close" size={18} color={MUTED} /> : null}
            </div>
            {items.map((it) => (
              <div key={it.label} style={{ display: 'flex', alignItems: 'center', gap: 16, height: 52, padding: '0 16px', font: `400 16px/1 ${FONT}`, color: NAVY, background: it.hover ? 'rgba(0,0,0,0.05)' : 'transparent' }}>
                <Checkbox checked={!!it.checked} />
                {it.label}
              </div>
            ))}
            <div style={{ display: 'flex', alignItems: 'center', height: 48, padding: '0 16px', borderTop: '1px solid #E6E8F0' }}>
              <span style={{ font: `400 15px/1 ${FONT}`, color: MUTED }}>{filtered ? `${checked ? 2 : 1} selected` : '1 selected'}</span>
              <div style={{ flex: 1 }} />
              <Icon name="filter_list" size={20} color={MUTED} style={{ marginRight: 24 }} />
              <Icon name="check" size={22} color={TXT} />
            </div>
          </div>
        ) : null}
      </div>
    );
  }

  // ── Cursor: waypoints + curved, damped motion ─────────────────────────
  const WAYPOINTS = [
    [0, 900, 700], [0.8, 900, 700],
    [2.3, 1552, 615], [2.9, 1552, 615],          // kebab (click 2.45)
    [3.95, 1330, 661], [4.5, 1330, 661],          // Edit item (click 4.05)
    [6.2, 742, 377], [6.9, 742, 377],             // roles arrow (click 6.45)
    [7.4, 400, 440], [8.7, 400, 440],             // hover near search while typing
    [9.15, 296, 491],                             // pass MCP Read
    [9.55, 296, 543], [10.1, 296, 543],           // MCP Manage checkbox (click 9.75)
    [11.05, 738, 593], [11.6, 738, 593],          // confirm (click 11.25)
    [12.9, 325, 577], [13.5, 325, 577],           // Save (click 13.05)
    [15.2, 1180, 655], [17.6, 1180, 655],         // rest near the new tag
    [19.2, 900, 700],
  ];
  const CLICKS = [2.45, 4.05, 6.45, 9.75, 11.25, 13.05];

  function cursorPos(t) {
    const w = WAYPOINTS;
    if (t <= w[0][0]) return { x: w[0][1], y: w[0][2] };
    for (let i = 0; i < w.length - 1; i++) {
      const [t0, x0, y0] = w[i], [t1, x1, y1] = w[i + 1];
      if (t >= t0 && t <= t1) {
        const p = easeIO((t - t0) / (t1 - t0));
        const dx = x1 - x0, dy = y1 - y0;
        const dist = Math.hypot(dx, dy);
        let x = x0 + dx * p, y = y0 + dy * p;
        if (dist > 60) {
          // gentle curved path, alternating side, strongest mid-flight
          const bow = Math.min(22, dist * 0.05) * (i % 2 ? -1 : 1);
          const s = Math.sin(Math.PI * p) * bow;
          x += (-dy / dist) * s;
          y += (dx / dist) * s;
        }
        return { x, y };
      }
    }
    const last = w[w.length - 1];
    return { x: last[1], y: last[2] };
  }

  function Cursor({ t }) {
    const { x, y } = cursorPos(t);
    let press = 1;
    for (const ct of CLICKS) {
      if (t >= ct - 0.06 && t <= ct + 0.1) press = 0.9;
      else if (t > ct + 0.1 && t <= ct + 0.22) press = 0.9 + 0.1 * easeIO((t - ct - 0.1) / 0.12);
    }
    return (
      <div style={{ position: 'absolute', left: 0, top: 0, zIndex: 50, transform: `translate(${x}px,${y}px) scale(${press})`, transformOrigin: '5px 3px', pointerEvents: 'none' }}>
        {/* macOS pointer */}
        <svg width="22" height="26" viewBox="0 0 20 24" style={{ filter: 'drop-shadow(0 1.5px 2.5px rgba(0,0,0,0.4))' }}>
          <path d="M4.5 2 L4.5 19.4 L8.7 15.7 L11.3 21.8 L14.1 20.6 L11.5 14.7 L17.2 14.5 Z"
            fill="#000" stroke="#fff" strokeWidth="1.5" strokeLinejoin="round" />
        </svg>
      </div>
    );
  }

  // ── Main scene ────────────────────────────────────────────────────────
  function SceneInner({ hotelName }) {
    const t = useTime();

    // camera
    const cx = kv(t, [[0, 800], [1.5, 800], [2.8, 1040], [4.4, 1040], [5.2, 600], [11.4, 600], [12.0, 620], [13.1, 620], [13.7, 800], [14.8, 1020], [17.2, 1020], [18.9, 800]]);
    const cy = kv(t, [[0, 450], [1.5, 450], [2.8, 590], [4.4, 590], [5.2, 450], [11.4, 450], [12.0, 480], [13.1, 480], [13.7, 490], [14.8, 575], [17.2, 575], [18.9, 450]]);
    const s = kv(t, [[0, 1], [1.5, 1], [2.8, 1.45], [4.4, 1.45], [5.2, 1.5], [11.4, 1.5], [12.0, 1.4], [13.1, 1.4], [13.7, 1.1], [14.8, 1.4], [17.2, 1.4], [18.9, 1]]);

    // screen opacities
    const listOp = kv(t, [[4.4, 1], [5.1, 0]]);
    const editOp = t < 4.4 ? 0 : kv(t, [[4.4, 0], [5.1, 1], [13.9, 1], [14.6, 0]]);
    const list2Op = t < 13.9 ? 0 : kv(t, [[13.9, 0], [14.6, 1]]);

    // row action menu
    const menu = {
      op: t < 2.5 || t > 4.35 ? 0 : kv(t, [[2.5, 0], [2.68, 1], [4.15, 1], [4.32, 0]]),
      hoverEdit: t >= 3.7,
    };

    // roles dropdown
    const dd = { op: t < 6.5 || t > 11.5 ? 0 : kv(t, [[6.5, 0], [6.72, 1], [11.25, 1], [11.44, 0]]) };
    const nChars = t < 7.5 ? 0 : t < 7.85 ? 1 : t < 8.2 ? 2 : 3;
    const searchText = 'mcp'.slice(0, nChars);
    const caretOn = dd.op > 0.5 && t >= 7.0 && (t % 0.9) < 0.5;
    const checked = t >= 9.8;
    const hoverRead = t >= 8.85 && t < 9.25;
    const hoverManage = t >= 9.35 && t < 10.1;
    const chipPop = kv(t, [[9.8, 0.8], [10.1, 1]]);
    const chipOp = t < 9.8 ? 0 : kv(t, [[9.8, 0], [10.02, 1]]);
    const savePressed = t >= 13.05 && t < 13.35;

    // toast
    const toastOp = t < 13.35 ? 0 : kv(t, [[13.35, 0], [13.62, 1], [15.3, 1], [15.85, 0]]);
    const toastY = kv(t, [[13.35, 22], [13.7, 0]]);

    // final tag reveal
    const newTag = {
      op: t < 15.0 ? 0 : kv(t, [[15.0, 0], [15.22, 1]]),
      pop: kv(t, [[15.0, 0.7], [15.24, 1.05], [15.45, 1]]),
      ring: t < 15.0 ? 0 : kv(t, [[15.0, 0], [15.2, 1], [16.2, 1], [17.4, 0]]),
    };

    return (
      <div style={{ position: 'absolute', inset: 0, overflow: 'hidden', background: '#EDEFF5' }}>
        <div style={{
          position: 'absolute', left: 0, top: 0, width: 1600, height: 900,
          transform: `translate(${800 - s * cx}px, ${450 - s * cy}px) scale(${s})`,
          transformOrigin: '0 0',
        }}>
          {listOp > 0.001 ? (
            <div style={{ position: 'absolute', inset: 0, opacity: listOp }}>
              <ListScreen hotelName={hotelName} showNewRole={false} newTag={newTag} menu={menu} />
            </div>
          ) : null}
          {editOp > 0.001 ? (
            <div style={{ position: 'absolute', inset: 0, opacity: editOp }}>
              <EditScreen hotelName={hotelName} dd={dd} searchText={searchText} caretOn={caretOn}
                checked={checked} chipPop={chipPop} chipOp={chipOp}
                hoverRead={hoverRead} hoverManage={hoverManage} savePressed={savePressed} />
            </div>
          ) : null}
          {list2Op > 0.001 ? (
            <div style={{ position: 'absolute', inset: 0, opacity: list2Op }}>
              <ListScreen hotelName={hotelName} showNewRole={true} newTag={newTag} menu={{ op: 0, hoverEdit: false }} />
            </div>
          ) : null}
          <Sidebar />
          {/* toast */}
          {toastOp > 0.001 ? (
            <div style={{
              position: 'absolute', left: 270, top: 830, transform: `translateY(${toastY}px)`, opacity: toastOp,
              background: NAVY, color: '#fff', borderRadius: 4, padding: '14px 22px',
              display: 'flex', alignItems: 'center', gap: 12, font: `400 15px/1 ${FONT}`,
              boxShadow: '0 4px 12px rgba(9,30,66,0.3)', zIndex: 40,
            }}>
              <Icon name="check_circle" size={20} color="#4ADE80" />
              User updated
            </div>
          ) : null}
          <Cursor t={t} />
        </div>
      </div>
    );
  }

  // ── Embedded player ───────────────────────────────────────────────────
  // Scales the 1600×900 scene to its container, loops a 20s timeline,
  // autoplays while in view (paused for prefers-reduced-motion), and shows
  // a slim play/pause + scrub bar.
  const DURATION = 20;
  const W = 1600, H = 900;

  function McpAccessDemo({ hotelName = 'Hotel Berlin' }) {
    const reduced = typeof matchMedia === 'function' && matchMedia('(prefers-reduced-motion: reduce)').matches;
    const [time, setTime] = useState(0);
    const [playing, setPlaying] = useState(false);
    const [scale, setScale] = useState(0);
    const hostRef = useRef(null);
    const lastTs = useRef(null);
    const startedRef = useRef(false);

    // fit scene to container width
    useEffect(() => {
      const el = hostRef.current;
      if (!el) return;
      const measure = () => setScale(el.clientWidth / W);
      measure();
      const ro = new ResizeObserver(measure);
      ro.observe(el);
      return () => ro.disconnect();
    }, []);

    // autoplay when scrolled into view (once), pause when scrolled away
    useEffect(() => {
      const el = hostRef.current;
      if (!el || reduced) return;
      const io = new IntersectionObserver(([e]) => {
        if (e.isIntersecting && !startedRef.current) { startedRef.current = true; setPlaying(true); }
        else if (!e.isIntersecting) setPlaying(false);
      }, { threshold: 0.3 });
      io.observe(el);
      return () => io.disconnect();
    }, [reduced]);

    // timeline loop
    useEffect(() => {
      if (!playing) { lastTs.current = null; return; }
      let raf;
      const step = (ts) => {
        if (lastTs.current == null) lastTs.current = ts;
        const dt = Math.min(0.1, (ts - lastTs.current) / 1000); // clamp rAF gaps (hidden tab)
        lastTs.current = ts;
        setTime((t) => (t + dt) % DURATION);
        raf = requestAnimationFrame(step);
      };
      raf = requestAnimationFrame(step);
      return () => cancelAnimationFrame(raf);
    }, [playing]);

    const seek = (e) => {
      const rect = e.currentTarget.getBoundingClientRect();
      setTime(Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width)) * DURATION);
    };

    return (
      <div ref={hostRef} style={{ position: 'relative', width: '100%', borderRadius: 8, border: '1px solid var(--border)', overflow: 'hidden', background: '#EDEFF5' }}>
        <div style={{ position: 'relative', width: '100%', paddingTop: `${(H / W) * 100}%` }}>
          <div style={{ position: 'absolute', left: 0, top: 0, width: W, height: H, transform: `scale(${scale})`, transformOrigin: '0 0' }}>
            <TimeContext.Provider value={time}>
              <SceneInner hotelName={hotelName} />
            </TimeContext.Provider>
          </div>
        </div>
        {/* controls */}
        <div style={{ position: 'absolute', left: 0, right: 0, bottom: 0, display: 'flex', alignItems: 'center', gap: 10, padding: '8px 12px', background: 'linear-gradient(transparent, rgba(25,33,59,0.55))' }}>
          <button
            type="button"
            aria-label={playing ? 'Pause demo' : 'Play demo'}
            onClick={() => setPlaying((p) => !p)}
            style={{ width: 30, height: 30, border: 'none', borderRadius: '50%', background: 'rgba(25,33,59,0.75)', color: '#fff', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 0, flex: 'none' }}
          >
            <Icon name={playing ? 'pause' : 'play_arrow'} size={18} />
          </button>
          <div onClick={seek} style={{ flex: 1, height: 18, display: 'flex', alignItems: 'center', cursor: 'pointer' }}>
            <div style={{ position: 'relative', width: '100%', height: 4, borderRadius: 2, background: 'rgba(255,255,255,0.35)' }}>
              <div style={{ position: 'absolute', left: 0, top: 0, bottom: 0, width: `${(time / DURATION) * 100}%`, borderRadius: 2, background: '#fff' }} />
            </div>
          </div>
        </div>
      </div>
    );
  }

  window.McpAccessDemo = McpAccessDemo;
})();
