<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Tarjeta de Cumpleaños · Grupo Carbón</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Pacifico&family=Fraunces:ital,opsz,wght@0,9..144,600;0,9..144,800;1,9..144,700&family=Inter:wght@400;500;600;700;800&family=Caveat:wght@600;700&display=swap" rel="stylesheet" />
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/react@18.3.1/umd/react.development.js" integrity="sha384-hD6/rw4ppMLGNu3tX5cjIb+uRZ7UkRJ6BPkLpg4hAu/6onKUg4lLsHAs9EBPT82L" crossorigin="anonymous"></script>
<script src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.development.js" integrity="sha384-u6aeetuaXnQ38mYT8rp6sbXaQe3NL9t+IBXmnYxwkUI2Hw4bsp2Wvmx4yRQF1uAm" crossorigin="anonymous"></script>
<script src="https://unpkg.com/@babel/standalone@7.29.0/babel.min.js" integrity="sha384-m08KidiNqLdpJqLq95G/LEi8Qvjl/xUYll3QILypMoQ65QorJ9Lvtp2RXYGBFj1y" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/canvas-confetti@1.9.3/dist/confetti.browser.min.js"></script>
<style>
  html,body { margin:0; padding:0; background: #0e0a0a; font-family: 'Inter', system-ui, sans-serif; color:#fff; overflow-x:hidden; }
  .font-vibes { font-family: 'Pacifico', cursive; }
  .font-fraunces { font-family: 'Fraunces', serif; }
  .font-caveat { font-family: 'Caveat', cursive; }
  .grain {
    background-image: radial-gradient(rgba(255,255,255,0.06) 1px, transparent 1px);
    background-size: 3px 3px;
  }
  .soft-shadow { box-shadow: 0 30px 80px -20px rgba(0,0,0,0.6), 0 10px 30px -10px rgba(0,0,0,0.4); }
  @keyframes float { 0%,100% { transform: translateY(0) rotate(0deg); } 50% { transform: translateY(-8px) rotate(3deg); } }
  @keyframes floatSlow { 0%,100% { transform: translateY(0) rotate(0deg); } 50% { transform: translateY(-14px) rotate(-4deg); } }
  @keyframes flicker {
    0%, 100% { transform: translate(-50%, 0) scale(1, 1); opacity: 1; }
    25% { transform: translate(-50%, -1px) scale(0.95, 1.05); opacity: 0.92; }
    50% { transform: translate(-50%, 1px) scale(1.05, 0.95); opacity: 1; }
    75% { transform: translate(-50%, 0) scale(0.98, 1.02); opacity: 0.95; }
  }
  @keyframes smoke {
    0% { opacity: 0.9; transform: translate(-50%, 0) scale(0.6); }
    100% { opacity: 0; transform: translate(-50%, -80px) scale(1.6); }
  }
  @keyframes pulse-ring {
    0% { transform: scale(0.6); opacity: 0.8; }
    100% { transform: scale(2.4); opacity: 0; }
  }
  @keyframes sparkle {
    0%, 100% { opacity: 0; transform: scale(0.4) rotate(0deg); }
    50% { opacity: 1; transform: scale(1) rotate(180deg); }
  }
  .float { animation: float 4s ease-in-out infinite; }
  .float-slow { animation: floatSlow 6s ease-in-out infinite; }
  .flicker { animation: flicker 0.15s infinite; transform-origin: 50% 100%; }
  .sparkle { animation: sparkle 2.5s ease-in-out infinite; }

  /* scratch layer */
  .scratch-canvas { touch-action: none; cursor: grab; }
  .scratch-canvas:active { cursor: grabbing; }

  /* button shine */
  .btn-shine { position: relative; overflow: hidden; }
  .btn-shine::after {
    content: ''; position: absolute; inset: 0;
    background: linear-gradient(120deg, transparent 30%, rgba(255,255,255,0.5) 50%, transparent 70%);
    transform: translateX(-120%);
    transition: transform 0.8s;
  }
  .btn-shine:hover::after { transform: translateX(120%); }

  /* tweaks panel */
  .tweaks-scroll::-webkit-scrollbar { width: 6px; }
  .tweaks-scroll::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.2); border-radius: 3px; }
</style>
</head>
<body>
<div id="root"></div>

<script type="text/babel" data-presets="react">
const { useState, useEffect, useRef, useMemo } = React;

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "empleadoNombre": "JORGE PIZA",
  "aniosServicio": 1,
  "variante": "jardin",
  "beneficio": "Vale de café ☕",
  "beneficioSubtitulo": "Tu dosis de energía por cuenta de la casa",
  "mostrarAniversario": false
}/*EDITMODE-END*/;

/* --------------------------- THEME VARIANTS --------------------------- */
const THEMES = {
  brasa: {
    label: "Brasa (Vino & Saffron)",
    bg: "radial-gradient(ellipse at 20% 10%, #7a1f1f 0%, #3d0d0d 45%, #1a0505 100%)",
    card: "linear-gradient(160deg, #8b2323 0%, #5a1414 60%, #2a0808 100%)",
    accent: "#f5b544",
    accent2: "#ff7849",
    cream: "#fff6e8",
    ribbon: "#f5b544",
    scratchColor: ["#f5b544", "#e89130"],
    confetti: ["#f5b544", "#ff7849", "#fff6e8", "#c9302c", "#ffdb7a"],
  },
  medianoche: {
    label: "Medianoche (Vino profundo)",
    bg: "radial-gradient(ellipse at 80% 20%, #1f1847 0%, #0e0a2a 55%, #05030f 100%)",
    card: "linear-gradient(160deg, #2a1f5e 0%, #1a1240 55%, #0a0620 100%)",
    accent: "#ffc857",
    accent2: "#ff6b9d",
    cream: "#fdf4e3",
    ribbon: "#ffc857",
    scratchColor: ["#ffc857", "#ff9d5c"],
    confetti: ["#ffc857", "#ff6b9d", "#9b7cff", "#fdf4e3", "#5ce1ff"],
  },
  cobre: {
    label: "Cobre (Terracota cálido)",
    bg: "radial-gradient(ellipse at 30% 90%, #7a3517 0%, #3d1a08 50%, #1a0a04 100%)",
    card: "linear-gradient(160deg, #a04820 0%, #6e2e12 55%, #331306 100%)",
    accent: "#ffd27a",
    accent2: "#ff9b5c",
    cream: "#fff2dd",
    ribbon: "#ffd27a",
    scratchColor: ["#ffd27a", "#e89a4a"],
    confetti: ["#ffd27a", "#ff9b5c", "#fff2dd", "#c25c2a", "#5c9b8b"],
  },
  jardin: {
    label: "Jardín (Verde & Lima)",
    bg: "radial-gradient(ellipse at 70% 15%, #1a4d3a 0%, #0c2a20 55%, #050f0b 100%)",
    card: "linear-gradient(160deg, #1f6b4f 0%, #114433 55%, #0a2419 100%)",
    accent: "#d4e85c",
    accent2: "#ff9a3c",
    cream: "#f4fce8",
    ribbon: "#d4e85c",
    scratchColor: ["#d4e85c", "#9dbf42"],
    confetti: ["#d4e85c", "#ff9a3c", "#f4fce8", "#52b788", "#ffd166"],
  },
  rosa: {
    label: "Rosa (Rosé & Champagne)",
    bg: "radial-gradient(ellipse at 25% 30%, #6b2847 0%, #3a1127 55%, #180610 100%)",
    card: "linear-gradient(160deg, #8a3558 0%, #5a1e3a 55%, #2a0c1c 100%)",
    accent: "#f5d48a",
    accent2: "#ff8fb8",
    cream: "#fff0e8",
    ribbon: "#f5d48a",
    scratchColor: ["#f5d48a", "#e89ba8"],
    confetti: ["#f5d48a", "#ff8fb8", "#fff0e8", "#c94a7a", "#ffd4a8"],
  },
};

