// app.jsx — Recuerdas (multi-tenant)
// ─────────────────────────────────────────────────────────────────────
// Casa M Recuerdas room — a hosted, multi-tenant rituals layer.
// Each HOUSE is a private tenant. People belong to many houses. The
// brand chip top-right is a HOUSE × PERSON switcher.
//
// Architectural decisions are documented in HANDOFF.md alongside this
// file. This file is the prototype that demonstrates them.
//
// Components, top → bottom:
//   tokens   · colors, fonts, T context, langStyle helper
//   atoms    · Dot, Eyebrow, Hair, ListGlyph, HouseSeal, Candle, Bi
//   chrome   · top bar + House×Person switcher dropdown
//   rail     · left rail (Mis casas, bins, lists, invitar)
//   pane     · main pane (single-house view + Mis casas pan view)
//   aside    · right column (Las llamadas, Rituales, Libro, Quién)
//   invite   · invitation drawer (sketch)
//   app      · root component, state, Tweaks panel
// ─────────────────────────────────────────────────────────────────────

(function () {
  const { useState, useMemo, useEffect } = React;
  const {
    PEOPLE, HOUSES, LIST_TYPES, BINS, SNOOZE,
    LIFE_EVENTS, NOTIF_CHANNELS, NOTIF_DEFAULTS,
    personById, houseById,
    listsForHouse, listForHouse, membersForHouse,
    visibleRemindersFor, housesForPerson,
    subjectById, lifeEventById,
  } = window.RECUERDAS;

  // ═════════════════════════════════════════════════════════════════
  //  TOKENS · colors, fonts, language modes
  // ═════════════════════════════════════════════════════════════════
  const SERIF = "'Cormorant Garamond', 'EB Garamond', Georgia, serif";
  const SANS  = "'Manrope', ui-sans-serif, system-ui, -apple-system, sans-serif";
  const MONO  = "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace";
  const P = {
    bg: '#1A120C', bgWarm: '#221710', bgSoft: '#251A12',
    paper: '#2A1D14', paperEdge: '#3A2A1E',
    ink: '#F1E6D0', inkSoft: '#D8C9AC', inkMid: '#A89880', inkFaint: '#7A6E5A',
    gold: '#D4B07A', goldDeep: '#A87B2C', goldHi: '#E8D4A8',
    wine: '#7E1F1F', wineDeep: '#5B1313',
    ember: '#C04A2A', sage: '#5C6B4A', cellar: '#7E1F1F', cocina: '#B5651D',
    line: '#48372A', lineSoft: '#3A2A1E', lineFaint: '#2F2118',
  };

  // Persisted tweaks. The host rewrites this JSON block on disk when
  // the user changes a value, so the change survives reload.
  const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
    "language": "es-en",
    "density":  "cómoda",
    "ledger":   true,
    "rituals":  true,
    "llamadas": true
  }/*EDITMODE-END*/;
  const TweaksCtx = React.createContext({ t: TWEAK_DEFAULTS, setTweak: () => {} });

  // Language mode helper. See HANDOFF.md §Voice for the rationale.
  //   en      → English only
  //   es      → Español only
  //   en-es   → EN primary  + ES secondary
  //   es-en   → ES primary  + EN secondary  [default]
  //   ambas   → Both at equal weight
  function langStyle(mode) {
    switch (mode) {
      case 'en':    return { primary: 'en', secondary: null,  equal: false };
      case 'es':    return { primary: 'es', secondary: null,  equal: false };
      case 'en-es': return { primary: 'en', secondary: 'es',  equal: false };
      case 'ambas': return { primary: 'es', secondary: 'en',  equal: true  };
      case 'es-en':
      default:      return { primary: 'es', secondary: 'en',  equal: false };
    }
  }

  // ═════════════════════════════════════════════════════════════════
  //  ATOMS
  // ═════════════════════════════════════════════════════════════════
  function Bi({ es, en, sep = ' · ', style = {} }) {
    const T = React.useContext(TweaksCtx);
    const { primary, secondary, equal } = langStyle(T.t.language);
    if (!secondary) {
      return <span style={style}>{primary === 'es' ? es : en}</span>;
    }
    if (equal) {
      return (
        <span style={style}>
          <span>{es}</span>
          <span style={{ color: P.inkFaint, fontWeight: 'inherit' }}>{sep}</span>
          <span>{en}</span>
        </span>
      );
    }
    const p = primary === 'es' ? es : en;
    const s = secondary === 'es' ? es : en;
    return (
      <span style={style}>
        <span>{p}</span>
        <span style={{ color: P.inkFaint, fontWeight: 'inherit' }}>{sep}</span>
        <span style={{ color: P.inkMid, fontStyle: 'italic', fontFamily: SERIF }}>{s}</span>
      </span>
    );
  }

  function Dot({ id, size = 22, ring = false, title }) {
    const m = personById(id);
    return (
      <span title={title || `${m.name} · ${m.city}`} style={{
        width: size, height: size, borderRadius: 999, background: m.hex, color: '#fff',
        display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
        fontFamily: SANS, fontWeight: 700, fontSize: Math.round(size * 0.44),
        flexShrink: 0,
        boxShadow: ring ? `0 0 0 2px ${P.bg}, 0 0 0 3.5px ${m.hex}` : 'none',
      }}>{m.initial}</span>
    );
  }

  // Each House has a seal — a small wax-stamp circle in the house color.
  function HouseSeal({ houseId, size = 24, title }) {
    const h = houseById(houseId);
    const initial = h.name.replace(/^Casa\s+/, '').charAt(0);
    return (
      <span title={title || h.name} style={{
        width: size, height: size, borderRadius: 999, background: h.seal,
        color: P.bg, display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
        fontFamily: SERIF, fontStyle: 'italic', fontWeight: 600,
        fontSize: Math.round(size * 0.5), flexShrink: 0,
        border: `1px solid ${P.bg}`,
        boxShadow: `inset 0 0 0 1px ${h.accent}`,
      }}>{initial}</span>
    );
  }

  function Eyebrow({ children, color = P.gold, size = 9.5, ls = 2.4, style = {} }) {
    return <span style={{
      fontFamily: SANS, fontSize: size, letterSpacing: ls, color,
      fontWeight: 700, textTransform: 'uppercase', display: 'inline-block', ...style,
    }}>{children}</span>;
  }

  function Hair({ mt = 14, mb = 14, color = P.lineSoft, dashed = false }) {
    return <div style={{
      borderTop: `1px ${dashed ? 'dashed' : 'solid'} ${color}`,
      margin: `${mt}px 0 ${mb}px`, height: 0,
    }} />;
  }

  function ListGlyph({ which, size = 14, color = 'currentColor' }) {
    const s = { fill: 'none', stroke: color, strokeWidth: 1.5,
                strokeLinecap: 'round', strokeLinejoin: 'round' };
    if (which === 'cocina') return (
      <svg width={size} height={size} viewBox="0 0 24 24" {...s}>
        <path d="M4 10h16v6a4 4 0 0 1-4 4H8a4 4 0 0 1-4-4v-6z" />
        <path d="M8 6l1 2M12 5l1 2M16 6l1 2" /></svg>
    );
    if (which === 'cava') return (
      <svg width={size} height={size} viewBox="0 0 24 24" {...s}>
        <path d="M9 3h6v5a3 3 0 0 1-6 0V3z" /><path d="M12 11v9" /><path d="M9 20h6" /></svg>
    );
    if (which === 'casa') return (
      <svg width={size} height={size} viewBox="0 0 24 24" {...s}>
        <path d="M3 11l9-7 9 7" /><path d="M5 10v10h14V10" /></svg>
    );
    if (which === 'familia') return (
      <svg width={size} height={size} viewBox="0 0 24 24" {...s}>
        <circle cx="8" cy="10" r="3" /><circle cx="16" cy="10" r="3" />
        <path d="M3 20c0-3 2-5 5-5s5 2 5 5M11 20c0-3 2-5 5-5s5 2 5 5" /></svg>
    );
    if (which === 'jardin') return (
      <svg width={size} height={size} viewBox="0 0 24 24" {...s}>
        <path d="M12 21V9" /><path d="M12 9c-3-2-3-6 0-7 3 1 3 5 0 7z" />
        <path d="M12 14c-4 0-6-2-6-5 3 0 6 2 6 5zM12 14c4 0 6-2 6-5-3 0-6 2-6 5z" /></svg>
    );
    if (which === 'demos') return (
      <svg width={size} height={size} viewBox="0 0 24 24" {...s}>
        <rect x="3" y="6" width="14" height="12" rx="2" />
        <path d="M17 10l4-2v8l-4-2z" /></svg>
    );
    if (which === 'carinos') return (
      <svg width={size} height={size} viewBox="0 0 24 24" {...s}>
        <circle cx="9" cy="12" r="5" /><circle cx="15" cy="12" r="5" /></svg>
    );
    if (which === 'libros') return (
      <svg width={size} height={size} viewBox="0 0 24 24" {...s}>
        <path d="M4 4h6a3 3 0 0 1 3 3v13H7a3 3 0 0 1-3-3V4z" />
        <path d="M20 4h-6a3 3 0 0 0-3 3v13h6a3 3 0 0 0 3-3V4z" /></svg>
    );
    if (which === 'viajes') return (
      <svg width={size} height={size} viewBox="0 0 24 24" {...s}>
        <path d="M3 17l18-6-7-2-2-7-9 15z" /></svg>
    );
    if (which === 'meds') return (
      <svg width={size} height={size} viewBox="0 0 24 24" {...s}>
        <rect x="4" y="8" width="16" height="8" rx="4" />
        <path d="M12 8v8" /></svg>
    );
    if (which === 'salud') return (
      <svg width={size} height={size} viewBox="0 0 24 24" {...s}>
        <path d="M12 21s-7-4.5-9-9a5 5 0 0 1 9-3 5 5 0 0 1 9 3c-2 4.5-9 9-9 9z" /></svg>
    );
    if (which === 'mascotas') return (
      <svg width={size} height={size} viewBox="0 0 24 24" {...s}>
        <circle cx="7" cy="9" r="1.5" /><circle cx="17" cy="9" r="1.5" />
        <circle cx="5" cy="14" r="1.5" /><circle cx="19" cy="14" r="1.5" />
        <path d="M12 14c-3 0-5 2-5 4s2 3 5 3 5-1 5-3-2-4-5-4z" /></svg>
    );
    if (which === 'vehiculos') return (
      <svg width={size} height={size} viewBox="0 0 24 24" {...s}>
        <path d="M3 14l2-5h14l2 5v4H3v-4z" />
        <circle cx="7" cy="18" r="1.5" /><circle cx="17" cy="18" r="1.5" /></svg>
    );
    if (which === 'hogar') return (
      <svg width={size} height={size} viewBox="0 0 24 24" {...s}>
        <path d="M3 11l9-7 9 7" /><path d="M5 10v10h14V10" />
        <path d="M10 14h4v6h-4z" /></svg>
    );
    if (which === 'ninos') return (
      <svg width={size} height={size} viewBox="0 0 24 24" {...s}>
        <circle cx="12" cy="7" r="3" />
        <path d="M6 21c0-4 3-7 6-7s6 3 6 7" />
        <path d="M10 13s.5 2 2 2 2-2 2-2" /></svg>
    );
    if (which === 'cuentas') return (
      <svg width={size} height={size} viewBox="0 0 24 24" {...s}>
        <rect x="3" y="6" width="18" height="12" rx="2" />
        <path d="M3 10h18" /></svg>
    );
    if (which === 'trabajo') return (
      <svg width={size} height={size} viewBox="0 0 24 24" {...s}>
        <rect x="3" y="7" width="18" height="13" rx="2" />
        <path d="M9 7V5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2v2" /></svg>
    );
    if (which === 'hobby') return (
      <svg width={size} height={size} viewBox="0 0 24 24" {...s}>
        <path d="M12 2l3 7h7l-5.5 4.5L18 21l-6-4-6 4 1.5-7.5L2 9h7z" /></svg>
    );
    return null;
  }

  // Subject avatar (pet / vehicle / kid) — emoji-led, in subject hex.
  function SubjectAvatar({ subject, size = 22 }) {
    if (!subject) return null;
    return (
      <span title={`${subject.name} · ${subject.kind}`} style={{
        width: size, height: size, borderRadius: 6,
        background: subject.hex || P.gold,
        display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
        fontSize: Math.round(size * 0.6), flexShrink: 0,
        border: `1px solid ${P.bg}`,
      }}>{subject.emoji || subject.name[0]}</span>
    );
  }

  function Candle({ done, onClick, accent = P.gold, size = 22 }) {
    return (
      <button onClick={onClick} aria-label={done ? 'Mark undone' : 'Mark done'}
        style={{
          width: size, height: size, padding: 0, cursor: 'pointer', flexShrink: 0,
          border: `1.5px solid ${done ? accent : P.line}`,
          borderRadius: 999, background: done ? `${accent}28` : 'transparent',
          display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
          transition: 'background 160ms, border-color 160ms, transform 120ms',
        }}>
        {done ? (
          <svg width={size * 0.55} height={size * 0.55} viewBox="0 0 24 24">
            <path d="M5 12l4 4 10-10" fill="none" stroke={accent}
                  strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round" />
          </svg>
        ) : null}
      </button>
    );
  }

  // ─── Time helpers ─────────────────────────────────────────────────
  function fmtTime(iso, tz) {
    return new Date(iso).toLocaleTimeString('en-US', {
      hour: 'numeric', minute: '2-digit', hour12: true, timeZone: tz,
    }).replace(/\u202f/g, ' ');
  }
  function fmtDay(iso) {
    const d = new Date(iso); const n = new Date();
    const dayMs = 1000 * 60 * 60 * 24;
    const diff = Math.round((d.setHours(0,0,0,0) - n.setHours(0,0,0,0)) / dayMs);
    if (diff === 0)  return 'Hoy';
    if (diff === 1)  return 'Mañana';
    if (diff === -1) return 'Ayer';
    if (diff > 1 && diff < 7)
      return new Date(iso).toLocaleDateString('en-US', { weekday: 'long' });
    return new Date(iso).toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
  }
  function isToday(iso) {
    const d = new Date(iso); const n = new Date();
    return d.getFullYear() === n.getFullYear()
        && d.getMonth() === n.getMonth()
        && d.getDate() === n.getDate();
  }
  function timeOfDay(iso) {
    const h = new Date(iso).getHours();
    if (h < 11) return 'mañana';
    if (h < 17) return 'tarde';
    return 'noche';
  }

  // ═════════════════════════════════════════════════════════════════
  //  CHROME · top bar + House × Person switcher
  // ═════════════════════════════════════════════════════════════════
  function Chrome({ personId, houseId, onSwitch, onSetup, onAjustes, children }) {
    const [open, setOpen] = useState(false);
    const me = personById(personId);
    const house = houseById(houseId);
    const myHouses = housesForPerson(personId);
    const role = house.memberships.find(m => m.personId === personId)?.role;

    // Close switcher when clicking outside
    useEffect(() => {
      if (!open) return;
      const handler = (e) => {
        if (!e.target.closest('[data-switcher]')) setOpen(false);
      };
      window.addEventListener('mousedown', handler);
      return () => window.removeEventListener('mousedown', handler);
    }, [open]);

    return (
      <div style={{
        minHeight: '100vh', background: P.bg, color: P.ink,
        fontFamily: SANS, position: 'relative', overflow: 'hidden',
      }}>
        {/* still-life ambient gradient — house accent tints the corner */}
        <div aria-hidden style={{
          position: 'fixed', inset: 0, pointerEvents: 'none', zIndex: 0,
          background: [
            `radial-gradient(ellipse 520px 380px at 92% 88%, ${house.seal}30 0%, ${house.seal}0a 26%, transparent 60%)`,
            `radial-gradient(ellipse 110px 460px at 10% 60%, ${house.accent}3a 0%, transparent 70%)`,
            `radial-gradient(ellipse 300px 80px at 80% 18%, ${P.cocina}2a 0%, transparent 70%)`,
          ].join(', '),
          transition: 'background 600ms ease',
        }} />
        {/* paper grain */}
        <div aria-hidden style={{
          position: 'fixed', inset: 0, pointerEvents: 'none', zIndex: 1, opacity: 0.16,
          mixBlendMode: 'overlay',
          backgroundImage: `url("data:image/svg+xml,%3Csvg viewBox='0 0 240 240' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence baseFrequency='0.9' numOctaves='2'/%3E%3CfeColorMatrix values='0 0 0 0 0.85 0 0 0 0 0.78 0 0 0 0 0.65 0 0 0 0.5 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E")`,
        }} />

        {/* Top brand bar */}
        <header style={{
          position: 'relative', zIndex: 3,
          display: 'flex', alignItems: 'center', gap: 14,
          padding: '18px clamp(22px, 3vw, 36px)',
          borderBottom: `1px solid ${P.line}`,
          background: 'rgba(0,0,0,0.18)',
        }}>
          <a href="https://casam.me" style={{
            display: 'flex', alignItems: 'center', gap: 12, textDecoration: 'none',
          }}>
            <img src="brand/casam-logo-white.png" alt=""
                 width={36} height={36} style={{ display: 'block' }} />
            <span style={{ display: 'flex', flexDirection: 'column', lineHeight: 1 }}>
              <span style={{
                fontFamily: SERIF, fontStyle: 'italic', fontWeight: 600,
                fontSize: 19, color: P.gold,
              }}>Casa M</span>
              <Eyebrow color={P.inkMid} size={8.5} ls={2.2} style={{ marginTop: 3 }}>
                multi-tenant rituals
              </Eyebrow>
            </span>
          </a>
          <span style={{ width: 1, height: 28, background: P.line, margin: '0 8px' }} />
          {/* Room name */}
          <span style={{ display: 'flex', flexDirection: 'column', lineHeight: 1.05 }}>
            <span style={{
              fontFamily: SERIF, fontStyle: 'italic', fontWeight: 600,
              fontSize: 22, color: P.ink, letterSpacing: -0.4,
            }}>Recuerdas<span style={{ color: P.gold }}>?</span></span>
            <Eyebrow color={P.gold} size={8.5} ls={2.4} style={{ marginTop: 3 }}>
              · {house.name}
            </Eyebrow>
          </span>
          <span style={{ flex: 1 }} />

          {/* room peers — these live in every house, externally */}
          <nav style={{ display: 'flex', gap: 4, alignItems: 'center' }}>
            {[
              { id: 'cellar', label: 'Cellar', href: 'https://cellar.casam.me', c: P.wine },
              { id: 'cocina', label: 'Cocina', href: 'https://cocina.casam.me', c: P.cocina },
              { id: 'demos',  label: 'Demos',  href: 'https://demos.cmsco.io',  c: '#6FA67A' },
            ].map((r) => (
              <a key={r.id} href={r.href} target="_blank" rel="noopener" style={{
                padding: '6px 10px', borderRadius: 4, textDecoration: 'none',
                fontFamily: SANS, fontSize: 9, letterSpacing: 1.8, fontWeight: 700,
                color: P.inkMid, textTransform: 'uppercase',
              }} onMouseEnter={(e) => e.currentTarget.style.color = r.c}
                 onMouseLeave={(e) => e.currentTarget.style.color = P.inkMid}>
                {r.label}<span style={{ marginLeft: 4, opacity: 0.5 }}>↗</span>
              </a>
            ))}
          </nav>

          {/* House × Person switcher */}
          <div data-switcher style={{ position: 'relative' }}>
            <button onClick={() => setOpen(o => !o)} style={{
              display: 'inline-flex', alignItems: 'center', gap: 10,
              padding: '6px 12px 6px 8px', cursor: 'pointer',
              border: `1px solid ${P.line}`, borderRadius: 999,
              background: 'rgba(0,0,0,0.22)', color: 'inherit', font: 'inherit',
            }}>
              <HouseSeal houseId={houseId} size={28} />
              <div style={{ display: 'flex', flexDirection: 'column',
                            alignItems: 'flex-start', lineHeight: 1.05 }}>
                <span style={{
                  fontFamily: SERIF, fontStyle: 'italic', fontSize: 14,
                  color: P.inkSoft,
                }}>{me.name}<span style={{ color: P.inkMid }}> · {me.city}</span></span>
                <Eyebrow color={house.seal} size={8} ls={1.8} style={{ marginTop: 3 }}>
                  {house.name} · {role}
                </Eyebrow>
              </div>
              <Dot id={personId} size={26} />
              <span style={{ color: P.inkFaint, fontSize: 12, marginLeft: -2 }}>▾</span>
            </button>
            {open && (
              <SwitcherPanel personId={personId} myHouses={myHouses}
                currentHouseId={houseId}
                onPick={(nextHouseId, nextPersonId) => {
                  onSwitch(nextPersonId, nextHouseId);
                  setOpen(false);
                }}
                onSetup={() => { setOpen(false); onSetup(); }}
                onAjustes={() => { setOpen(false); onAjustes(); }} />
            )}
          </div>
        </header>
        {children}
      </div>
    );
  }

  function SwitcherPanel({ personId, currentHouseId, myHouses, onPick, onSetup, onAjustes }) {
    const me = personById(personId);
    return (
      <div style={{
        position: 'absolute', top: 'calc(100% + 8px)', right: 0,
        zIndex: 50, width: 340,
        background: P.bgWarm, border: `1px solid ${P.line}`,
        borderRadius: 8, boxShadow: '0 16px 40px rgba(0,0,0,0.5)',
        padding: 14, display: 'flex', flexDirection: 'column', gap: 14,
      }}>
        <div>
          <Eyebrow color={P.inkMid} size={8.5} ls={2}>Signed in as · Como</Eyebrow>
          <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginTop: 8 }}>
            <Dot id={personId} size={32} />
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{
                fontFamily: SERIF, fontStyle: 'italic', fontSize: 18, color: P.ink,
              }}>{me.name}</div>
              <div style={{
                fontFamily: MONO, fontSize: 9, color: P.inkMid,
                letterSpacing: 1.4, fontWeight: 600, marginTop: 2,
              }}>{me.city} · {me.tz.split('/').pop().replace('_', ' ')}</div>
            </div>
          </div>
        </div>
        <Hair mt={0} mb={0} />
        <div>
          <Eyebrow color={P.gold} size={8.5} ls={2}>
            Switch house · Cambiar de casa
          </Eyebrow>
          <div style={{ display: 'flex', flexDirection: 'column',
                        marginTop: 8, gap: 2 }}>
            {myHouses.map(h => {
              const role = h.memberships.find(m => m.personId === personId)?.role;
              const active = h.id === currentHouseId;
              return (
                <button key={h.id} onClick={() => onPick(h.id, personId)} style={{
                  display: 'flex', alignItems: 'center', gap: 10, padding: '8px',
                  background: active ? 'rgba(255,255,255,0.06)' : 'transparent',
                  border: 'none', borderRadius: 4, cursor: 'pointer',
                  borderLeft: `3px solid ${active ? h.seal : 'transparent'}`,
                  color: 'inherit', font: 'inherit', textAlign: 'left',
                }}>
                  <HouseSeal houseId={h.id} size={28} />
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{
                      fontFamily: SERIF, fontStyle: 'italic', fontSize: 15,
                      color: active ? P.ink : P.inkSoft, lineHeight: 1.1,
                    }}>{h.name}</div>
                    <div style={{
                      fontFamily: SANS, fontSize: 8.5, letterSpacing: 1.6,
                      color: h.seal, fontWeight: 700, textTransform: 'uppercase',
                      marginTop: 3,
                    }}>{role} · {h.memberships.length} miembros</div>
                  </div>
                </button>
              );
            })}
          </div>
        </div>
        <Hair mt={0} mb={0} />
        <div>
          <Eyebrow color={P.inkMid} size={8.5} ls={2}>Demo · view as</Eyebrow>
          <div style={{ display: 'flex', flexWrap: 'wrap',
                        marginTop: 8, gap: 4 }}>
            {PEOPLE.filter(p => p.id !== 'casa').map(p => (
              <button key={p.id} onClick={() => onPick(currentHouseId, p.id)}
                title={`View as ${p.name}`} style={{
                  display: 'inline-flex', alignItems: 'center', gap: 6,
                  padding: '4px 8px 4px 4px', borderRadius: 999,
                  background: p.id === personId ? `${p.hex}22` : 'transparent',
                  border: `1px solid ${p.id === personId ? p.hex : P.lineSoft}`,
                  cursor: 'pointer', color: 'inherit', font: 'inherit',
                }}>
                <Dot id={p.id} size={20} />
                <span style={{
                  fontFamily: SANS, fontSize: 9, letterSpacing: 1.4,
                  fontWeight: 600, textTransform: 'uppercase',
                }}>{p.name}</span>
              </button>
            ))}
          </div>
        </div>
        <Hair mt={0} mb={0} />
        <div style={{ display: 'flex', gap: 6 }}>
          <button onClick={onSetup} style={{
            flex: 1, padding: '10px 12px', cursor: 'pointer',
            background: 'transparent', color: P.gold,
            border: `1px solid ${P.lineSoft}`, borderRadius: 4,
            fontFamily: SANS, fontSize: 10, letterSpacing: 1.8, fontWeight: 700,
            textTransform: 'uppercase', textAlign: 'left',
          }}>Setup · Configurar</button>
          <button onClick={onAjustes} style={{
            flex: 1, padding: '10px 12px', cursor: 'pointer',
            background: 'transparent', color: P.gold,
            border: `1px solid ${P.lineSoft}`, borderRadius: 4,
            fontFamily: SANS, fontSize: 10, letterSpacing: 1.8, fontWeight: 700,
            textTransform: 'uppercase', textAlign: 'left',
          }}>Ajustes · Settings</button>
        </div>
      </div>
    );
  }

  // ═════════════════════════════════════════════════════════════════
  //  RAIL · left sidebar (Mis casas, bins, lists, invitar)
  // ═════════════════════════════════════════════════════════════════
  function LeftRail({ house, viewerPersonId, active, setActive, counts, onInvite }) {
    const T = React.useContext(TweaksCtx);
    const lists = listsForHouse(house, viewerPersonId);
    return (
      <aside style={{
        width: 260, flexShrink: 0,
        padding: '24px 16px 24px 24px',
        borderRight: `1px solid ${P.line}`,
        position: 'sticky', top: 73, alignSelf: 'flex-start',
        maxHeight: 'calc(100vh - 73px)', overflowY: 'auto',
      }}>
        <Eyebrow color={P.inkMid} size={9} ls={2.2}>
          Las vistas · the views
        </Eyebrow>
        <nav style={{ display: 'flex', flexDirection: 'column',
                      marginTop: 10, gap: 1 }}>
          {BINS.map((b) => (
            <RailRow key={b.id} active={active.kind === 'bin' && active.id === b.id}
              onClick={() => setActive({ kind: 'bin', id: b.id })}
              count={counts.bins[b.id]} accent={b.panHouse ? house.seal : P.gold}
              labelEs={b.label} labelEn={b.en}
              icon={b.panHouse ? 'panhouse' : null}
              dot={b.id === 'today' ? 'pulse' : null}
              prominent={b.panHouse} />
          ))}
        </nav>

        <Eyebrow color={P.inkMid} size={9} ls={2.2} style={{ marginTop: 22 }}>
          Los rincones · the corners
        </Eyebrow>
        <nav style={{ display: 'flex', flexDirection: 'column',
                      marginTop: 10, gap: 1 }}>
          {lists.map((l) => (
            <RailRow key={l.id} active={active.kind === 'list' && active.id === l.id}
              onClick={() => setActive({ kind: 'list', id: l.id })}
              count={counts.lists[l.id]} accent={l.hex}
              icon={l.icon} labelEs={l.label} labelEn={l.en}
              scopeLabel={l.scopeLabel} />
          ))}
        </nav>

        {/* Invitar affordance */}
        <button onClick={onInvite} style={{
          marginTop: 18, padding: '10px 12px', cursor: 'pointer',
          background: 'transparent', color: P.inkMid,
          border: `1px dashed ${P.line}`, borderRadius: 6,
          fontFamily: SANS, fontSize: 10, letterSpacing: 2, fontWeight: 700,
          textTransform: 'uppercase', textAlign: 'left',
          display: 'flex', alignItems: 'center', justifyContent: 'space-between',
          width: '100%',
        }}>
          <span><Bi es="Invitar a la casa" en="Invite to this house" /></span>
          <span style={{ color: P.gold, fontSize: 16, lineHeight: 1 }}>+</span>
        </button>

        <a href="https://casam.me" style={{
          display: 'block', marginTop: 24, padding: '14px 14px',
          background: 'rgba(0,0,0,0.18)',
          border: `1px solid ${P.lineSoft}`, borderLeft: `2px solid ${P.gold}`,
          borderRadius: 4, textDecoration: 'none', color: 'inherit',
        }}>
          <Eyebrow color={P.gold} size={8.5} ls={2}>
            ← <Bi es="Volver a la casa" en="Back to the front door" />
          </Eyebrow>
          <div style={{
            fontFamily: SERIF, fontStyle: 'italic', fontSize: 15, color: P.inkSoft,
            marginTop: 6, lineHeight: 1.3,
          }}>casam.me</div>
        </a>
      </aside>
    );
  }

  function RailRow({ active, onClick, count, accent, icon, labelEs, labelEn,
                    dot, scopeLabel, prominent }) {
    const T = React.useContext(TweaksCtx);
    const { secondary, primary } = langStyle(T.t.language);
    return (
      <button onClick={onClick} style={{
        display: 'flex', alignItems: 'center', gap: 10, width: '100%',
        padding: prominent ? '11px 10px' : '9px 10px',
        borderRadius: 4, cursor: 'pointer',
        background: active ? 'rgba(255,255,255,0.05)' : 'transparent',
        border: 'none', borderLeft: `3px solid ${active ? accent : 'transparent'}`,
        color: active ? P.ink : P.inkSoft, textAlign: 'left',
        font: 'inherit',
      }}>
        {icon === 'panhouse' ? (
          <svg width={15} height={15} viewBox="0 0 24 24" fill="none"
               stroke={accent} strokeWidth="1.5" strokeLinecap="round">
            <circle cx="7" cy="9" r="4" />
            <circle cx="17" cy="9" r="4" />
            <circle cx="12" cy="17" r="4" />
          </svg>
        ) : icon
          ? <ListGlyph which={icon} size={15} color={accent} />
          : <span style={{
              width: 8, height: 8, borderRadius: 999,
              background: active ? accent : P.inkMid, flexShrink: 0,
              animation: dot === 'pulse' ? 'pulseDot 2.4s ease-in-out infinite' : 'none',
            }} />}
        <span style={{
          fontFamily: SANS, fontSize: prominent ? 11.5 : 11, fontWeight: 700,
          letterSpacing: 1.4, textTransform: 'uppercase', flex: 1,
        }}>{labelEs}</span>
        {secondary && (
          <span style={{
            fontFamily: SERIF, fontStyle: 'italic', fontSize: 11, color: P.inkFaint,
          }}>{primary === 'es' ? labelEn : labelEs}</span>
        )}
        {scopeLabel && (
          <span title={`Scoped to ${scopeLabel}`} style={{
            fontFamily: MONO, fontSize: 9, color: accent, marginLeft: 2,
          }}>◐</span>
        )}
        {count !== undefined && count !== null && (
          <span style={{
            fontFamily: MONO, fontSize: 10, color: active ? accent : P.inkMid,
            fontWeight: 600,
          }}>{count}</span>
        )}
      </button>
    );
  }

  // ═════════════════════════════════════════════════════════════════
  //  PANE · main area (single-house view + Mis casas pan view)
  // ═════════════════════════════════════════════════════════════════
  function ReminderRow({ r, house, viewerPersonId, onToggle, density,
                         showHouseBadge }) {
    const T = React.useContext(TweaksCtx);
    const m = personById(r.who);
    const list = listForHouse(house, r.list, viewerPersonId);
    const giver = r.from ? personById(r.from) : null;
    const originHouse = r.originHouse ? houseById(r.originHouse) : null;
    const subject = r.subjectId ? subjectById(house, r.subjectId) : null;
    const isTravelAware = r.tz_priority === 'current';
    const pad = density === 'compacta' ? '8px 0' : '13px 0';
    const isSecret = r.secret && r.who === viewerPersonId && !r.done;
    const { primary, secondary } = langStyle(T.t.language);
    const pickTitle = (which) =>
      which === 'es' ? (r.titleEs || r.title) : r.title;
    const titleStr = isSecret
      ? '· · ·  un cariño en camino  · · ·'
      : pickTitle(primary);
    const showSubtitle = !isSecret && secondary
      && r.title && r.titleEs && r.title !== r.titleEs;
    return (
      <div style={{
        display: 'flex', alignItems: 'flex-start', gap: 14,
        padding: pad, borderBottom: `1px dashed ${P.lineFaint}`,
      }}>
        <Candle done={r.done} accent={list.hex}
          onClick={() => onToggle(r.id)} size={22} />
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ display: 'flex', alignItems: 'baseline', gap: 10,
                        flexWrap: 'wrap' }}>
            {isSecret && (
              <span title="A secret cariño" style={{
                display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
                width: 20, height: 20, borderRadius: 999,
                background: `${list.hex}33`, border: `1px solid ${list.hex}88`,
                color: list.hex, fontSize: 11, fontFamily: SERIF,
                fontStyle: 'italic', fontWeight: 600,
              }}>✦</span>
            )}
            <span style={{
              fontFamily: SERIF, fontStyle: 'italic', fontWeight: 500,
              fontSize: density === 'compacta' ? 16 : 18.5,
              color: r.done ? P.inkFaint : (isSecret ? list.hex : P.ink),
              textDecoration: r.done ? 'line-through' : 'none',
              textDecorationColor: P.goldDeep,
              lineHeight: 1.2,
              letterSpacing: isSecret ? 0.4 : 'normal',
            }}>{titleStr}</span>
            {showSubtitle && (
              <span style={{
                fontFamily: SERIF, fontStyle: 'italic', fontSize: 13,
                color: P.inkMid, lineHeight: 1.2,
              }}>· {pickTitle(secondary)}</span>
            )}
          </div>
          {/* Meta row */}
          <div style={{
            display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap',
            marginTop: 6,
          }}>
            {/* House badge — shows in Mis casas pan view */}
            {showHouseBadge && (
              <span style={{
                display: 'inline-flex', alignItems: 'center', gap: 5,
              }}>
                <HouseSeal houseId={house.id} size={16} />
                <span style={{
                  fontFamily: SANS, fontSize: 9, letterSpacing: 1.6, fontWeight: 700,
                  color: house.seal, textTransform: 'uppercase',
                }}>{house.name.replace(/^Casa\s+/, '')}</span>
              </span>
            )}
            <span style={{
              display: 'inline-flex', alignItems: 'center', gap: 5,
              padding: '2px 7px 2px 6px', borderRadius: 3,
              background: `${list.hex}22`,
              border: `1px solid ${list.hex}55`,
              fontFamily: SANS, fontSize: 8.5, letterSpacing: 1.6, fontWeight: 700,
              color: list.hex, textTransform: 'uppercase',
            }}>
              <ListGlyph which={list.icon} size={10} color={list.hex} />
              {list.label}
            </span>
            {r.due && (
              <span style={{
                fontFamily: MONO, fontSize: 10, color: P.inkMid, fontWeight: 600,
                letterSpacing: 1.2, textTransform: 'uppercase',
              }}>
                {fmtDay(r.due)} · {fmtTime(r.due, m.tz)}{' '}
                {m.id !== 'casa' && (
                  <span style={{ color: P.inkFaint }}>{m.city}</span>
                )}
              </span>
            )}
            {r.recur && (
              <span style={{
                display: 'inline-flex', alignItems: 'center', gap: 4,
                fontFamily: SERIF, fontStyle: 'italic', fontSize: 12.5,
                color: P.gold,
              }}>
                <span style={{ fontSize: 11 }}>↻</span>
                {primary === 'en' ? r.recur.labelEn : r.recur.label}
              </span>
            )}
            {r.streak != null && r.streak > 0 && (
              <span style={{
                display: 'inline-flex', alignItems: 'center', gap: 4,
                fontFamily: MONO, fontSize: 10, color: P.goldHi, fontWeight: 600,
                letterSpacing: 1, padding: '2px 6px',
                background: `${P.gold}1A`, borderRadius: 2,
                border: `1px solid ${P.goldDeep}55`,
              }}>
                ✦ {r.streak}<span style={{ color: P.goldDeep }}>×</span>
              </span>
            )}
            {r.tied && (
              <span style={{
                display: 'inline-flex', alignItems: 'center', gap: 4,
                fontFamily: SANS, fontSize: 9, color: P.inkMid, fontWeight: 600,
                letterSpacing: 1.4, textTransform: 'uppercase',
              }}>
                <span style={{ color: P.inkFaint, fontStyle: 'italic',
                  textTransform: 'none', fontFamily: SERIF, fontSize: 12 }}>
                  {r.tied.verb}
                </span>
                <span style={{
                  color: r.tied.room === 'cellar' ? P.wine
                    : r.tied.room === 'cocina' ? P.cocina
                    : r.tied.room === 'demos' ? '#6FA67A' : P.ember,
                }}>{r.tied.label} ↗</span>
              </span>
            )}
            {/* Subject (pet/vehicle/kid) chip beside the title */}
            {subject && !isSecret && (
              <span style={{
                display: 'inline-flex', alignItems: 'center', gap: 5,
                fontFamily: SERIF, fontStyle: 'italic', fontSize: 13,
                color: P.inkSoft,
              }}>
                <SubjectAvatar subject={subject} size={18} />
                <span>{subject.name}</span>
              </span>
            )}
            {/* tz_priority badge — safety-critical when traveling */}
            {isTravelAware && !r.done && (
              <span title="This reminder follows the assignee's current device timezone" style={{
                display: 'inline-flex', alignItems: 'center', gap: 4,
                padding: '2px 7px', borderRadius: 3,
                background: `${P.ember}1F`,
                border: `1px solid ${P.ember}55`,
                fontFamily: SANS, fontSize: 8.5, letterSpacing: 1.4,
                color: P.ember, fontWeight: 700, textTransform: 'uppercase',
              }}>⚛ local · travels with you</span>
            )}
            {/* Cariño · "from" giver (with origin house if cross-house) */}
            {giver && !isSecret && (
              <span style={{
                display: 'inline-flex', alignItems: 'center', gap: 5,
                fontFamily: SERIF, fontStyle: 'italic', fontSize: 12.5,
                color: P.inkMid,
              }}>
                <span style={{ color: P.inkFaint }}>de</span>
                <Dot id={giver.id} size={16} />
                <span>{giver.name}</span>
                {originHouse && (
                  <>
                    <span style={{ color: P.inkFaint }}>·</span>
                    <HouseSeal houseId={originHouse.id} size={14} />
                    <span style={{ color: originHouse.seal, fontSize: 11 }}>
                      from {originHouse.name.replace(/^Casa\s+/, '')}
                    </span>
                  </>
                )}
              </span>
            )}
            {isSecret && (
              <span style={{
                display: 'inline-flex', alignItems: 'center', gap: 4,
                fontFamily: SANS, fontSize: 9, color: list.hex, fontWeight: 700,
                letterSpacing: 1.6, textTransform: 'uppercase',
              }}>
                ✉ sellado · sealed
                {originHouse && (
                  <>
                    {' · '}
                    <HouseSeal houseId={originHouse.id} size={14} />
                  </>
                )}
              </span>
            )}
          </div>
          {r.note && !isSecret && density !== 'compacta' && (
            <div style={{
              fontFamily: SERIF, fontStyle: 'italic', fontSize: 13,
              color: P.inkMid, marginTop: 6, lineHeight: 1.4,
              paddingLeft: 12, borderLeft: `2px solid ${P.goldDeep}`,
            }}>{r.note}</div>
          )}
        </div>
        {/* Assignee */}
        <div style={{
          display: 'flex', flexDirection: 'column', alignItems: 'flex-end', gap: 4,
        }}>
          <Dot id={r.who} size={28} />
          <span style={{
            fontFamily: SANS, fontSize: 8.5, color: P.inkMid, fontWeight: 700,
            letterSpacing: 1.4, textTransform: 'uppercase',
          }}>{m.name}</span>
        </div>
      </div>
    );
  }

  function QuickAdd({ house, viewerPersonId, onAdd, defaultList = 'casa' }) {
    const T = React.useContext(TweaksCtx);
    const { primary } = langStyle(T.t.language);
    const [val, setVal] = useState('');
    const lists = listsForHouse(house, viewerPersonId);
    const [list, setList] = useState(
      lists.find(l => l.id === defaultList)?.id || lists[0]?.id);
    function submit() {
      const v = val.trim(); if (!v) return;
      onAdd({ title: v, titleEs: v, list, who: viewerPersonId,
              due: null, recur: null });
      setVal('');
    }
    const l = listForHouse(house, list, viewerPersonId);
    return (
      <div style={{
        display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap',
        padding: '14px 16px', borderRadius: 6,
        background: 'rgba(0,0,0,0.18)',
        border: `1px solid ${P.lineSoft}`,
        borderLeft: `3px solid ${l.hex}`,
      }}>
        <span style={{
          width: 22, height: 22, borderRadius: 999,
          border: `1.5px solid ${P.line}`, flexShrink: 0,
        }} />
        <input value={val} onChange={(e) => setVal(e.target.value)}
          onKeyDown={(e) => e.key === 'Enter' && submit()}
          placeholder={primary === 'en'
            ? 'Jot something for the house'
            : 'Anota algo nuevo para la casa'}
          style={{
            flex: 1, minWidth: 140,
            background: 'transparent', border: 'none', outline: 'none',
            fontFamily: SERIF, fontStyle: 'italic', fontSize: 18, color: P.ink,
          }} />
        <select value={list} onChange={(e) => setList(e.target.value)}
          style={{
            background: 'transparent', color: P.gold, border: `1px solid ${P.lineSoft}`,
            borderRadius: 3, padding: '4px 8px',
            fontFamily: SANS, fontSize: 9.5, letterSpacing: 1.6, fontWeight: 700,
            textTransform: 'uppercase', cursor: 'pointer',
          }}>
          {lists.map(item => (
            <option key={item.id} value={item.id}
                    style={{ background: P.bg, color: P.ink }}>{item.label}</option>
          ))}
        </select>
        <button onClick={submit} style={{
          padding: '9px 14px', cursor: 'pointer',
          background: P.gold, color: P.bg, border: 'none', borderRadius: 3,
          fontFamily: SANS, fontSize: 10, letterSpacing: 2, fontWeight: 700,
          textTransform: 'uppercase',
        }}>
          <Bi es="Anotar" en="Add" sep=" · " />
        </button>
      </div>
    );
  }

  function EmptyState({ title, sub }) {
    return (
      <div style={{
        padding: '40px 20px', textAlign: 'center', borderRadius: 6,
        border: `1px dashed ${P.line}`, background: 'rgba(0,0,0,0.12)',
      }}>
        <div style={{
          fontFamily: SERIF, fontStyle: 'italic', fontSize: 22, color: P.gold,
        }}>{title}</div>
        <div style={{
          fontFamily: SERIF, fontStyle: 'italic', fontSize: 14, color: P.inkMid,
          marginTop: 6,
        }}>{sub}</div>
      </div>
    );
  }

  function MainPane({ house, viewerPersonId, active, reminders, onToggle, onAdd }) {
    const T = React.useContext(TweaksCtx);
    const { primary } = langStyle(T.t.language);

    // Pan-house mode
    if (active.kind === 'bin' && active.id === 'mis-casas') {
      return <MisCasasPane personId={viewerPersonId} onToggle={onToggle} />;
    }

    // Header
    let title, subtitle;
    if (active.kind === 'bin') {
      const b = BINS.find(x => x.id === active.id);
      title = b.label; subtitle = b.en;
    } else {
      const l = listForHouse(house, active.id, viewerPersonId);
      title = l.label; subtitle = l.en;
    }
    const heading = active.kind === 'bin' && active.id === 'today'
      ? '¿Recuerdas?' : title;
    const subhead = active.kind === 'bin' && active.id === 'today'
      ? { es: `Hoy en ${house.name}`, en: `Today in ${house.name}` }
      : { es: title, en: subtitle };

    const filtered = useMemo(() => {
      if (active.kind === 'list') {
        return reminders.filter(r => r.list === active.id && !r.done);
      }
      switch (active.id) {
        case 'today':     return reminders.filter(r =>
                            !r.done && r.due && isToday(r.due));
        case 'tomorrow':  return reminders.filter(r => {
          if (r.done || !r.due) return false;
          const d = new Date(r.due);
          const t = new Date(); t.setDate(t.getDate() + 1);
          return d.toDateString() === t.toDateString();
        });
        case 'week': {
          const now = new Date();
          const end = new Date(now); end.setDate(end.getDate() + 7);
          return reminders.filter(r => !r.done && r.due
                                       && new Date(r.due) <= end);
        }
        case 'scheduled': return reminders.filter(r => !r.done && r.due);
        case 'rituals':   return reminders.filter(r => !r.done && r.recur);
        case 'assigned':  return reminders.filter(r =>
                            !r.done && r.who === viewerPersonId);
        case 'done':      return reminders.filter(r => r.done);
        default:          return reminders;
      }
    }, [active, reminders, viewerPersonId]);

    const grouped = useMemo(() => {
      if (active.kind !== 'bin' || active.id !== 'today') {
        return [{ key: '', items: filtered }];
      }
      const order = ['mañana', 'tarde', 'noche'];
      const buckets = { 'mañana': [], 'tarde': [], 'noche': [] };
      filtered.forEach(r => {
        if (!r.due) return;
        buckets[timeOfDay(r.due)].push(r);
      });
      return order.filter(k => buckets[k].length).map(k => ({
        key: k, items: buckets[k].sort((a, b) =>
          new Date(a.due) - new Date(b.due)),
      }));
    }, [filtered, active]);

    return (
      <main style={{ flex: 1, minWidth: 0, padding: '28px clamp(20px, 3vw, 40px)' }}>
        <div style={{ display: 'flex', alignItems: 'baseline', gap: 16,
                      flexWrap: 'wrap' }}>
          <h1 style={{
            margin: 0, fontFamily: SERIF, fontStyle: 'italic', fontWeight: 500,
            fontSize: 'clamp(40px, 4.6vw, 62px)', color: P.ink, lineHeight: 0.95,
            letterSpacing: -0.8,
          }}>{heading}</h1>
          <div style={{ display: 'flex', flexDirection: 'column' }}>
            <Eyebrow color={P.gold} size={10.5} ls={3}>
              <Bi es={subhead.es} en={subhead.en} />
            </Eyebrow>
            <span style={{
              fontFamily: MONO, fontSize: 10, letterSpacing: 1.6,
              color: P.inkMid, marginTop: 4, fontWeight: 600,
              textTransform: 'uppercase',
            }}>
              {filtered.length}{' '}<Bi es="pendientes" en="pending" sep=" / " />
              {' · '}
              {reminders.filter(r => r.done).length}{' '}
              <Bi es="cumplidos hoy" en="done today" sep=" / " />
            </span>
          </div>
        </div>
        <Hair mt={18} mb={20} />

        <QuickAdd house={house} viewerPersonId={viewerPersonId} onAdd={onAdd}
          defaultList={active.kind === 'list' ? active.id : 'casa'} />

        <div style={{ marginTop: 24 }}>
          {filtered.length === 0
            ? <EmptyState
                title={primary === 'en' ? 'Nothing pending' : 'Nada pendiente'}
                sub={primary === 'en'
                  ? 'Add something above, or open another corner.'
                  : 'Añade algo arriba, o abre otro rincón.'} />
            : grouped.map((g) => (
              <section key={g.key} style={{ marginBottom: 28 }}>
                {g.key && (
                  <Eyebrow color={P.goldDeep} size={9.5} ls={3}
                    style={{ marginBottom: 6, display: 'block' }}>
                    <Bi es={`Por la ${g.key}`}
                        en={g.key === 'mañana' ? 'In the morning'
                            : g.key === 'tarde' ? 'In the afternoon'
                            : 'In the evening'} />
                  </Eyebrow>
                )}
                {g.items.map(r => (
                  <ReminderRow key={r.id} r={r} house={house}
                    viewerPersonId={viewerPersonId}
                    density={T.t.density} onToggle={onToggle} />
                ))}
              </section>
            ))}
        </div>
      </main>
    );
  }

  // ─── Mis casas — pan-house digest of today ───────────────────────
  function MisCasasPane({ personId, onToggle }) {
    const T = React.useContext(TweaksCtx);
    const me = personById(personId);
    const houses = housesForPerson(personId);
    const sections = useMemo(() => houses.map(h => {
      const rems = visibleRemindersFor(h, personId)
        .filter(r => !r.done && r.due && isToday(r.due))
        .sort((a, b) => new Date(a.due) - new Date(b.due));
      return { house: h, rems };
    }), [houses, personId]);
    const total = sections.reduce((s, x) => s + x.rems.length, 0);

    return (
      <main style={{ flex: 1, minWidth: 0, padding: '28px clamp(20px, 3vw, 40px)' }}>
        <div style={{ display: 'flex', alignItems: 'baseline', gap: 16,
                      flexWrap: 'wrap' }}>
          <h1 style={{
            margin: 0, fontFamily: SERIF, fontStyle: 'italic', fontWeight: 500,
            fontSize: 'clamp(40px, 4.6vw, 62px)', color: P.ink, lineHeight: 0.95,
            letterSpacing: -0.8,
          }}>Mis casas</h1>
          <div style={{ display: 'flex', flexDirection: 'column' }}>
            <Eyebrow color={P.gold} size={10.5} ls={3}>
              <Bi es={`Hoy en las ${houses.length} casas de ${me.name}`}
                  en={`Today across ${me.name}'s ${houses.length} houses`} />
            </Eyebrow>
            <span style={{
              fontFamily: MONO, fontSize: 10, letterSpacing: 1.6,
              color: P.inkMid, marginTop: 4, fontWeight: 600,
              textTransform: 'uppercase',
            }}>
              {total} <Bi es="pendientes en total" en="pending across all" sep=" / " />
            </span>
          </div>
        </div>
        <Hair mt={18} mb={20} />

        <div style={{
          padding: '14px 16px', borderRadius: 6,
          background: 'rgba(0,0,0,0.18)',
          border: `1px solid ${P.lineSoft}`, borderLeft: `3px solid ${P.gold}`,
          marginBottom: 24,
        }}>
          <Eyebrow color={P.gold} size={9} ls={2.4}>
            <Bi es="El mayordomo dice" en="The mayordomo says" />
          </Eyebrow>
          <div style={{
            fontFamily: SERIF, fontStyle: 'italic', fontSize: 16,
            color: P.inkSoft, marginTop: 6, lineHeight: 1.4,
          }}>
            You belong to {houses.length} houses. Each one keeps its own
            rituals, its own ledger, its own seal. Cariños can cross
            between them. Nothing else does.
          </div>
        </div>

        {sections.map(({ house: h, rems }) => (
          <section key={h.id} style={{ marginBottom: 32 }}>
            <div style={{
              display: 'flex', alignItems: 'center', gap: 10,
              paddingBottom: 8, borderBottom: `1px solid ${h.seal}55`,
              marginBottom: 6,
            }}>
              <HouseSeal houseId={h.id} size={28} />
              <div>
                <div style={{
                  fontFamily: SERIF, fontStyle: 'italic', fontSize: 22,
                  color: P.ink, lineHeight: 1.05,
                }}>{h.name}</div>
                <Eyebrow color={h.seal} size={8.5} ls={2} style={{ marginTop: 3 }}>
                  {h.tagline}
                </Eyebrow>
              </div>
              <span style={{ flex: 1 }} />
              <span style={{
                fontFamily: MONO, fontSize: 10, color: h.seal, fontWeight: 600,
                letterSpacing: 1.2,
              }}>{rems.length} <Bi es="hoy" en="today" sep=" / " /></span>
            </div>
            {rems.length === 0 ? (
              <div style={{
                fontFamily: SERIF, fontStyle: 'italic', fontSize: 14,
                color: P.inkFaint, padding: '10px 0 14px',
              }}>—  Nada pendiente hoy.</div>
            ) : rems.map(r => (
              <ReminderRow key={`${h.id}-${r.id}`} r={r} house={h}
                viewerPersonId={personId} density={T.t.density}
                onToggle={(id) => onToggle(id, h.id)}
                showHouseBadge={false} />
            ))}
          </section>
        ))}
      </main>
    );
  }

  // ═════════════════════════════════════════════════════════════════
  //  ASIDE · right column (Las llamadas + Rituales + Libro + Quién)
  // ═════════════════════════════════════════════════════════════════
  function SectionHead({ es, en }) {
    const T = React.useContext(TweaksCtx);
    const { secondary, primary } = langStyle(T.t.language);
    return (
      <div style={{
        display: 'flex', alignItems: 'baseline', gap: 10,
        paddingBottom: 6, borderBottom: `1px solid ${P.line}`,
      }}>
        <h3 style={{
          margin: 0, fontFamily: SERIF, fontStyle: 'italic', fontWeight: 500,
          fontSize: 18, color: P.ink, lineHeight: 1.1,
        }}>{primary === 'en' ? en : es}</h3>
        {secondary && (
          <Eyebrow color={P.inkMid} size={9} ls={2.2}>
            · {primary === 'es' ? en : es}
          </Eyebrow>
        )}
      </div>
    );
  }

  function RightCol({ house, viewerPersonId, reminders }) {
    const T = React.useContext(TweaksCtx);
    const members = membersForHouse(house);
    const rituals = reminders.filter(r => r.recur && r.streak && !r.done)
      .sort((a, b) => b.streak - a.streak).slice(0, 6);
    const ledger = reminders.filter(r => r.done)
      .sort((a, b) => new Date(b.doneAt || 0) - new Date(a.doneAt || 0))
      .slice(0, 5);

    const ROOM_META = {
      cellar:   { label: 'Cellar',   es: 'La Cava',     hex: P.wine },
      cocina:   { label: 'Cocina',   es: 'La Cocina',   hex: P.cocina },
      demos:    { label: 'Demos',    es: 'En Vivo',     hex: '#6FA67A' },
      shop:     { label: 'Shop',     es: 'La Tienda',   hex: P.ember },
      'la-casa':{ label: 'La Casa',  es: 'manual',      hex: P.gold   },
    };
    const wires = useMemo(() => {
      const buckets = { cellar: [], cocina: [], demos: [], shop: [], 'la-casa': [] };
      reminders.filter(r => !r.done).forEach(r => {
        const k = r.tied?.room || 'la-casa';
        if (buckets[k]) buckets[k].push(r);
      });
      return Object.entries(buckets)
        .map(([k, items]) => ({ k, items }))
        .filter(b => b.items.length);
    }, [reminders]);

    return (
      <aside style={{
        width: 320, flexShrink: 0,
        padding: '28px 24px 24px 14px',
        borderLeft: `1px solid ${P.line}`,
        display: 'flex', flexDirection: 'column', gap: 28,
        position: 'sticky', top: 73, alignSelf: 'flex-start',
        maxHeight: 'calc(100vh - 73px)', overflowY: 'auto',
      }}>
        {T.t.llamadas && (
          <section>
            <SectionHead es="Las llamadas" en="The wires" />
            <div style={{
              fontFamily: SERIF, fontStyle: 'italic', fontSize: 12.5, color: P.inkMid,
              marginTop: 4, lineHeight: 1.45,
            }}>Which room asked the house for what.</div>
            <div style={{ display: 'flex', flexDirection: 'column', marginTop: 10 }}>
              {wires.map(({ k, items }) => {
                const meta = ROOM_META[k];
                return (
                  <div key={k} style={{
                    padding: '10px 0 8px',
                    borderBottom: `1px dashed ${P.lineFaint}`,
                  }}>
                    <div style={{
                      display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6,
                    }}>
                      <span style={{
                        width: 8, height: 8, borderRadius: 999, background: meta.hex,
                      }} />
                      <span style={{
                        fontFamily: SANS, fontSize: 9.5, letterSpacing: 1.8,
                        color: meta.hex, fontWeight: 700, textTransform: 'uppercase',
                      }}>{meta.label}</span>
                      <span style={{
                        fontFamily: SERIF, fontStyle: 'italic', fontSize: 11,
                        color: P.inkFaint,
                      }}>· {meta.es}</span>
                      <span style={{ flex: 1 }} />
                      <span style={{
                        fontFamily: MONO, fontSize: 10, color: P.inkMid,
                        fontWeight: 600, letterSpacing: 0.6,
                      }}>{items.length}</span>
                    </div>
                    {items.slice(0, 3).map(r => (
                      <div key={r.id} style={{
                        display: 'flex', alignItems: 'baseline', gap: 8,
                        padding: '3px 0 3px 14px', position: 'relative',
                      }}>
                        <span style={{
                          position: 'absolute', left: 3, top: 9, width: 6, height: 1,
                          background: meta.hex, opacity: 0.5,
                        }} />
                        <span style={{
                          fontFamily: SERIF, fontStyle: 'italic', fontSize: 13,
                          color: P.inkSoft, lineHeight: 1.25, flex: 1, minWidth: 0,
                          whiteSpace: 'nowrap', overflow: 'hidden',
                          textOverflow: 'ellipsis',
                        }}>{r.titleEs || r.title}</span>
                        <Dot id={r.who} size={16} />
                      </div>
                    ))}
                    {items.length > 3 && (
                      <div style={{
                        paddingLeft: 14, marginTop: 2,
                        fontFamily: MONO, fontSize: 9, color: P.inkFaint,
                        letterSpacing: 1, fontWeight: 600,
                      }}>+ {items.length - 3} más</div>
                    )}
                  </div>
                );
              })}
            </div>
            <div style={{
              marginTop: 10, padding: '8px 10px',
              background: 'rgba(0,0,0,0.18)',
              border: `1px dashed ${P.lineSoft}`, borderRadius: 4,
              fontFamily: SERIF, fontStyle: 'italic', fontSize: 12,
              color: P.inkMid, lineHeight: 1.4,
            }}>
              <Eyebrow color={P.gold} size={8} ls={2}>add a room</Eyebrow>
              <div style={{ marginTop: 4 }}>
                Any new room — Jardín, Niños, Bodega Bills — emits signals
                here. Nothing else changes.
              </div>
            </div>
          </section>
        )}

        {T.t.rituals && (
          <section>
            <SectionHead es="Rituales" en="The house runs on these" />
            <div style={{ display: 'flex', flexDirection: 'column',
                          gap: 4, marginTop: 8 }}>
              {rituals.map(r => {
                const l = listForHouse(house, r.list, viewerPersonId);
                const m = personById(r.who);
                return (
                  <div key={r.id} style={{
                    display: 'flex', alignItems: 'center', gap: 10, padding: '8px 0',
                    borderBottom: `1px dashed ${P.lineFaint}`,
                  }}>
                    <Dot id={r.who} size={22} />
                    <div style={{ flex: 1, minWidth: 0 }}>
                      <div style={{
                        fontFamily: SERIF, fontStyle: 'italic', fontSize: 14,
                        color: P.ink, lineHeight: 1.2,
                        whiteSpace: 'nowrap', overflow: 'hidden',
                        textOverflow: 'ellipsis',
                      }}>{r.titleEs || r.title}</div>
                      <div style={{
                        fontFamily: SANS, fontSize: 9, letterSpacing: 1.4,
                        color: l.hex, fontWeight: 700, textTransform: 'uppercase',
                        marginTop: 2,
                      }}>{l.label} · {m.name}</div>
                    </div>
                    <span style={{
                      fontFamily: MONO, fontSize: 11, color: P.goldHi, fontWeight: 600,
                      letterSpacing: 0.6,
                    }}>{r.streak}<span style={{ color: P.goldDeep }}>×</span></span>
                  </div>
                );
              })}
              {rituals.length === 0 && (
                <div style={{
                  fontFamily: SERIF, fontStyle: 'italic', fontSize: 13,
                  color: P.inkFaint, padding: '8px 0',
                }}>No rituals yet — every recurring reminder builds a streak here.</div>
              )}
            </div>
          </section>
        )}

        {T.t.ledger && (
          <section>
            <SectionHead es="El libro de la casa" en="What the house did" />
            <div style={{
              fontFamily: SERIF, fontStyle: 'italic', fontSize: 12.5,
              color: P.inkMid, marginTop: 4, lineHeight: 1.45,
            }}>The ledger of completed rituals — so the house has a memory.</div>
            <div style={{ display: 'flex', flexDirection: 'column',
                          marginTop: 12, gap: 0 }}>
              {ledger.map(r => {
                const m = personById(r.who);
                const l = listForHouse(house, r.list, viewerPersonId);
                return (
                  <div key={r.id} style={{
                    display: 'flex', alignItems: 'baseline', gap: 10, padding: '7px 0',
                    borderBottom: `1px dashed ${P.lineFaint}`,
                  }}>
                    <span style={{
                      width: 6, height: 6, borderRadius: 999, background: l.hex,
                      flexShrink: 0,
                    }} />
                    <span style={{
                      fontFamily: MONO, fontSize: 9.5, color: P.inkMid, fontWeight: 600,
                      letterSpacing: 1.2, minWidth: 64,
                    }}>{fmtTime(r.doneAt, m.tz)}</span>
                    <div style={{ flex: 1, minWidth: 0 }}>
                      <span style={{
                        fontFamily: SERIF, fontStyle: 'italic', fontSize: 13.5,
                        color: P.inkSoft, lineHeight: 1.2,
                      }}>{r.titleEs || r.title}</span>
                      <div style={{
                        fontFamily: SANS, fontSize: 9, letterSpacing: 1.4,
                        color: P.inkFaint, fontWeight: 600, textTransform: 'uppercase',
                        marginTop: 2,
                      }}>{m.name} · {fmtDay(r.doneAt)}</div>
                    </div>
                  </div>
                );
              })}
              {!ledger.length && (
                <div style={{
                  fontFamily: SERIF, fontStyle: 'italic', fontSize: 13.5,
                  color: P.inkFaint, padding: '8px 0',
                }}>Nothing logged yet today.</div>
              )}
            </div>
          </section>
        )}

        <section>
          <SectionHead es="Quién está en casa" en="Who's around" />
          <div style={{ display: 'flex', flexDirection: 'column',
                        marginTop: 10, gap: 8 }}>
            {members.filter(m => m.id !== 'casa').map(m => {
              const now = new Date();
              const local = now.toLocaleTimeString('en-US', {
                hour: 'numeric', minute: '2-digit', hour12: true, timeZone: m.tz,
              }).replace(/\u202f/g, ' ');
              const h = +now.toLocaleString('en-US', {
                hour: 'numeric', hour12: false, timeZone: m.tz });
              const awake = h >= 6 && h < 23;
              return (
                <div key={m.id} style={{
                  display: 'flex', alignItems: 'center', gap: 10,
                  opacity: m.state === 'active' ? (awake ? 1 : 0.55) : 0.4,
                }}>
                  <Dot id={m.id} size={22} />
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{
                      fontFamily: SERIF, fontStyle: 'italic', fontSize: 14,
                      color: P.ink,
                    }}>{m.name}<span style={{ color: P.inkMid, marginLeft: 6 }}>
                      · {m.city}</span></div>
                    <div style={{
                      fontFamily: MONO, fontSize: 9, color: P.inkMid, marginTop: 2,
                      letterSpacing: 1, fontWeight: 600,
                    }}>{local} ·{' '}
                      {m.state === 'invited' ? 'invitado · pending' :
                       m.state === 'inactive' ? 'familia · no app yet' :
                       awake ? `${m.role} · awake` : 'asleep'}</div>
                  </div>
                </div>
              );
            })}
          </div>
        </section>
      </aside>
    );
  }

  // ═════════════════════════════════════════════════════════════════
  //  INVITE · invitation drawer (sketch — token shape lives in HANDOFF)
  // ═════════════════════════════════════════════════════════════════
  function InviteDrawer({ open, onClose, house, viewerPersonId }) {
    const [email, setEmail] = useState('');
    const [role, setRole]   = useState('inner');
    const lists = listsForHouse(house, viewerPersonId);
    const [scope, setScope] = useState('house-wide');
    if (!open) return null;
    const token = email
      ? `cm-inv-${house.id}-${role}-${Math.random().toString(36).slice(2,10)}`
      : '—';
    const link = `https://recuerdas.casam.me/accept?token=${token}`;
    return (
      <div style={{
        position: 'fixed', inset: 0, zIndex: 100,
        background: 'rgba(0,0,0,0.6)', backdropFilter: 'blur(4px)',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        padding: 24,
      }} onClick={onClose}>
        <div onClick={(e) => e.stopPropagation()} style={{
          width: '100%', maxWidth: 540,
          background: P.bgWarm, border: `1px solid ${P.line}`,
          borderRadius: 10, padding: 28,
          boxShadow: '0 24px 60px rgba(0,0,0,0.6)',
        }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 14, marginBottom: 8 }}>
            <HouseSeal houseId={house.id} size={36} />
            <div>
              <Eyebrow color={house.seal} size={9} ls={2.4}>
                Invitar a · invite to
              </Eyebrow>
              <h2 style={{
                margin: '2px 0 0', fontFamily: SERIF, fontStyle: 'italic',
                fontWeight: 500, fontSize: 28, color: P.ink, lineHeight: 1,
              }}>{house.name}</h2>
            </div>
            <span style={{ flex: 1 }} />
            <button onClick={onClose} style={{
              padding: 6, background: 'transparent', border: 'none',
              color: P.inkMid, cursor: 'pointer', fontSize: 18,
            }}>✕</button>
          </div>
          <Hair mt={14} mb={18} />

          <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
            <Field label="Email or phone · cómo los contactas">
              <input value={email} onChange={(e) => setEmail(e.target.value)}
                placeholder="manny.hernandez.brother@example.com"
                style={inputStyle} />
            </Field>
            <Field label="Role · papel en la casa">
              <select value={role} onChange={(e) => setRole(e.target.value)}
                style={inputStyle}>
                <option value="host">Host · co-anfitrión (full control)</option>
                <option value="inner">Inner circle · círculo cercano</option>
                <option value="member">Member · miembro</option>
                <option value="family">Family · familia (passive)</option>
                <option value="guest">Guest · invitado (list-scoped)</option>
                <option value="view-only">View-only · ver solamente</option>
              </select>
            </Field>
            <Field label="Scope · qué pueden ver">
              <select value={scope} onChange={(e) => setScope(e.target.value)}
                style={inputStyle}>
                <option value="house-wide">House-wide · toda la casa</option>
                {lists.map(l => (
                  <option key={l.id} value={l.id}>Just {l.label}</option>
                ))}
              </select>
            </Field>
            <div style={{
              padding: 12, background: 'rgba(0,0,0,0.24)',
              border: `1px dashed ${P.lineSoft}`, borderRadius: 4,
            }}>
              <Eyebrow color={P.gold} size={9} ls={2}>
                Invite link · expires in 7 days
              </Eyebrow>
              <div style={{
                fontFamily: MONO, fontSize: 11, color: P.ink, marginTop: 6,
                wordBreak: 'break-all', lineHeight: 1.4,
              }}>{link}</div>
            </div>
            <button style={{
              padding: '12px 16px', cursor: 'pointer',
              background: P.gold, color: P.bg, border: 'none', borderRadius: 4,
              fontFamily: SANS, fontSize: 11, letterSpacing: 2.2, fontWeight: 700,
              textTransform: 'uppercase',
            }} onClick={onClose}>
              <Bi es="Enviar invitación" en="Send invite" />
            </button>
          </div>
        </div>
      </div>
    );
  }
  const inputStyle = {
    width: '100%', background: 'rgba(0,0,0,0.24)', color: P.ink,
    border: `1px solid ${P.lineSoft}`, borderRadius: 4,
    padding: '10px 12px', fontFamily: SERIF, fontStyle: 'italic',
    fontSize: 16, outline: 'none',
  };
  function Field({ label, children }) {
    return (
      <div>
        <Eyebrow color={P.inkMid} size={9} ls={2}>{label}</Eyebrow>
        <div style={{ marginTop: 6 }}>{children}</div>
      </div>
    );
  }

  // ═════════════════════════════════════════════════════════════════
  //  SETUP · life-events picker (onboarding spine)
  // ═════════════════════════════════════════════════════════════════
  function SetupDrawer({ open, onClose, house, onApply }) {
    const T = React.useContext(TweaksCtx);
    const [picked, setPicked] = useState({});  // id → boolean
    const [expanded, setExpanded] = useState(null);
    if (!open) return null;
    const completed = new Set(house.completedLifeEvents || []);

    function toggle(id) {
      setPicked(p => ({ ...p, [id]: !p[id] }));
    }
    function apply() {
      const ids = Object.entries(picked).filter(([, v]) => v).map(([k]) => k);
      onApply(ids);
      setPicked({});
      onClose();
    }

    const pickedCount = Object.values(picked).filter(Boolean).length;

    return (
      <div onClick={onClose} style={{
        position: 'fixed', inset: 0, zIndex: 100,
        background: 'rgba(0,0,0,0.65)', backdropFilter: 'blur(4px)',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        padding: 24, overflowY: 'auto',
      }}>
        <div onClick={(e) => e.stopPropagation()} style={{
          width: '100%', maxWidth: 880, maxHeight: '90vh',
          background: P.bgWarm, border: `1px solid ${P.line}`,
          borderRadius: 10, boxShadow: '0 24px 60px rgba(0,0,0,0.6)',
          display: 'flex', flexDirection: 'column',
        }}>
          <div style={{
            padding: '24px 28px 16px', borderBottom: `1px solid ${P.lineSoft}`,
            display: 'flex', alignItems: 'flex-start', gap: 16,
          }}>
            <HouseSeal houseId={house.id} size={40} />
            <div style={{ flex: 1 }}>
              <Eyebrow color={house.seal} size={9.5} ls={2.4}>
                Setup · Configurar {house.name}
              </Eyebrow>
              <h2 style={{
                margin: '4px 0 4px', fontFamily: SERIF, fontStyle: 'italic',
                fontWeight: 500, fontSize: 32, color: P.ink, lineHeight: 1,
              }}>What does this house remember?</h2>
              <div style={{
                fontFamily: SERIF, fontStyle: 'italic', fontSize: 15,
                color: P.inkMid, marginTop: 4,
              }}>
                Pick the life events that apply. Each one adds the right
                lists, subjects, and reminders — no setup grammar required.
              </div>
            </div>
            <button onClick={onClose} style={{
              padding: 8, background: 'transparent', border: 'none',
              color: P.inkMid, cursor: 'pointer', fontSize: 20,
            }}>✕</button>
          </div>

          {/* Import banner — Apple, Google, Todoist, paste */}
          <div style={{
            padding: '14px 28px 8px',
          }}>
            <div style={{
              padding: '14px 16px',
              background: 'rgba(0,0,0,0.18)',
              border: `1px solid ${P.lineSoft}`,
              borderLeft: `3px solid ${P.gold}`,
              borderRadius: 6,
            }}>
              <Eyebrow color={P.gold} size={9} ls={2.2}>
                <Bi es="Ya tienes recordatorios en otra app?"
                    en="Already have reminders elsewhere?" />
              </Eyebrow>
              <div style={{
                fontFamily: SERIF, fontStyle: 'italic', fontSize: 13.5,
                color: P.inkSoft, marginTop: 6, lineHeight: 1.4,
              }}>
                Import from <strong style={{ color: P.ink }}>Apple Reminders</strong>,
                Google Tasks, Todoist, Things, Microsoft To Do, or paste plain text —
                the mayordomo parses each line.
              </div>
              <div style={{
                display: 'flex', gap: 4, flexWrap: 'wrap', marginTop: 10,
              }}>
                {(window.RECUERDAS.IMPORT_SOURCES || []).map(src => (
                  <button key={src.id} style={{
                    display: 'inline-flex', alignItems: 'center', gap: 5,
                    padding: '5px 9px 5px 7px',
                    background: 'rgba(0,0,0,0.22)', border: `1px solid ${P.lineSoft}`,
                    borderRadius: 999, color: P.inkSoft, cursor: 'pointer',
                    fontFamily: SANS, fontSize: 10, letterSpacing: 0.6, fontWeight: 600,
                  }} title={src.blurb.en}>
                    <span style={{ fontSize: 14 }}>{src.emoji}</span>
                    {src.label}
                    <span style={{ color: P.inkFaint, fontSize: 10, marginLeft: 2 }}>↗</span>
                  </button>
                ))}
              </div>
              <div style={{
                fontFamily: MONO, fontSize: 9, color: P.inkFaint, letterSpacing: 1,
                marginTop: 8, textTransform: 'uppercase', fontWeight: 600,
              }}>
                ⓘ Import flow lives at /import — see HANDOFF §24
              </div>
            </div>
          </div>

          {/* Life events grid */}
          <div style={{
            flex: 1, overflowY: 'auto', padding: '20px 28px',
            display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(240px, 1fr))',
            gap: 12,
          }}>
            {LIFE_EVENTS.map(e => {
              const isCompleted = completed.has(e.id);
              const isPicked = picked[e.id];
              const isExpanded = expanded === e.id;
              const listLabels = (e.addsLists || [])
                .map(k => LIST_TYPES[k]?.label).filter(Boolean);
              return (
                <div key={e.id} style={{
                  position: 'relative',
                  background: isPicked ? `${P.gold}1A`
                    : isCompleted ? 'rgba(0,0,0,0.24)'
                    : 'rgba(0,0,0,0.16)',
                  border: `1px solid ${isPicked ? P.gold
                    : isCompleted ? P.lineSoft : P.lineFaint}`,
                  borderRadius: 8, padding: 16, cursor: 'pointer',
                  opacity: isCompleted ? 0.65 : 1,
                }} onClick={() => !isCompleted && toggle(e.id)}>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                    <ListGlyph which={e.icon} size={18}
                      color={isPicked ? P.gold : P.inkSoft} />
                    <span style={{
                      fontFamily: SERIF, fontStyle: 'italic', fontWeight: 500,
                      fontSize: 19, color: P.ink, lineHeight: 1.1, flex: 1,
                    }}>
                      <Bi es={e.title.es} en={e.title.en} />
                    </span>
                    {isCompleted && (
                      <span title="Already applied" style={{
                        fontSize: 11, color: P.goldHi, fontFamily: SANS,
                        letterSpacing: 1.4, fontWeight: 700,
                      }}>✓</span>
                    )}
                    {!isCompleted && (
                      <span style={{
                        width: 18, height: 18, borderRadius: 4,
                        border: `1.5px solid ${isPicked ? P.gold : P.line}`,
                        background: isPicked ? P.gold : 'transparent',
                        display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
                      }}>{isPicked && (
                        <svg width={11} height={11} viewBox="0 0 24 24">
                          <path d="M5 12l4 4 10-10" fill="none" stroke={P.bg}
                            strokeWidth="3" strokeLinecap="round" />
                        </svg>
                      )}</span>
                    )}
                  </div>
                  <div style={{
                    fontFamily: SERIF, fontStyle: 'italic', fontSize: 13,
                    color: P.inkMid, lineHeight: 1.35, marginTop: 8,
                  }}>
                    <Bi es={e.blurb.es} en={e.blurb.en} />
                  </div>
                  {listLabels.length > 0 && (
                    <div style={{ marginTop: 10, display: 'flex',
                                  gap: 4, flexWrap: 'wrap' }}>
                      {listLabels.map(l => (
                        <span key={l} style={{
                          fontFamily: SANS, fontSize: 8.5, letterSpacing: 1.4,
                          color: P.inkMid, fontWeight: 700,
                          textTransform: 'uppercase',
                          padding: '2px 7px', borderRadius: 2,
                          border: `1px solid ${P.lineSoft}`,
                        }}>{l}</span>
                      ))}
                    </div>
                  )}
                  <button onClick={(ev) => {
                    ev.stopPropagation();
                    setExpanded(isExpanded ? null : e.id);
                  }} style={{
                    marginTop: 10, padding: 0, background: 'transparent',
                    border: 'none', color: P.gold, cursor: 'pointer',
                    fontFamily: SANS, fontSize: 9.5, letterSpacing: 1.6,
                    fontWeight: 700, textTransform: 'uppercase',
                  }}>{isExpanded ? '− Ocultar reminders' : '+ Ver reminders'}</button>
                  {isExpanded && (
                    <div style={{
                      marginTop: 8, paddingTop: 8,
                      borderTop: `1px dashed ${P.lineFaint}`,
                      display: 'flex', flexDirection: 'column', gap: 4,
                    }}>
                      {(e.reminders || []).map((r, i) => (
                        <div key={i} style={{
                          display: 'flex', alignItems: 'baseline', gap: 6,
                          fontFamily: SERIF, fontStyle: 'italic',
                          fontSize: 12.5, color: P.inkSoft, lineHeight: 1.3,
                        }}>
                          <span style={{ color: P.inkFaint }}>·</span>
                          <span style={{ flex: 1 }}>{r.titleEs || r.title}</span>
                          {r.recur && (
                            <span style={{ color: P.goldDeep, fontSize: 11 }}>
                              ↻ {r.recur.label}
                            </span>
                          )}
                          {r.tz_priority === 'current' && (
                            <span style={{ color: P.ember, fontSize: 9,
                              fontFamily: SANS, letterSpacing: 1.2,
                              fontWeight: 700, textTransform: 'uppercase' }}>
                              ⚛ local
                            </span>
                          )}
                        </div>
                      ))}
                    </div>
                  )}
                </div>
              );
            })}
          </div>

          {/* Footer · apply */}
          <div style={{
            padding: '16px 28px', borderTop: `1px solid ${P.lineSoft}`,
            display: 'flex', alignItems: 'center', gap: 16,
          }}>
            <div style={{
              fontFamily: SERIF, fontStyle: 'italic', fontSize: 14, color: P.inkMid,
            }}>
              {pickedCount === 0
                ? 'Pick one or more to add. Nothing happens until you apply.'
                : `${pickedCount} event${pickedCount === 1 ? '' : 's'} selected.`}
            </div>
            <span style={{ flex: 1 }} />
            <button onClick={onClose} style={{
              padding: '10px 14px', cursor: 'pointer',
              background: 'transparent', color: P.inkMid,
              border: `1px solid ${P.line}`, borderRadius: 4,
              fontFamily: SANS, fontSize: 10, letterSpacing: 1.8, fontWeight: 700,
              textTransform: 'uppercase',
            }}>Cancel</button>
            <button onClick={apply} disabled={pickedCount === 0} style={{
              padding: '10px 18px',
              cursor: pickedCount === 0 ? 'not-allowed' : 'pointer',
              background: pickedCount === 0 ? P.lineSoft : P.gold,
              color: pickedCount === 0 ? P.inkFaint : P.bg,
              border: 'none', borderRadius: 4,
              fontFamily: SANS, fontSize: 11, letterSpacing: 2.2, fontWeight: 700,
              textTransform: 'uppercase',
            }}>
              Apply · Aplicar
            </button>
          </div>
        </div>
      </div>
    );
  }

  // ═════════════════════════════════════════════════════════════════
  //  AJUSTES · notification preferences (per-channel × per-list)
  // ═════════════════════════════════════════════════════════════════
  function AjustesDrawer({ open, onClose, house, viewerPersonId }) {
    const [prefs, setPrefs] = useState(() => {
      const seed = {};
      listsForHouse(house, viewerPersonId).forEach(l => {
        seed[l.id] = { ...(NOTIF_DEFAULTS[l.type] || {
          inapp:true, desktop:false, mobile:false, email:false, sms:false }) };
      });
      return seed;
    });
    const me = personById(viewerPersonId);
    const deviceTz = Intl.DateTimeFormat().resolvedOptions().timeZone;
    const traveling = deviceTz !== me.tz;
    if (!open) return null;

    function toggle(listId, channel) {
      setPrefs(p => ({ ...p, [listId]: { ...p[listId], [channel]: !p[listId][channel] }}));
    }

    return (
      <div onClick={onClose} style={{
        position: 'fixed', inset: 0, zIndex: 100,
        background: 'rgba(0,0,0,0.65)', backdropFilter: 'blur(4px)',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        padding: 24, overflowY: 'auto',
      }}>
        <div onClick={(e) => e.stopPropagation()} style={{
          width: '100%', maxWidth: 840, maxHeight: '90vh',
          background: P.bgWarm, border: `1px solid ${P.line}`,
          borderRadius: 10, boxShadow: '0 24px 60px rgba(0,0,0,0.6)',
          display: 'flex', flexDirection: 'column',
        }}>
          <div style={{
            padding: '24px 28px 16px', borderBottom: `1px solid ${P.lineSoft}`,
            display: 'flex', alignItems: 'flex-start', gap: 16,
          }}>
            <div style={{
              width: 40, height: 40, borderRadius: 999,
              background: `${P.gold}28`, color: P.gold,
              display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
              fontSize: 22,
            }}>⚭</div>
            <div style={{ flex: 1 }}>
              <Eyebrow color={P.gold} size={9.5} ls={2.4}>
                Ajustes · Notifications & travel
              </Eyebrow>
              <h2 style={{
                margin: '4px 0 4px', fontFamily: SERIF, fontStyle: 'italic',
                fontWeight: 500, fontSize: 32, color: P.ink, lineHeight: 1,
              }}>How should the mayordomo reach you?</h2>
              <div style={{
                fontFamily: SERIF, fontStyle: 'italic', fontSize: 14,
                color: P.inkMid, marginTop: 6, lineHeight: 1.4,
              }}>
                Channels per list. Defaults are conservative — meds and
                health get every channel; cariños whisper only.
              </div>
            </div>
            <button onClick={onClose} style={{
              padding: 8, background: 'transparent', border: 'none',
              color: P.inkMid, cursor: 'pointer', fontSize: 20,
            }}>✕</button>
          </div>

          <div style={{ flex: 1, overflowY: 'auto', padding: '20px 28px' }}>
            {/* Travel mode banner */}
            <div style={{
              padding: '14px 16px', borderRadius: 6,
              background: traveling ? `${P.ember}1F` : 'rgba(0,0,0,0.18)',
              border: `1px solid ${traveling ? P.ember : P.lineSoft}`,
              borderLeft: `3px solid ${traveling ? P.ember : P.gold}`,
              marginBottom: 24,
            }}>
              <Eyebrow color={traveling ? P.ember : P.gold} size={9} ls={2}>
                {traveling ? '⚛ Travel mode active' : 'Travel mode · standby'}
              </Eyebrow>
              <div style={{
                fontFamily: SERIF, fontStyle: 'italic', fontSize: 14,
                color: P.inkSoft, marginTop: 6, lineHeight: 1.4,
              }}>
                {traveling
                  ? `You are in ${deviceTz.split('/').pop().replace('_', ' ')}, away from your home tz (${me.tz.split('/').pop().replace('_', ' ')}). Reminders flagged ⚛ local will fire on device time.`
                  : `You are at home in ${me.tz.split('/').pop().replace('_', ' ')}. When you travel, reminders flagged ⚛ local will follow you automatically.`}
              </div>
            </div>

            {/* Channel × list matrix */}
            <div style={{
              display: 'grid', gridTemplateColumns: `220px repeat(${NOTIF_CHANNELS.length}, 1fr)`,
              gap: 6, alignItems: 'center',
            }}>
              <div /> {/* corner */}
              {NOTIF_CHANNELS.map(c => (
                <div key={c.id} title={c.desc} style={{
                  fontFamily: SANS, fontSize: 9, letterSpacing: 1.6,
                  color: P.gold, fontWeight: 700, textTransform: 'uppercase',
                  textAlign: 'center', padding: '6px 4px',
                  borderBottom: `1px solid ${P.lineSoft}`,
                }}>{c.label}</div>
              ))}
              {listsForHouse(house, viewerPersonId).map(l => (
                <React.Fragment key={l.id}>
                  <div style={{
                    display: 'flex', alignItems: 'center', gap: 8,
                    padding: '8px 4px',
                    borderBottom: `1px dashed ${P.lineFaint}`,
                  }}>
                    <ListGlyph which={l.icon} size={14} color={l.hex} />
                    <span style={{
                      fontFamily: SERIF, fontStyle: 'italic', fontSize: 14,
                      color: P.ink,
                    }}>{l.label}</span>
                  </div>
                  {NOTIF_CHANNELS.map(c => (
                    <div key={c.id} style={{
                      display: 'flex', justifyContent: 'center', alignItems: 'center',
                      padding: '8px 4px',
                      borderBottom: `1px dashed ${P.lineFaint}`,
                    }}>
                      <button onClick={() => toggle(l.id, c.id)} style={{
                        width: 28, height: 16, borderRadius: 99, padding: 0,
                        background: prefs[l.id][c.id] ? l.hex : P.lineSoft,
                        border: 'none', cursor: 'pointer', position: 'relative',
                        transition: 'background 150ms',
                      }}>
                        <span style={{
                          position: 'absolute', top: 2,
                          left: prefs[l.id][c.id] ? 14 : 2,
                          width: 12, height: 12, borderRadius: 99,
                          background: '#fff',
                          transition: 'left 150ms',
                        }} />
                      </button>
                    </div>
                  ))}
                </React.Fragment>
              ))}
            </div>

            {/* Quiet hours sketch */}
            <div style={{ marginTop: 24, padding: 16,
              background: 'rgba(0,0,0,0.18)',
              border: `1px dashed ${P.lineSoft}`, borderRadius: 6,
            }}>
              <Eyebrow color={P.inkMid} size={9} ls={2}>
                Quiet hours · Horas de silencio
              </Eyebrow>
              <div style={{
                fontFamily: SERIF, fontStyle: 'italic', fontSize: 14,
                color: P.inkSoft, marginTop: 6,
              }}>
                10 pm – 6 am local. Meds, salud, niños override this.
                <span style={{ color: P.inkFaint }}> (editable in v1.1)</span>
              </div>
            </div>
          </div>

          <div style={{
            padding: '16px 28px', borderTop: `1px solid ${P.lineSoft}`,
            display: 'flex', justifyContent: 'flex-end', gap: 12,
          }}>
            <button onClick={onClose} style={{
              padding: '10px 18px', cursor: 'pointer',
              background: P.gold, color: P.bg, border: 'none', borderRadius: 4,
              fontFamily: SANS, fontSize: 11, letterSpacing: 2.2, fontWeight: 700,
              textTransform: 'uppercase',
            }}>
              Save · Guardar
            </button>
          </div>
        </div>
      </div>
    );
  }

  // ═════════════════════════════════════════════════════════════════
  //  TWEAKS PANEL
  // ═════════════════════════════════════════════════════════════════
  function Panel() {
    const T = React.useContext(TweaksCtx);
    const { TweaksPanel, TweakSection, TweakSelect, TweakRadio, TweakToggle } = window;
    return (
      <TweaksPanel>
        <TweakSection title="Voice">
          <TweakSelect label="Language" value={T.t.language}
            onChange={(v) => T.setTweak('language', v)}
            options={[
              { value: 'es-en', label: 'ES 1st  ·  EN secondary' },
              { value: 'en-es', label: 'EN 1st  ·  ES secondary' },
              { value: 'ambas', label: 'Ambas  ·  EN + ES equal' },
              { value: 'es',    label: 'Sólo Español' },
              { value: 'en',    label: 'English only' },
            ]} />
        </TweakSection>
        <TweakSection title="Layout">
          <TweakRadio label="Density" value={T.t.density}
            onChange={(v) => T.setTweak('density', v)}
            options={[
              { value: 'cómoda',    label: 'Cómoda' },
              { value: 'compacta',  label: 'Compacta' },
            ]} />
        </TweakSection>
        <TweakSection title="Right column">
          <TweakToggle label="Las llamadas · the wires"
            value={T.t.llamadas} onChange={(v) => T.setTweak('llamadas', v)} />
          <TweakToggle label="Rituales · streak leaderboard"
            value={T.t.rituals}  onChange={(v) => T.setTweak('rituals', v)} />
          <TweakToggle label="El libro · the ledger"
            value={T.t.ledger}   onChange={(v) => T.setTweak('ledger', v)} />
        </TweakSection>
      </TweaksPanel>
    );
  }

  // ═════════════════════════════════════════════════════════════════
  //  APP · root component, state, persistence
  // ═════════════════════════════════════════════════════════════════
  function App() {
    const [tweaks, setTweak] = window.useTweaks(TWEAK_DEFAULTS);
    const T = useMemo(() => ({ t: tweaks, setTweak }), [tweaks, setTweak]);

    // Identity + current tenant
    const [personId, setPersonId] = useState('mike');
    const myHouses = useMemo(() => housesForPerson(personId), [personId]);
    const [houseId, setHouseId] = useState('casa-m');
    const [active, setActive] = useState({ kind: 'bin', id: 'today' });
    const [inviteOpen, setInviteOpen] = useState(false);
    const [setupOpen, setSetupOpen] = useState(false);
    const [ajustesOpen, setAjustesOpen] = useState(false);

    // Per-house reminders, held in state so toggles + adds work in-prototype.
    // Production: each house's data is fetched from /api/houses/:id/reminders.
    const [housesState, setHousesState] = useState(() => {
      const s = {};
      HOUSES.forEach(h => { s[h.id] = h.reminders.map(r => ({ ...r })); });
      return s;
    });

    // The current house, with its reminders patched in from local state.
    const currentHouse = useMemo(() => {
      const base = houseById(houseId);
      return { ...base, reminders: housesState[base.id] };
    }, [houseId, housesState]);

    const reminders = useMemo(() =>
      visibleRemindersFor(currentHouse, personId),
      [currentHouse, personId]);

    // Drop the right column under ~1080px
    const [wide, setWide] = useState(() =>
      typeof window !== 'undefined' && window.innerWidth >= 1080);
    useEffect(() => {
      const onR = () => setWide(window.innerWidth >= 1080);
      window.addEventListener('resize', onR);
      return () => window.removeEventListener('resize', onR);
    }, []);

    // Counts
    const counts = useMemo(() => {
      const open = reminders.filter(r => !r.done);
      const lists = {};
      listsForHouse(currentHouse, personId).forEach(l => {
        lists[l.id] = open.filter(r => r.list === l.id).length;
      });
      // Pan-house mis-casas count
      let misCasas = 0;
      myHouses.forEach(h => {
        misCasas += visibleRemindersFor(
          { ...h, reminders: housesState[h.id] }, personId)
          .filter(r => !r.done && r.due && isToday(r.due)).length;
      });
      return {
        bins: {
          'mis-casas': misCasas,
          today: open.filter(r => r.due && isToday(r.due)).length,
          tomorrow: open.filter(r => {
            if (!r.due) return false;
            const d = new Date(r.due);
            const t = new Date(); t.setDate(t.getDate() + 1);
            return d.toDateString() === t.toDateString();
          }).length,
          week: open.filter(r => {
            if (!r.due) return false;
            const end = new Date(); end.setDate(end.getDate() + 7);
            return new Date(r.due) <= end;
          }).length,
          scheduled: open.filter(r => r.due).length,
          rituals:   open.filter(r => r.recur).length,
          assigned:  open.filter(r => r.who === personId).length,
          done:      reminders.filter(r => r.done).length,
        },
        lists,
      };
    }, [reminders, currentHouse, personId, myHouses, housesState]);

    function onToggle(id, scopeHouseId) {
      const hid = scopeHouseId || houseId;
      setHousesState(prev => ({
        ...prev,
        [hid]: prev[hid].map(r => r.id === id ? {
          ...r, done: !r.done,
          doneAt: !r.done ? new Date().toISOString() : null,
        } : r),
      }));
    }
    function onAdd(partial) {
      const id = 'r-' + Math.random().toString(36).slice(2, 8);
      setHousesState(prev => ({
        ...prev,
        [houseId]: [{ id, ...partial, done: false }, ...prev[houseId]],
      }));
    }
    function applyLifeEvents(ids) {
      // Materialize each picked life event into the current house's
      // lists, subjects, and reminders. In production this becomes a
      // single POST /api/houses/{id}/life-events/apply call.
      const baseHouse = houseById(houseId);
      const newLists = [...baseHouse.lists];
      const newSubjects = [...(baseHouse.subjects || [])];
      const newReminders = [...housesState[houseId]];
      ids.forEach((eid) => {
        const ev = lifeEventById(eid);
        if (!ev) return;
        (ev.addsLists || []).forEach((lt) => {
          if (!newLists.some(l => l.id === lt)) {
            newLists.push({ id: lt, type: lt });
          }
        });
        (ev.addsSubjects || []).forEach((sub, i) => {
          const id = `subj-${eid}-${i}-${Math.random().toString(36).slice(2, 6)}`;
          newSubjects.push({
            id, kind: sub.kind, species: sub.species || null,
            name: sub.kind === 'pet'
              ? `New ${sub.species || 'pet'}`
              : sub.kind === 'vehicle' ? 'New vehicle' : 'New subject',
            hex: P.gold,
            emoji: sub.kind === 'pet' ? '🐾'
              : sub.kind === 'vehicle' ? '🚗' : '✦',
          });
        });
        (ev.reminders || []).forEach((r, i) => {
          newReminders.push({
            id: `r-${eid}-${i}-${Math.random().toString(36).slice(2, 6)}`,
            list: r.list, who: 'casa',
            title: r.title, titleEs: r.titleEs,
            note: r.note || null,
            recur: r.recur || null,
            streak: 0, due: null,
            tz_priority: r.tz_priority || 'home',
            done: false,
          });
        });
      });
      // Prototype: mutate the seed in place so the new lists/subjects
      // become visible without a re-render of the whole HOUSES array.
      baseHouse.lists = newLists;
      baseHouse.subjects = newSubjects;
      baseHouse.completedLifeEvents = [
        ...(baseHouse.completedLifeEvents || []), ...ids,
      ];
      setHousesState(prev => ({ ...prev, [houseId]: newReminders }));
    }

    function onSwitch(nextPersonId, nextHouseId) {
      setPersonId(nextPersonId);
      // If the new person isn't a member of the current house, jump them
      // to their first house.
      const stillIn = houseById(nextHouseId)?.memberships
        ?.some(m => m.personId === nextPersonId && m.state === 'active');
      if (stillIn) {
        setHouseId(nextHouseId);
      } else {
        const fallback = housesForPerson(nextPersonId)[0];
        if (fallback) setHouseId(fallback.id);
      }
    }

    return (
      <TweaksCtx.Provider value={T}>
        <Chrome personId={personId} houseId={houseId} onSwitch={onSwitch}
          onSetup={() => setSetupOpen(true)}
          onAjustes={() => setAjustesOpen(true)}>
          <div style={{
            position: 'relative', zIndex: 2,
            display: 'flex', minHeight: 'calc(100vh - 73px)',
          }}>
            <LeftRail house={currentHouse} viewerPersonId={personId}
              active={active} setActive={setActive} counts={counts}
              onInvite={() => setInviteOpen(true)} />
            <MainPane house={currentHouse} viewerPersonId={personId}
              active={active} reminders={reminders}
              onToggle={onToggle} onAdd={onAdd} />
            {wide && active.id !== 'mis-casas' && (
              <RightCol house={currentHouse} viewerPersonId={personId}
                reminders={reminders} />
            )}
          </div>
          <Panel />
          <InviteDrawer open={inviteOpen} onClose={() => setInviteOpen(false)}
            house={currentHouse} viewerPersonId={personId} />
          <SetupDrawer open={setupOpen} onClose={() => setSetupOpen(false)}
            house={currentHouse} onApply={applyLifeEvents} />
          <AjustesDrawer open={ajustesOpen} onClose={() => setAjustesOpen(false)}
            house={currentHouse} viewerPersonId={personId} />
        </Chrome>
      </TweaksCtx.Provider>
    );
  }

  // Keyframes for the pulse dot
  const style = document.createElement('style');
  style.textContent = `
    @keyframes pulseDot {
      0%, 100% { box-shadow: 0 0 0 0 ${P.gold}80; }
      50%      { box-shadow: 0 0 0 6px ${P.gold}00; }
    }
  `;
  document.head.appendChild(style);

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