/* --------------------------- CONFETTI HELPERS --------------------------- */
function fireConfetti(colors) {
  if (!window.confetti) return;
  const end = Date.now() + 800;
  (function frame() {
    window.confetti({ particleCount: 4, angle: 60, spread: 70, origin: { x: 0, y: 0.7 }, colors });
    window.confetti({ particleCount: 4, angle: 120, spread: 70, origin: { x: 1, y: 0.7 }, colors });
    if (Date.now() < end) requestAnimationFrame(frame);
  })();
  window.confetti({ particleCount: 120, spread: 100, startVelocity: 45, origin: { y: 0.35 }, colors });
}

function bigBurst(colors) {
  if (!window.confetti) return;
  window.confetti({ particleCount: 200, spread: 160, startVelocity: 55, origin: { y: 0.5 }, colors, scalar: 1.2 });
  setTimeout(() => window.confetti({ particleCount: 80, spread: 100, startVelocity: 35, origin: { y: 0.4 }, colors, shapes: ['circle'] }), 200);
}

/* --------------------------- DECORATIVE SVGs --------------------------- */
// Original cake illustration (no branded logos) — drawn from primitives
const Cake = ({ theme, candleLit, years }) => (
  <svg viewBox="0 0 240 260" className="w-full h-full">
    {/* plate */}
    <ellipse cx="120" cy="235" rx="105" ry="12" fill="rgba(0,0,0,0.35)" />
    <ellipse cx="120" cy="230" rx="100" ry="10" fill={theme.cream} opacity="0.9" />
    {/* bottom tier */}
    <rect x="35" y="175" width="170" height="55" rx="6" fill={theme.accent2}/>
    <path d="M35 180 Q50 170 75 178 T115 178 T155 178 T205 180 L205 200 L35 200 Z" fill={theme.cream} opacity="0.95"/>
    {/* drips */}
    <path d="M50 198 Q55 215 60 200 M85 200 Q90 220 95 202 M135 199 Q140 218 145 201 M175 200 Q180 216 185 202" stroke={theme.cream} strokeWidth="0" fill={theme.cream}/>
    <circle cx="60" cy="213" r="3" fill={theme.cream}/>
    <circle cx="95" cy="216" r="3.5" fill={theme.cream}/>
    <circle cx="145" cy="214" r="3" fill={theme.cream}/>
    <circle cx="185" cy="215" r="3.2" fill={theme.cream}/>
    {/* sprinkles on bottom */}
    {[...Array(14)].map((_,i) => (
      <rect key={i} x={45 + i*11} y={210 + (i%3)*4} width="6" height="2" rx="1"
        fill={i%3===0?theme.accent:i%3===1?theme.accent2:theme.cream}
        transform={`rotate(${i*27} ${48+i*11} ${211+(i%3)*4})`}/>
    ))}
    {/* top tier */}
    <rect x="65" y="125" width="110" height="55" rx="6" fill={theme.accent}/>
    <path d="M65 130 Q80 120 105 128 T145 128 T175 130 L175 150 L65 150 Z" fill={theme.cream} opacity="0.95"/>
    <circle cx="85" cy="162" r="3" fill={theme.accent2}/>
    <circle cx="115" cy="164" r="3.5" fill={theme.accent2}/>
    <circle cx="145" cy="162" r="3" fill={theme.accent2}/>
    {/* number plaque (only for anniversary) */}
    {years != null && (
      <g>
        <rect x="95" y="138" width="50" height="26" rx="4" fill="#fff" stroke={theme.accent2} strokeWidth="2"/>
        <text x="120" y="157" textAnchor="middle" fontFamily="Fraunces, serif" fontWeight="800" fontSize="18" fill="#3d1212">{years}</text>
      </g>
    )}
    {/* candle */}
    <rect x="115" y="90" width="10" height="38" rx="2" fill={theme.cream}/>
    <rect x="115" y="90" width="10" height="38" rx="2" fill="url(#candleStripe)" opacity="0.6"/>
    <defs>
      <pattern id="candleStripe" width="5" height="8" patternUnits="userSpaceOnUse" patternTransform="rotate(25)">
        <rect width="2.5" height="8" fill={theme.accent2}/>
      </pattern>
    </defs>
    {/* wick */}
    <rect x="119.2" y="85" width="1.6" height="8" fill="#1a0a04"/>
    {/* flame */}
    {candleLit && (
      <g className="flicker" style={{transformOrigin: "120px 85px"}}>
        <ellipse cx="120" cy="78" rx="6" ry="10" fill="#ff9b1a"/>
        <ellipse cx="120" cy="76" rx="3.5" ry="7" fill="#ffe066"/>
        <ellipse cx="120" cy="74" rx="1.8" ry="4" fill="#fff"/>
        <circle cx="120" cy="78" r="15" fill="#ffb347" opacity="0.25"/>
      </g>
    )}
  </svg>
);

const StarSparkle = ({ className, style }) => (
  <svg viewBox="0 0 24 24" className={className} style={style}>
    <path d="M12 2 L14 10 L22 12 L14 14 L12 22 L10 14 L2 12 L10 10 Z" fill="currentColor"/>
  </svg>
);

const Balloon = ({ color, stringColor = "rgba(255,255,255,0.4)" }) => (
  <svg viewBox="0 0 60 90" className="w-full h-full">
    <ellipse cx="30" cy="32" rx="22" ry="28" fill={color}/>
    <ellipse cx="22" cy="24" rx="5" ry="8" fill="rgba(255,255,255,0.35)"/>
    <path d="M30 60 L28 66 L32 66 Z" fill={color}/>
    <path d="M30 66 Q34 75 28 82 Q32 86 30 90" stroke={stringColor} strokeWidth="1" fill="none"/>
  </svg>
);

const PartyHat = ({ color, stripe }) => (
  <svg viewBox="0 0 60 80" className="w-full h-full">
    <path d="M30 5 L55 70 L5 70 Z" fill={color}/>
    <path d="M20 45 L40 45 M15 58 L45 58" stroke={stripe} strokeWidth="3" strokeLinecap="round"/>
    <circle cx="30" cy="5" r="5" fill={stripe}/>
  </svg>
);

const Gift = ({ color1, color2, ribbon }) => (
  <svg viewBox="0 0 80 80" className="w-full h-full">
    <rect x="8" y="30" width="64" height="45" rx="3" fill={color1}/>
    <rect x="8" y="30" width="64" height="8" fill={color2} opacity="0.4"/>
    <rect x="35" y="30" width="10" height="45" fill={ribbon}/>
    <path d="M25 30 Q20 15 30 15 Q40 15 40 30 Q40 15 50 15 Q60 15 55 30" stroke={ribbon} strokeWidth="4" fill="none"/>
  </svg>
);

/* --------------------------- LOGO (GRUPO CARBÓN) --------------------------- */
const BrandLogo = ({ theme, size = 48 }) => (
  <div className="flex items-center gap-2.5" style={{ height: size }}>
    <div className="relative flex items-center justify-center rounded-full" style={{
      width: size, height: size,
      background: `radial-gradient(circle at 35% 30%, #ff5a3c 0%, #c9302c 55%, #7a1a14 100%)`,
      boxShadow: `0 4px 14px rgba(201,48,44,0.55), inset 0 -4px 10px rgba(0,0,0,0.3)`
    }}>
      <span className="font-fraunces font-extrabold text-white" style={{fontSize: size*0.55, lineHeight: 1, textShadow: '0 2px 4px rgba(0,0,0,0.3)'}}>C</span>
      {/* ember glow */}
      <div className="absolute inset-0 rounded-full pointer-events-none" style={{
        background: 'radial-gradient(circle at 30% 25%, rgba(255,220,150,0.6) 0%, transparent 40%)'
      }}/>
    </div>
    <div className="flex flex-col leading-none">
      <span className="font-fraunces font-extrabold text-white" style={{fontSize: size*0.36, letterSpacing: '0.04em'}}>GRUPO CARBÓN</span>
      <span className="font-inter text-[9px] tracking-[0.3em] uppercase" style={{color: theme.accent, marginTop: 3}}>Restaurantes</span>
    </div>
  </div>
);

/* --------------------------- SISTER BRAND CHIPS (real group) --------------------------- */
const sisterBrands = [
  { name: "Don V",        src: "logos/donv.png",             h: 44 },
  { name: "Carbón",       src: "logos/carbon.png",           h: 38 },
  { name: "Casa Dolores", src: "logos/casa-dolores.png",     h: 34 },
  { name: "Chapultepec",  src: "logos/chapultepec-cap.png",  h: 46 },
];

const BrandChip = ({ b }) => (
  <div className="flex items-center justify-center rounded-lg py-2 px-2"
       style={{background: "#fff", height: 64, boxShadow: "0 4px 12px rgba(0,0,0,0.22)"}}>
    <img src={b.src} alt={b.name} style={{height: b.h, maxWidth: '100%', objectFit: 'contain'}}/>
  </div>
);

const BrandRow = ({ theme }) => (
  <div className="grid grid-cols-4 gap-1.5 px-2 py-2 rounded-2xl" style={{background: "rgba(0,0,0,0.28)", backdropFilter: "blur(4px)"}}>
    {sisterBrands.map((b,i) => <BrandChip key={i} b={b}/>)}
  </div>
);

/* --------------------------- SCRATCH-TO-REVEAL --------------------------- */
const ScratchCard = ({ theme, beneficio, subtitulo, onRevealed }) => {
  const canvasRef = useRef(null);
  const [revealed, setRevealed] = useState(false);
  const [progress, setProgress] = useState(0);
  const isDrawing = useRef(false);
  const lastPos = useRef(null);

  useEffect(() => {
    const c = canvasRef.current; if (!c) return;
    const dpr = window.devicePixelRatio || 1;
    const rect = c.getBoundingClientRect();
    c.width = rect.width * dpr;
    c.height = rect.height * dpr;
    const ctx = c.getContext('2d');
    ctx.scale(dpr, dpr);
    const grad = ctx.createLinearGradient(0, 0, rect.width, rect.height);
    grad.addColorStop(0, theme.scratchColor[0]);
    grad.addColorStop(1, theme.scratchColor[1]);
    ctx.fillStyle = grad;
    ctx.fillRect(0, 0, rect.width, rect.height);
    // label
    ctx.fillStyle = "rgba(0,0,0,0.55)";
    ctx.font = "700 13px Inter, sans-serif";
    ctx.textAlign = "center";
    ctx.fillText("RASCA AQUÍ  ✦  TU SORPRESA", rect.width/2, rect.height/2 - 6);
    ctx.font = "600 11px Inter, sans-serif";
    ctx.fillText("desliza con el dedo o el cursor", rect.width/2, rect.height/2 + 14);
    // decorative dots
    for (let i=0; i<40; i++) {
      ctx.fillStyle = `rgba(0,0,0,${0.05 + Math.random()*0.08})`;
      ctx.beginPath();
      ctx.arc(Math.random()*rect.width, Math.random()*rect.height, Math.random()*3+1, 0, Math.PI*2);
      ctx.fill();
    }
    ctx.globalCompositeOperation = 'destination-out';
  }, [theme]);

  const getPos = (e) => {
    const c = canvasRef.current;
    const rect = c.getBoundingClientRect();
    const t = e.touches ? e.touches[0] : e;
    return { x: t.clientX - rect.left, y: t.clientY - rect.top };
  };

  const scratch = (e) => {
    if (!isDrawing.current || revealed) return;
    e.preventDefault();
    const c = canvasRef.current; const ctx = c.getContext('2d');
    const p = getPos(e);
    ctx.lineWidth = 40;
    ctx.lineCap = 'round';
    ctx.lineJoin = 'round';
    ctx.beginPath();
    if (lastPos.current) {
      ctx.moveTo(lastPos.current.x, lastPos.current.y);
      ctx.lineTo(p.x, p.y);
      ctx.stroke();
    } else {
      ctx.arc(p.x, p.y, 22, 0, Math.PI*2);
      ctx.fill();
    }
    lastPos.current = p;
    // progress check (sample)
    const rect = c.getBoundingClientRect();
    const dpr = window.devicePixelRatio || 1;
    const img = ctx.getImageData(0, 0, c.width, c.height).data;
    let cleared = 0;
    const step = 40 * dpr;
    for (let i = 3; i < img.length; i += step*4) if (img[i] < 50) cleared++;
    const total = img.length / (step*4);
    const pct = cleared / total;
    setProgress(pct);
    if (pct > 0.5 && !revealed) {
      setRevealed(true);
      ctx.clearRect(0, 0, c.width, c.height);
      onRevealed && onRevealed();
    }
  };

  const start = (e) => { isDrawing.current = true; lastPos.current = null; scratch(e); };
  const end = () => { isDrawing.current = false; lastPos.current = null; };

  return (
    <div className="relative w-full rounded-2xl overflow-hidden soft-shadow"
         style={{ aspectRatio: '3 / 1.25', background: `linear-gradient(135deg, ${theme.cream} 0%, #fff 100%)` }}>
      {/* revealed content */}
      <div className="absolute inset-0 flex flex-col items-center justify-center px-6 text-center">
        <div className="flex items-center gap-2 mb-1.5">
          <StarSparkle className="w-3.5 h-3.5" style={{color: theme.accent2}}/>
          <span className="text-[10px] tracking-[0.3em] font-bold uppercase" style={{color: theme.accent2}}>TU REGALO</span>
          <StarSparkle className="w-3.5 h-3.5" style={{color: theme.accent2}}/>
        </div>
        <div className="font-fraunces font-extrabold leading-none"
             style={{color: '#3d1212', fontSize: 'clamp(22px, 6vw, 34px)'}}>
          {beneficio}
        </div>
        <div className="font-inter text-[11px] mt-1.5 px-3" style={{color: '#6b3939'}}>{subtitulo}</div>
      </div>
      {/* scratch canvas overlay */}
      <canvas
        ref={canvasRef}
        className="scratch-canvas absolute inset-0 w-full h-full"
        style={{opacity: revealed ? 0 : 1, transition: 'opacity 0.4s'}}
        onMouseDown={start} onMouseMove={scratch} onMouseUp={end} onMouseLeave={end}
        onTouchStart={start} onTouchMove={scratch} onTouchEnd={end}
      />
      {revealed && (
        <div className="absolute top-2 right-3 text-[10px] font-bold tracking-wider" style={{color: theme.accent2}}>¡REVELADO!</div>
      )}
    </div>
  );
};

/* --------------------------- GUESTBOOK (memoria por persona, con histórico) --------------------------- */
/*
  PERSISTENCIA: Google Sheets vía Apps Script Web App.
  Cada firma es una fila nueva en el Sheet "Carbón - Memoria de Cumpleaños".
  Si falla la red (modo offline / sin permisos), cae a localStorage.
*/
const GUESTBOOK_CONFIG = {
  sheetUrl: "https://script.google.com/macros/s/AKfycbxWtaC4D-zODIBYOa5qQn9hAM9PvbTC2bBGUlZY1l4G4aQgOxiliXtSRUHXpwUAi_Ax/exec",
  localKey: "carbon_guestbook_v2",
};

function slugifyName(s) {
  return (s || "")
    .toLowerCase()
    .normalize("NFD").replace(/[\u0300-\u036f]/g, "")
    .replace(/[^a-z0-9]+/g, "-")
    .replace(/^-|-$/g, "") || "sin-nombre";
}

// Convierte array de filas del Sheet -> estructura { people: { slug: { name, entries[] } } }
function entriesToBook(entries) {
  const people = {};
  (entries || []).forEach(r => {
    const slug = r.slug || slugifyName(r.nombreFestejado);
    if (!people[slug]) people[slug] = { name: r.nombreFestejado || "", entries: [] };
    people[slug].name = r.nombreFestejado || people[slug].name;
    people[slug].entries.push({
      id: r.id,
      name: r.nombreFirmante,
      text: r.mensaje,
      date: r.fecha,
      year: Number(r.año) || new Date(r.fecha).getFullYear(),
      color: Number(r.color) || 0,
      tilt: r.tilt || 0,
    });
  });
  // newest first within each person
  Object.values(people).forEach(p => p.entries.sort((a,b) => new Date(b.date) - new Date(a.date)));
  return { people };
}

async function fetchBook() {
  const { sheetUrl, localKey } = GUESTBOOK_CONFIG;
  if (sheetUrl) {
    try {
      const r = await fetch(sheetUrl, { method: "GET" });
      const j = await r.json();
      const book = entriesToBook(j.entries || []);
      // cache locally so next load is instant even if offline
      try { localStorage.setItem(localKey, JSON.stringify(book)); } catch {}
      return book;
    } catch (e) { console.warn("Sheet read failed, using local cache", e); }
  }
  try { return JSON.parse(localStorage.getItem(localKey) || '{"people":{}}'); }
  catch { return { people: {} }; }
}

// Envía UNA entrada nueva al Sheet (append). Si falla, solo queda en cache local.
async function appendEntry(festejado, entry) {
  const { sheetUrl, localKey } = GUESTBOOK_CONFIG;
  const row = {
    id: entry.id,
    slug: slugifyName(festejado),
    nombreFestejado: festejado,
    nombreFirmante: entry.name,
    mensaje: entry.text,
    fecha: entry.date,
    "año": entry.year,
    color: entry.color,
    tilt: entry.tilt,
  };
  if (sheetUrl) {
    try {
      // text/plain evita el preflight CORS de Apps Script
      await fetch(sheetUrl, {
        method: "POST",
        mode: "no-cors",
        headers: { "Content-Type": "text/plain;charset=utf-8" },
        body: JSON.stringify(row),
      });
    } catch (e) { console.warn("Sheet write failed, kept local only", e); }
  }
}

// Cache local completa (para que el usuario vea su mensaje inmediatamente)
function cacheBookLocally(book) {
  try { localStorage.setItem(GUESTBOOK_CONFIG.localKey, JSON.stringify(book)); } catch {}
}

const POSTIT_COLORS = [
  { bg: "#fff59d", tape: "rgba(255,220,130,0.7)" },
  { bg: "#ffccbc", tape: "rgba(255,180,150,0.7)" },
  { bg: "#c5e1a5", tape: "rgba(200,220,150,0.7)" },
  { bg: "#b3e5fc", tape: "rgba(180,220,240,0.7)" },
  { bg: "#f8bbd0", tape: "rgba(240,180,200,0.7)" },
  { bg: "#fff1c2", tape: "rgba(245,220,160,0.7)" },
];

function Guestbook({ theme, employeeName }) {
  const slug = slugifyName(employeeName);
  const currentYear = new Date().getFullYear();
  const [book, setBook]         = React.useState({ people: {} });
  const [open, setOpen]         = React.useState(false);
  const [name, setName]         = React.useState("");
  const [msg, setMsg]           = React.useState("");
  const [sending, setSending]   = React.useState(false);
  const [loaded, setLoaded]     = React.useState(false);
  const [viewYear, setViewYear] = React.useState(currentYear);

  React.useEffect(() => {
    let live = true;
    fetchBook().then(b => { if (live) { setBook(b); setLoaded(true); } });
    return () => { live = false; };
  }, []);

  // reset viewYear to current when person changes
  React.useEffect(() => { setViewYear(currentYear); }, [slug]);

  const person = book.people?.[slug];
  const allEntries = person?.entries || [];
  const years = Array.from(new Set(allEntries.map(e => e.year))).sort((a,b) => b-a);
  if (!years.includes(currentYear)) years.unshift(currentYear);
  const entriesForYear = allEntries.filter(e => e.year === viewYear);

  const submit = async (e) => {
    e.preventDefault();
    const n = name.trim(); const t = msg.trim();
    if (!n || !t) return;
    setSending(true);
    const entry = {
      id: Date.now() + "_" + Math.random().toString(36).slice(2,7),
      name: n, text: t,
      date: new Date().toISOString(),
      year: currentYear,
      color: Math.floor(Math.random() * POSTIT_COLORS.length),
      tilt: (Math.random() * 6 - 3).toFixed(2),
    };
    const next = JSON.parse(JSON.stringify(book.people ? book : { people: {} }));
    if (!next.people[slug]) next.people[slug] = { name: employeeName, entries: [] };
    next.people[slug].name = employeeName; // keep latest display name
    next.people[slug].entries.unshift(entry);
    setBook(next);
    setViewYear(currentYear);
    cacheBookLocally(next);
    await appendEntry(employeeName, entry);
    setName(""); setMsg(""); setOpen(false); setSending(false);
    bigBurst(theme.confetti);
  };

  const fmtDate = (iso) => {
    try {
      const d = new Date(iso);
      return d.toLocaleDateString('es-MX', { day:'numeric', month:'short' });
    } catch { return ""; }
  };

  const firstName = (employeeName || "").split(" ")[0];

  return (
    <div>
      <div className="text-center mb-2">
        <span className="font-inter text-[9px] tracking-[0.35em] uppercase" style={{color: theme.accent, opacity: 0.85}}>
          Memoria de compañeros
        </span>
        <div className="font-caveat mt-0.5" style={{color: theme.cream, fontSize: 17, opacity: 0.85}}>
          Déjale un mensaje a {firstName || "tu compañero/a"}
        </div>
      </div>

      <div className="rounded-2xl p-3" style={{background: "rgba(0,0,0,0.28)", backdropFilter: "blur(4px)"}}>
        {/* year tabs (only if there's history) */}
        {years.length > 1 && (
          <div className="flex gap-1 mb-2 overflow-x-auto pb-1">
            {years.map(y => (
              <button key={y} onClick={() => setViewYear(y)}
                className="px-2.5 py-1 rounded-full text-[10px] font-fraunces font-extrabold tracking-wider shrink-0"
                style={{
                  background: y === viewYear ? theme.accent : "rgba(255,255,255,0.08)",
                  color: y === viewYear ? "#3d1212" : theme.cream,
                  border: `1px solid ${theme.accent}33`
                }}>
                {y} {y === currentYear ? "• hoy" : ""}
              </button>
            ))}
          </div>
        )}

        {loaded && entriesForYear.length === 0 && (
          <div className="text-center py-5 font-inter text-[11px]" style={{color: theme.cream, opacity: 0.6}}>
            {viewYear === currentYear
              ? "Sé el primero en dejar un mensaje ✨"
              : `Sin mensajes en ${viewYear}`}
          </div>
        )}
        {entriesForYear.length > 0 && (
          <div className="grid grid-cols-2 gap-2 max-h-[260px] overflow-y-auto pr-1" style={{scrollbarWidth:"thin"}}>
            {entriesForYear.slice(0, 60).map((m, i) => {
              const c = POSTIT_COLORS[m.color % POSTIT_COLORS.length];
              return (
                <div key={m.id || i} className="relative rounded-md p-2.5"
                     style={{
                       background: c.bg, color: "#3d2a0e",
                       transform: `rotate(${m.tilt || 0}deg)`,
                       boxShadow: "0 6px 12px rgba(0,0,0,0.25), 0 2px 4px rgba(0,0,0,0.15)",
                       minHeight: 74
                     }}>
                  <div className="absolute -top-1.5 left-1/2 -translate-x-1/2 w-10 h-3 rounded-sm"
                       style={{background: c.tape, boxShadow: "0 1px 2px rgba(0,0,0,0.1)"}}/>
                  <div className="font-caveat leading-tight" style={{fontSize: 15}}>
                    "{m.text}"
                  </div>
                  <div className="flex justify-between items-end mt-1.5">
                    <div className="font-fraunces font-extrabold text-[10px] truncate">— {m.name}</div>
                    <div className="font-inter text-[8px] opacity-60 shrink-0 ml-1">{fmtDate(m.date)}</div>
                  </div>
                </div>
              );
            })}
          </div>
        )}

        {/* add button / form — only on current year */}
        {viewYear === currentYear && !open && (
          <button onClick={() => setOpen(true)}
            className="w-full mt-3 rounded-xl py-2.5 font-fraunces font-extrabold text-[12px] tracking-wide transition-transform active:scale-95 hover:scale-[1.02]"
            style={{
              background: `linear-gradient(135deg, ${theme.accent} 0%, ${theme.accent2} 100%)`,
              color: '#3d1212',
              boxShadow: `0 6px 16px -6px ${theme.accent2}88`
            }}>
            ✍️  DEJAR UN MENSAJE
          </button>
        )}
        {viewYear !== currentYear && (
          <button onClick={() => setViewYear(currentYear)}
            className="w-full mt-3 rounded-xl py-2 font-inter text-[11px]"
            style={{background: "rgba(255,255,255,0.08)", color: theme.cream, border: `1px solid ${theme.accent}22`}}>
            ← Volver a {currentYear}
          </button>
        )}
        {viewYear === currentYear && open && (
          <form onSubmit={submit} className="mt-3 space-y-2">
            <input
              type="text" value={name} onChange={e => setName(e.target.value)}
              placeholder="Tu nombre" maxLength={40} required
              className="w-full rounded-lg px-3 py-2 text-[13px] font-inter"
              style={{background: "rgba(255,255,255,0.1)", color: theme.cream, border: `1px solid ${theme.accent}33`}}
            />
            <textarea
              value={msg} onChange={e => setMsg(e.target.value)}
              placeholder={`Escríbele algo bonito a ${firstName || "tu compañero/a"}...`}
              maxLength={200} rows={3} required
              className="w-full rounded-lg px-3 py-2 text-[13px] font-inter resize-none"
              style={{background: "rgba(255,255,255,0.1)", color: theme.cream, border: `1px solid ${theme.accent}33`}}
            />
            <div className="flex gap-2">
              <button type="submit" disabled={sending}
                className="flex-1 rounded-lg py-2 font-fraunces font-extrabold text-[12px] tracking-wide disabled:opacity-60"
                style={{
                  background: `linear-gradient(135deg, ${theme.accent} 0%, ${theme.accent2} 100%)`,
                  color: '#3d1212',
                }}>
                {sending ? "Enviando..." : "🎉 FIRMAR"}
              </button>
              <button type="button" onClick={() => { setOpen(false); setName(""); setMsg(""); }}
                className="rounded-lg px-4 py-2 font-inter text-[11px]"
                style={{background: "rgba(255,255,255,0.08)", color: theme.cream, border: `1px solid ${theme.accent}22`}}>
                Cancelar
              </button>
            </div>
            <div className="font-inter text-[9px] opacity-55 text-center" style={{color: theme.cream}}>
              {msg.length}/200 · {GUESTBOOK_CONFIG.sheetUrl ? "se guarda en la nube para todos" : "se guarda en este dispositivo"}
            </div>
          </form>
        )}
      </div>

      {/* historial footer */}
      {loaded && allEntries.length > 0 && (
        <div className="text-center mt-2 font-inter text-[9px]" style={{color: theme.cream, opacity: 0.55}}>
          📖 {allEntries.length} mensaje{allEntries.length!==1?"s":""} en su historial
          {years.length > 1 ? ` · ${years.length} años` : ""}
        </div>
      )}
    </div>
  );
}

/* --------------------------- MAIN CARD --------------------------- */
function BirthdayCard({ tweaks, onTweakChange }) {
  const theme = THEMES[tweaks.variante] || THEMES.brasa;
  const [candleLit, setCandleLit] = useState(true);
  const [blowing, setBlowing] = useState(false);
  const [scratchDone, setScratchDone] = useState(false);
  const [greeted, setGreeted] = useState(false);
  const cardRef = useRef(null);

  // Auto confetti on load
  useEffect(() => {
    const t = setTimeout(() => { fireConfetti(theme.confetti); setGreeted(true); }, 500);
    return () => clearTimeout(t);
  }, []);

  // Re-fire on variant change
  useEffect(() => {
    if (greeted) fireConfetti(theme.confetti);
  }, [tweaks.variante]);

  const blowCandle = () => {
    if (!candleLit) { setCandleLit(true); return; }
    setBlowing(true);
    setTimeout(() => { setCandleLit(false); setBlowing(false); fireConfetti(theme.confetti); }, 400);
  };

  const handleCelebrate = () => bigBurst(theme.confetti);

  const firstName = (tweaks.empleadoNombre || "").split(" ")[0] || "CAMPEÓN/A";

  return (
    <div className="min-h-screen w-full flex items-center justify-center py-6 px-3" style={{ background: theme.bg }}>
      {/* ambient background sparkles */}
      <div className="absolute inset-0 pointer-events-none overflow-hidden">
        {[...Array(22)].map((_,i) => (
          <div key={i} className="sparkle absolute" style={{
            top: `${Math.random()*100}%`, left: `${Math.random()*100}%`,
            animationDelay: `${Math.random()*3}s`,
            color: i%2?theme.accent:theme.accent2,
          }}>
            <StarSparkle className="w-3 h-3"/>
          </div>
        ))}
      </div>

      {/* CARD */}
      <div ref={cardRef} className="relative w-full max-w-[420px] rounded-[32px] overflow-hidden soft-shadow"
           style={{ background: theme.card, border: `1px solid ${theme.accent}33` }}>
        {/* inner grain + texture */}
        <div className="absolute inset-0 grain opacity-40 pointer-events-none"/>
        <div className="absolute inset-0 pointer-events-none" style={{
          background: `radial-gradient(circle at 50% 0%, ${theme.accent}22 0%, transparent 55%)`
        }}/>

        {/* ribbon corner (only for anniversary) */}
        {tweaks.mostrarAniversario && (
          <div className="absolute -top-1 -right-1 z-10">
            <div className="relative px-5 py-1.5 font-fraunces font-extrabold text-[11px] tracking-[0.2em]"
                 style={{ background: theme.accent, color: '#3d1212',
                          transform: 'rotate(8deg) translate(10px, 12px)', boxShadow: '0 6px 14px rgba(0,0,0,0.3)' }}>
              {`AÑO #${tweaks.aniosServicio}`}
            </div>
          </div>
        )}

        {/* HEADER: logo */}
        <div className="relative z-10 flex justify-center pt-7 pb-2">
          <BrandLogo theme={theme} size={44}/>
        </div>

        {/* "Te desea un" */}
        <div className="relative z-10 text-center mt-3 mb-1">
          <div className="inline-flex items-center gap-2">
            <div className="h-px w-8" style={{background: theme.accent}}/>
            <span className="font-inter text-[10px] tracking-[0.4em] uppercase" style={{color: theme.accent}}>
              Te deseamos un
            </span>
            <div className="h-px w-8" style={{background: theme.accent}}/>
          </div>
        </div>

        {/* Hero lettering */}
        <div className="relative z-10 text-center px-6">
          <div className="font-vibes leading-[0.95]" style={{
            color: theme.cream,
            fontSize: 'clamp(40px, 11vw, 58px)',
            textShadow: `0 4px 20px rgba(0,0,0,0.35), 0 0 40px ${theme.accent}33`
          }}>
            ¡Feliz
          </div>
          <div className="font-vibes leading-[0.95] mt-1" style={{
            color: theme.cream,
            fontSize: 'clamp(44px, 12vw, 64px)',
            textShadow: `0 4px 20px rgba(0,0,0,0.35), 0 0 40px ${theme.accent}33`
          }}>
            {tweaks.mostrarAniversario ? 'Aniversario!' : 'Cumpleaños!'}
          </div>
        </div>

        {/* Floating decorations around cake */}
        <div className="relative z-10 w-full flex justify-center mt-2 mb-3">
          <div className="relative w-[280px] h-[260px]">
            {/* floaters */}
            <div className="absolute -top-2 -left-6 w-14 h-20 float" style={{animationDelay:'0s'}}>
              <Balloon color={theme.accent2}/>
            </div>
            <div className="absolute -top-4 right-0 w-12 h-16 float-slow" style={{animationDelay:'1s'}}>
              <Balloon color={theme.accent}/>
            </div>
            <div className="absolute top-6 -right-8 w-10 h-12 float" style={{animationDelay:'2s'}}>
              <PartyHat color={theme.accent} stripe={theme.accent2}/>
            </div>
            <div className="absolute bottom-4 -left-10 w-12 h-12 float-slow" style={{animationDelay:'0.5s'}}>
              <Gift color1={theme.accent2} color2="#ffffff" ribbon={theme.accent}/>
            </div>
            {/* sparkles */}
            <StarSparkle className="absolute top-10 left-4 w-4 h-4 sparkle" style={{color: theme.accent}}/>
            <StarSparkle className="absolute top-24 right-6 w-5 h-5 sparkle" style={{color: theme.cream, animationDelay:'0.8s'}}/>
            <StarSparkle className="absolute bottom-16 right-2 w-3 h-3 sparkle" style={{color: theme.accent2, animationDelay:'1.5s'}}/>

            {/* cake (interactive) */}
            <button onClick={blowCandle}
              className="relative w-full h-full block focus:outline-none"
              title={candleLit ? "Sopla la velita" : "Enciéndela otra vez"}>
              <Cake theme={theme} candleLit={candleLit} years={tweaks.mostrarAniversario ? tweaks.aniosServicio : null}/>
              {/* pulse ring when candle lit */}
              {candleLit && (
                <div className="absolute left-1/2 top-[24%] -translate-x-1/2 w-10 h-10 rounded-full pointer-events-none"
                     style={{ border: `2px solid ${theme.accent}`, animation: 'pulse-ring 2s ease-out infinite' }}/>
              )}
              {/* smoke puff */}
              {blowing && (
                <>
                  <div className="absolute left-1/2 top-[22%] w-6 h-6 rounded-full pointer-events-none"
                       style={{ background: 'rgba(255,255,255,0.4)', animation: 'smoke 0.8s ease-out forwards' }}/>
                  <div className="absolute left-[56%] top-[24%] w-4 h-4 rounded-full pointer-events-none"
                       style={{ background: 'rgba(255,255,255,0.3)', animation: 'smoke 1s ease-out forwards', animationDelay: '0.1s' }}/>
                </>
              )}
            </button>
            {/* hint */}
            <div className="absolute bottom-[-6px] left-1/2 -translate-x-1/2 font-caveat text-center whitespace-nowrap"
                 style={{ color: theme.cream, opacity: 0.75, fontSize: 16 }}>
              {candleLit ? "← toca la velita y pide un deseo" : "✨ ¡deseo concedido!"}
            </div>
          </div>
        </div>

        {/* Name plate */}
        <div className="relative z-10 mx-5 mt-4 rounded-2xl p-[2px]" style={{
          background: `linear-gradient(135deg, ${theme.accent} 0%, ${theme.accent2} 100%)`,
          boxShadow: `0 10px 30px -8px ${theme.accent2}66`
        }}>
          <div className="rounded-[14px] px-5 py-3 text-center" style={{ background: theme.cream }}>
            <div className="font-inter text-[9px] tracking-[0.35em] uppercase" style={{color: theme.accent2, opacity: 0.8}}>
              {tweaks.mostrarAniversario ? "Celebramos a" : "Para"}
            </div>
            <div className="font-fraunces font-extrabold leading-tight mt-0.5"
                 style={{ color: '#3d1212', fontSize: 'clamp(18px, 5.5vw, 24px)', letterSpacing: '0.02em' }}>
              {tweaks.empleadoNombre || "CAMPEÓN/A"}
            </div>
          </div>
        </div>

        {/* Message */}
        <div className="relative z-10 px-7 mt-5 text-center">
          <p className="font-inter text-[12px] leading-relaxed" style={{color: theme.cream, opacity: 0.92}}>
            <span className="font-semibold" style={{color: theme.accent}}>¡Feliz cumpleaños!</span>
            {" "}Gracias por tu energía, compromiso y por ser parte esencial de esta gran familia.
            Que este nuevo año esté lleno de logros dentro y fuera del servicio.
          </p>
        </div>

        {/* SCRATCH */}
        <div className="relative z-10 px-5 mt-5">
          <ScratchCard theme={theme}
                       beneficio={tweaks.beneficio}
                       subtitulo={tweaks.beneficioSubtitulo}
                       onRevealed={() => { setScratchDone(true); bigBurst(theme.confetti); }}/>
        </div>

        {/* CTA */}
        <div className="relative z-10 px-5 mt-4 flex gap-2">
          <button onClick={handleCelebrate}
            className="btn-shine flex-1 rounded-2xl py-3 font-fraunces font-extrabold text-[14px] tracking-wide transition-transform active:scale-95 hover:scale-[1.02]"
            style={{
              background: `linear-gradient(135deg, ${theme.accent} 0%, ${theme.accent2} 100%)`,
              color: '#3d1212',
              boxShadow: `0 10px 24px -8px ${theme.accent2}88`
            }}>
            🎉  ¡CELEBRAR!
          </button>
          <button onClick={() => setCandleLit(c => !c)}
            className="rounded-2xl px-4 py-3 font-inter font-bold text-[12px] transition-transform active:scale-95 hover:scale-[1.02]"
            style={{
              background: 'rgba(255,255,255,0.08)',
              color: theme.cream,
              border: `1px solid ${theme.accent}44`
            }}>
            {candleLit ? "🌬️ Soplar" : "🕯️ Encender"}
          </button>
        </div>

        {/* GUESTBOOK — memoria de compañeros */}
        <div className="relative z-10 px-5 mt-5">
          <Guestbook theme={theme} employeeName={tweaks.empleadoNombre}/>
        </div>

        {/* sister brands */}
        <div className="relative z-10 px-5 mt-5 pb-6">
          <div className="text-center mb-2">
            <span className="font-inter text-[9px] tracking-[0.35em] uppercase" style={{color: theme.accent, opacity: 0.8}}>
              Nuestras casas
            </span>
          </div>
          <BrandRow theme={theme}/>
        </div>

        {/* bottom signature strip */}
        <div className="relative z-10 px-5 pb-5 text-center">
          <div className="font-caveat" style={{color: theme.cream, opacity: 0.7, fontSize: 18}}>
            — con cariño, tu familia Grupo Carbón —
          </div>
        </div>
      </div>
    </div>
  );
}

/* --------------------------- TWEAKS PANEL --------------------------- */
function LinkGenerator({ tweaks }) {
  const [copied, setCopied] = React.useState(false);
  const [includeAdmin, setIncludeAdmin] = React.useState(false);

  const buildUrl = () => {
    const base = window.location.origin + window.location.pathname;
    const params = new URLSearchParams();
    if (tweaks.empleadoNombre) params.set('festejado', tweaks.empleadoNombre);
    if (tweaks.variante) params.set('variante', tweaks.variante);
    params.set('tipo', tweaks.mostrarAniversario ? 'aniversario' : 'cumple');
    if (tweaks.mostrarAniversario) params.set('anios', tweaks.aniosServicio);
    if (tweaks.beneficio) params.set('beneficio', tweaks.beneficio);
    if (tweaks.beneficioSubtitulo) params.set('beneficioSub', tweaks.beneficioSubtitulo);
    if (includeAdmin) params.set('admin', ADMIN_CODE);
    return base + '?' + params.toString();
  };
  const url = buildUrl();

  const copy = async () => {
    try {
      await navigator.clipboard.writeText(url);
      setCopied(true);
      setTimeout(() => setCopied(false), 2000);
    } catch {
      // fallback
      const ta = document.createElement('textarea');
      ta.value = url; document.body.appendChild(ta); ta.select();
      document.execCommand('copy'); document.body.removeChild(ta);
      setCopied(true); setTimeout(() => setCopied(false), 2000);
    }
  };

  const shareWA = () => {
    const firstName = (tweaks.empleadoNombre || "").split(" ")[0];
    const tipo = tweaks.mostrarAniversario ? "aniversario" : "cumpleaños";
    const msg = `🎉 ¡${firstName}! La familia Carbón te manda una sorpresa por tu ${tipo}. Ábrela aquí:\n\n${url}`;
    window.open('https://wa.me/?text=' + encodeURIComponent(msg), '_blank');
  };

  return (
    <div className="mt-4 pt-3 border-t" style={{borderColor: 'rgba(245,181,68,0.18)'}}>
      <div className="font-fraunces font-extrabold text-white text-[12px] mb-2 flex items-center gap-2">
        🔗 Liga personalizada
      </div>
      <div className="text-[10px] text-white/55 leading-relaxed mb-2">
        Genera un link para que {(tweaks.empleadoNombre || "").split(" ")[0] || "el festejado"} abra la tarjeta ya configurada.
      </div>
      <div className="rounded-lg p-2 mb-2 text-[10px] font-mono break-all"
           style={{background: 'rgba(0,0,0,0.35)', color: '#f5b544', border: '1px solid rgba(245,181,68,0.15)', maxHeight: 70, overflow: 'auto'}}>
        {url}
      </div>
      <label className="flex items-center gap-2 text-[10px] text-white/60 mb-2 cursor-pointer">
        <input type="checkbox" checked={includeAdmin} onChange={e => setIncludeAdmin(e.target.checked)}/>
        Incluir clave admin (solo para ti)
      </label>
      <div className="flex gap-2">
        <button onClick={copy}
                className="flex-1 py-2 rounded-lg text-xs font-bold transition"
                style={{background: copied ? '#4ade80' : '#f5b544', color: '#3d1212'}}>
          {copied ? '✓ Copiado' : '📋 Copiar liga'}
        </button>
        <button onClick={shareWA}
                className="flex-1 py-2 rounded-lg text-xs font-bold"
                style={{background: '#25D366', color: '#fff'}}>
          WhatsApp
        </button>
      </div>
    </div>
  );
}

function TweaksPanel({ tweaks, setTweaks, visible, onClose }) {
  if (!visible) return null;
  const update = (k, v) => {
    const next = { ...tweaks, [k]: v };
    setTweaks(next);
    window.parent.postMessage({ type: '__edit_mode_set_keys', edits: { [k]: v } }, '*');
  };
  const Row = ({ label, children }) => (
    <div className="mb-3">
      <div className="text-[10px] tracking-[0.2em] uppercase font-bold mb-1" style={{color: '#a0a0a0'}}>{label}</div>
      {children}
    </div>
  );
  return (
    <div className="fixed bottom-4 right-4 z-50 w-[300px] max-h-[85vh] overflow-y-auto tweaks-scroll rounded-2xl p-4"
         style={{ background: 'rgba(20,14,14,0.96)', border: '1px solid rgba(255,255,255,0.12)', boxShadow: '0 20px 60px rgba(0,0,0,0.5)', backdropFilter: 'blur(12px)' }}>
      <div className="flex items-center justify-between mb-3">
        <div className="font-fraunces font-extrabold text-white text-[15px]">Tweaks</div>
        <button onClick={onClose} className="text-white/60 hover:text-white text-sm">✕</button>
      </div>
      <Row label="Nombre del colaborador">
        <input type="text" value={tweaks.empleadoNombre}
               onChange={e => update('empleadoNombre', e.target.value.toUpperCase())}
               className="w-full rounded-lg px-3 py-2 text-sm font-inter"
               style={{ background: 'rgba(255,255,255,0.08)', color: '#fff', border: '1px solid rgba(255,255,255,0.1)' }}/>
      </Row>
      <Row label="Tipo de celebración">
        <div className="flex gap-2">
          <button onClick={() => update('mostrarAniversario', true)}
                  className="flex-1 py-2 rounded-lg text-xs font-bold"
                  style={{background: tweaks.mostrarAniversario?'#f5b544':'rgba(255,255,255,0.08)', color: tweaks.mostrarAniversario?'#3d1212':'#fff'}}>
            Aniversario
          </button>
          <button onClick={() => update('mostrarAniversario', false)}
                  className="flex-1 py-2 rounded-lg text-xs font-bold"
                  style={{background: !tweaks.mostrarAniversario?'#f5b544':'rgba(255,255,255,0.08)', color: !tweaks.mostrarAniversario?'#3d1212':'#fff'}}>
            Cumpleaños
          </button>
        </div>
      </Row>
      {tweaks.mostrarAniversario && (
        <Row label={`Años de servicio: ${tweaks.aniosServicio}`}>
          <input type="range" min="1" max="30" value={tweaks.aniosServicio}
                 onChange={e => update('aniosServicio', parseInt(e.target.value))}
                 className="w-full"/>
        </Row>
      )}
      <Row label="Paleta / Variante">
        {Object.entries(THEMES).map(([key, t]) => (
          <button key={key} onClick={() => update('variante', key)}
                  className="w-full flex items-center gap-3 py-2 px-2 rounded-lg mb-1"
                  style={{background: tweaks.variante===key?'rgba(255,255,255,0.1)':'transparent', border: tweaks.variante===key?`1px solid ${t.accent}`:'1px solid transparent'}}>
            <div className="flex gap-1">
              <div className="w-4 h-4 rounded" style={{background: t.card}}/>
              <div className="w-4 h-4 rounded" style={{background: t.accent}}/>
              <div className="w-4 h-4 rounded" style={{background: t.accent2}}/>
            </div>
            <span className="text-xs text-white font-inter">{t.label}</span>
          </button>
        ))}
      </Row>
      <Row label="Beneficio revelado">
        <select value={tweaks.beneficio}
                onChange={e => {
                  const v = e.target.value;
                  const subs = {
                    "¡Tarde libre!": "Canjea este día antes de fin de mes con RH",
                    "Cena para 2": "Válida en cualquiera de nuestras casas",
                    "Vale de café ☕": "Tu dosis de energía por cuenta de la casa",
                    "Día libre pagado": "Úsalo cuando más lo necesites",
                    "Postre del chef": "Pásate al restaurante más cercano",
                  };
                  update('beneficio', v);
                  if (subs[v]) update('beneficioSubtitulo', subs[v]);
                }}
                className="w-full rounded-lg px-2 py-2 text-sm"
                style={{ background: 'rgba(255,255,255,0.08)', color: '#fff', border: '1px solid rgba(255,255,255,0.1)' }}>
          <option>¡Tarde libre!</option>
          <option>Cena para 2</option>
          <option>Vale de café ☕</option>
          <option>Día libre pagado</option>
          <option>Postre del chef</option>
        </select>
      </Row>
      <Row label="Texto secundario del beneficio">
        <input type="text" value={tweaks.beneficioSubtitulo}
               onChange={e => update('beneficioSubtitulo', e.target.value)}
               className="w-full rounded-lg px-3 py-2 text-xs font-inter"
               style={{ background: 'rgba(255,255,255,0.08)', color: '#fff', border: '1px solid rgba(255,255,255,0.1)' }}/>
      </Row>

      <LinkGenerator tweaks={tweaks}/>

      <div className="text-[10px] text-white/40 mt-3 leading-relaxed">
        Consejo: toca la velita para soplarla 🌬️, rasca el recuadro dorado para revelar el regalo 🎁
      </div>
    </div>
  );
}

/* --------------------------- ROOT --------------------------- */
const ADMIN_CODE = "carbon2026";   // cámbialo al publicar — ej. ?admin=mi_clave_secreta

// Reads ?festejado=NOMBRE&variante=xxx&beneficio=yyy&anios=N&aniversario=1 from URL
// and merges into defaults. Used for personalized links the admin sends.
function hydrateFromURL(defaults) {
  try {
    const p = new URLSearchParams(window.location.search);
    const out = { ...defaults };
    const nombre = p.get('festejado') || p.get('nombre');
    if (nombre) out.empleadoNombre = nombre.toUpperCase();
    const v = p.get('variante');
    if (v && THEMES[v]) out.variante = v;
    const b = p.get('beneficio');
    if (b) out.beneficio = b;
    const bs = p.get('beneficioSub');
    if (bs) out.beneficioSubtitulo = bs;
    const anios = p.get('anios');
    if (anios) { out.aniosServicio = parseInt(anios, 10) || 1; out.mostrarAniversario = true; }
    const tipo = p.get('tipo');
    if (tipo === 'cumple') out.mostrarAniversario = false;
    if (tipo === 'aniversario') out.mostrarAniversario = true;
    return out;
  } catch { return defaults; }
}

function App() {
  const [tweaks, setTweaks] = useState(() => hydrateFromURL(TWEAK_DEFAULTS));
  const [tweaksVisible, setTweaksVisible] = useState(false);
  const [isAdmin, setIsAdmin] = useState(false);
  const [inEditor, setInEditor] = useState(false);

  useEffect(() => {
    // Detect if we're inside the design editor (parent responds)
    let inEditorTimeout;
    const onMsg = (e) => {
      const d = e.data || {};
      if (d.type === '__activate_edit_mode') { setInEditor(true); setTweaksVisible(true); setIsAdmin(true); }
      else if (d.type === '__deactivate_edit_mode') setTweaksVisible(false);
    };
    window.addEventListener('message', onMsg);
    try { window.parent.postMessage({ type: '__edit_mode_available' }, '*'); } catch(e) {}

    // Detect admin mode via URL: ?admin=<code>
    const params = new URLSearchParams(window.location.search);
    const adminParam = params.get('admin');
    if (adminParam && adminParam === ADMIN_CODE) {
      setIsAdmin(true);
      try { sessionStorage.setItem('carbon_admin', '1'); } catch {}
    } else if (sessionStorage.getItem('carbon_admin') === '1') {
      setIsAdmin(true);
    }

    return () => window.removeEventListener('message', onMsg);
  }, []);

  return (
    <>
      <BirthdayCard tweaks={tweaks} onTweakChange={setTweaks}/>
      {isAdmin && !inEditor && (
        <button
          onClick={() => setTweaksVisible(v => !v)}
          className="fixed bottom-4 left-4 z-40 rounded-full w-11 h-11 flex items-center justify-center text-lg shadow-lg"
          style={{ background: "#1a0f0f", color: "#f5b544", border: "1px solid rgba(245,181,68,0.35)" }}
          title="Panel admin">
          ⚙️
        </button>
      )}
      {isAdmin && (
        <TweaksPanel tweaks={tweaks} setTweaks={setTweaks} visible={tweaksVisible} onClose={() => setTweaksVisible(false)}/>
      )}
    </>
  );
}

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