// ── TailorAudioBooks · v3 (single-output spec) ──────────────
// Adapted from the WiseTip Audio System functional spec:
//   • ONE story → ONE audio file (no branching, no series)
//   • Multi-voice & episodic content live on the Podcast page
//   • Output: JSON contract → Claude → ElevenLabs → MP3
//
// 7 STEPS (guided builder):
//   1. World & tone     (nature · home · fantasy · sea · city · mystery)
//   2. Characters       (child name + age + traits, sibling, pet, freeform)
//   3. Narrator voice   (single voice — warm female · warm male · child · neutral)
//   4. Duration tier    (flash 2-4 · standard 4-7 · extended 7-10)
//   5. Language         (en · es · bilingual · de · fr)
//   6. Creative brief   ("Dear storyteller…" letter + mood + avoid + cultural + edition link)
//   7. Order + JSON-contract reveal + dual pricing
//
// Plus an "Admin script upload" mode toggle for power-users who paste
// a fully authored script (see AudioBooks_Podcast spec §1.2.B).

const TAILOR_VOICES = [
  { id:"warm_female_1", label:"Sofia",   sub: { en:"Warm female · calm, close",      es:"Femenina cálida · calma, cercana" } },
  { id:"warm_female_2", label:"Elena",   sub: { en:"Warm female · bright, expressive", es:"Femenina cálida · brillante, expresiva" } },
  { id:"warm_male_1",   label:"Marco",   sub: { en:"Warm male · deep, gentle",       es:"Masculina cálida · grave, suave" } },
  { id:"warm_male_2",   label:"Luca",    sub: { en:"Warm male · light, animated",    es:"Masculina cálida · ligera, animada" } },
  { id:"child_1",       label:"Kai",     sub: { en:"Child · natural, curious",       es:"Niño · natural, curiosa" } },
  { id:"child_2",       label:"Mia",     sub: { en:"Child · soft, young",            es:"Niña · suave, joven" } },
  { id:"elderly",       label:"Wilhelm", sub: { en:"Elderly · wise, measured",       es:"Mayor · sabia, mesurada" } },
  { id:"neutral",       label:"Classic", sub: { en:"Neutral · timeless",             es:"Neutra · atemporal" } },
];

const TAILOR_TRAITS_EN = ["brave","curious","shy","adventurous","kind","funny","stubborn","creative","sensitive","energetic","calm","determined","loves animals","loves planes","loves stones","collects bugs","big sister","big brother","scared of dark"];
const TAILOR_TRAITS_ES = ["valiente","curioso","tímido","aventurero","amable","divertido","cabezota","creativo","sensible","enérgico","tranquilo","determinado","ama animales","ama aviones","ama piedras","colecciona bichos","hermana mayor","hermano mayor","miedo a la oscuridad"];

const TAILOR_AVOID_EN = ["loud noises","scary creatures","death","illness","conflict","dark themes","competition","screens","sugar","violence"];
const TAILOR_AVOID_ES = ["ruidos fuertes","criaturas de miedo","muerte","enfermedad","conflicto","temas oscuros","competición","pantallas","azúcar","violencia"];

const TAILOR_STEPS_EN = ["World","Who","Voice","Length","Lang","Brief","Order"];
const TAILOR_STEPS_ES = ["Mundo","Quién","Voz","Dur.","Idioma","Brief","Orden"];

function TailorPage({ theme, dark, lang, isMember, nav }) {
  const [mode, setMode] = useState("guided");  // guided | admin
  const [step, setStep] = useState(1);
  const [d, setD] = useState({
    world: "nature",            // nature | family | fantasy | sea | city | mystery
    char: { name: "", age: 6, traits: [], sib: "", pet: "", desc: "" },
    voice: "warm_female_1",
    duration: "standard",       // flash | standard | extended  (no series — single-output)
    language: "en",             // en | es | bilingual | de | fr
    brief: { body: "", mood: "calm", avoid: [], cultural: "", edition: "" },
  });
  const set = (path, v) => setD(prev => {
    const n = JSON.parse(JSON.stringify(prev));
    const parts = path.split(".");
    let cur = n;
    for (let i = 0; i < parts.length - 1; i++) cur = cur[parts[i]];
    cur[parts[parts.length - 1]] = v;
    return n;
  });
  const toggle = (path, v, max) => setD(prev => {
    const n = JSON.parse(JSON.stringify(prev));
    const parts = path.split(".");
    let cur = n;
    for (let i = 0; i < parts.length - 1; i++) cur = cur[parts[i]];
    const arr = cur[parts[parts.length - 1]];
    const has = arr.includes(v);
    cur[parts[parts.length - 1]] = has ? arr.filter(x => x !== v) : (max ? [...arr, v].slice(0, max) : [...arr, v]);
    return n;
  });

  const TOTAL = 7;
  const stepLabels = lang === "es" ? TAILOR_STEPS_ES : TAILOR_STEPS_EN;

  const priceFor = (dur, tier) => {
    const base = { flash: 5.99, standard: 8.99, extended: 11.99 }[dur] || 8.99;
    return tier === "member" ? +(base * 0.8).toFixed(2) : base;
  };

  if (mode === "admin") {
    return <AdminScriptUpload theme={theme} dark={dark} lang={lang} nav={nav} onBack={() => setMode("guided")}/>;
  }

  return (
    <div style={{ background: dark ? theme.panel : "#0E2318", color:"#F0EBE0", paddingTop:"5rem", minHeight:"100vh" }}>
      {/* migration ribbon */}
      <div style={{
        background: "rgba(200,168,90,.10)", borderBottom: "1px solid rgba(200,168,90,.28)",
        padding: "10px clamp(1.5rem,5vw,5vw)", textAlign:"center",
        fontSize: ".82rem", color: "rgba(200,168,90,.9)", letterSpacing: ".02em",
      }}>
        {lang === "es"
          ? "Estamos migrando WiseTip a nuevos servidores — TailorAudioBooks podría no funcionar al completo todavía."
          : "We're migrating WiseTip to new servers — TailorAudioBooks may not be fully functional yet."}
      </div>
      {/* ── Header ── */}
      <div style={{ padding:"3.5rem clamp(1.5rem, 5vw, 5vw) 1.5rem", position:"relative", overflow:"hidden" }}>
        <Grain amount={.03}/>
        <div className="wrap" style={{ position:"relative" }}>
          <div style={{ display:"flex", justifyContent:"space-between", alignItems:"flex-start", gap: "1rem", flexWrap:"wrap" }}>
            <Pill color="#C8A85A" bd="#C8A85A66" style={{ marginBottom:"1rem" }}>◐ TailorAudioBooks · single-output system</Pill>
            <button onClick={() => setMode("admin")} style={{
              background:"transparent", border:"1px solid rgba(200,168,90,.4)",
              color:"#C8A85A", padding:"5px 12px", borderRadius: 14,
              fontSize:".7rem", letterSpacing:".14em", textTransform:"uppercase", cursor:"pointer",
            }}>{lang==="en"?"⚙ Admin script upload":"⚙ Subir guion · admin"}</button>
          </div>
          <h1 className="serif" style={{
            fontStyle:"italic", fontWeight: 300, fontSize:"clamp(2rem, 4.5vw, 3.4rem)",
            color:"#F0EBE0", lineHeight: 1.05, letterSpacing:"-.02em", maxWidth: 820,
          }}>{lang==="en"
            ? <>Seven quiet choices. <em style={{ color:"#9DC872" }}>One story. One audio file. Yours only.</em></>
            : <>Siete elecciones tranquilas. <em style={{ color:"#9DC872" }}>Una historia. Un audio. Solo tuyo.</em></>}</h1>
          <p style={{ color:"rgba(240,235,224,.6)", fontSize:".98rem", lineHeight: 1.7, fontWeight: 300, marginTop:"1rem", maxWidth: 640 }}>
            {lang==="en"
              ? "Tell us what kind of story, who it's for, how long it should be — and write a short note to the storyteller. Single emotional arc, single resolution, single MP3. Multi-voice and episodic series live on the Podcast page."
              : "Cuéntanos qué historia, para quién, cuánto debe durar — y escribe una nota breve al narrador. Un solo arco emocional, una resolución, un MP3. El multi-voz y las series episódicas están en la página de Podcast."}
          </p>
        </div>
      </div>

      {/* ── Stepper ── */}
      <div style={{ background:"rgba(8,15,10,.5)", borderTop:"1px solid rgba(255,255,255,.06)", padding:"1rem clamp(1.5rem, 5vw, 5vw)" }}>
        <div className="wrap" style={{ display:"flex", alignItems:"center", gap: 4, overflowX:"auto" }}>
          {stepLabels.map((tl, i) => {
            const n = i + 1, active = step === n, done = step > n;
            return (
              <React.Fragment key={n}>
                <button onClick={() => done && setStep(n)} style={{
                  display:"flex", alignItems:"center", gap: 8,
                  background:"none", border:"none",
                  cursor: done ? "pointer" : "default", color:"inherit",
                  padding:"4px 8px", borderRadius: 16, whiteSpace:"nowrap",
                }}>
                  <span style={{
                    width: 24, height: 24, borderRadius:"50%",
                    background: active ? "#9DC872" : done ? "rgba(125,190,90,.16)" : "rgba(255,255,255,.04)",
                    border: `1px solid ${active || done ? "#9DC872" : "rgba(255,255,255,.1)"}`,
                    color: active ? "#050F07" : done ? "#9DC872" : "rgba(240,235,224,.45)",
                    display:"inline-flex", alignItems:"center", justifyContent:"center",
                    fontSize:".7rem", fontFamily:"'JetBrains Mono',monospace",
                  }}>{done ? "✓" : n}</span>
                  <span style={{ fontSize:".74rem", color: active ? "#F0EBE0" : "rgba(240,235,224,.4)" }}>{tl}</span>
                </button>
                {n < TOTAL && <span style={{ width: 18, height: 1, background:"rgba(255,255,255,.08)" }}/>}
              </React.Fragment>
            );
          })}
        </div>
      </div>

      {/* ── Body: panel + live preview ── */}
      <div style={{ padding:"3rem clamp(1.5rem, 5vw, 5vw) 5rem" }}>
        <div className="wrap tailor-grid" style={{ display:"grid", gridTemplateColumns:"1fr 360px", gap:"2.5rem", alignItems:"start" }}>
          <div style={{
            background:"rgba(8,15,10,.5)", border:"1px solid rgba(125,190,90,.16)",
            borderRadius: 8, padding:"2rem 2.25rem", minHeight: 400,
          }}>
            <div className="label-tiny" style={{ color:"#9DC872", marginBottom: 8 }}>
              {lang==="en"?`Step ${step} / ${TOTAL}`:`Paso ${step} / ${TOTAL}`} · {stepLabels[step - 1]}
            </div>

            {step === 1 && <S2World     d={d} set={set} lang={lang}/>}
            {step === 2 && <S3Chars     d={d} set={set} toggle={toggle} lang={lang}/>}
            {step === 3 && <S4Voice     d={d} set={set} lang={lang} nav={nav}/>}
            {step === 4 && <S5Duration  d={d} set={set} lang={lang} isMember={isMember} priceFor={priceFor}/>}
            {step === 5 && <S6Language  d={d} set={set} lang={lang}/>}
            {step === 6 && <S7Brief     d={d} set={set} toggle={toggle} lang={lang}/>}
            {step === 7 && <S8Order     d={d} lang={lang} isMember={isMember} priceFor={priceFor} nav={nav}/>}

            <div style={{
              display:"flex", justifyContent:"space-between", marginTop:"2rem",
              paddingTop:"1.5rem", borderTop:"1px solid rgba(255,255,255,.08)",
            }}>
              <button disabled={step === 1} onClick={() => setStep(Math.max(1, step - 1))} style={{
                background:"transparent", color:"rgba(240,235,224,.55)",
                border:"1px solid rgba(255,255,255,.12)",
                padding:"10px 18px", borderRadius: 22, fontSize:".82rem",
                cursor: step===1?"not-allowed":"pointer", opacity: step===1?.3:1,
              }}>← {lang==="en"?"Back":"Atrás"}</button>
              {step < TOTAL ? (
                <button onClick={() => setStep(step + 1)} style={{
                  background:"#9DC872", color:"#050F07", border:"none",
                  padding:"10px 22px", borderRadius: 22, fontSize:".84rem",
                  fontWeight: 600, cursor:"pointer",
                }}>{lang==="en"?"Continue":"Continuar"} →</button>
              ) : (
                <button style={{
                  background:"#9DC872", color:"#050F07", border:"none",
                  padding:"10px 22px", borderRadius: 22, fontSize:".84rem",
                  fontWeight: 600, cursor:"pointer",
                }}>
                  {isMember
                    ? (lang==="en"?`Generate · €${priceFor(d.duration,"member")} (WiseTip+)`:`Generar · ${priceFor(d.duration,"member").toString().replace(".",",")} € (WiseTip+)`)
                    : (lang==="en"?`Generate · €${priceFor(d.duration,"std")}`:`Generar · ${priceFor(d.duration,"std").toString().replace(".",",")} €`)} →
                </button>
              )}
            </div>
          </div>

          {/* ── Live side preview ── */}
          <div style={{
            background:"rgba(8,15,10,.45)", border:"1px solid rgba(255,255,255,.06)",
            borderRadius: 8, padding:"1.5rem", position:"sticky", top:"6rem",
          }}>
            <div className="label-tiny" style={{ color:"#C8A85A", marginBottom: 10 }}>
              {lang==="en"?"Live preview":"Vista en vivo"}
            </div>
            <div className="serif italic" style={{ fontSize:"1.2rem", color:"#F0EBE0", lineHeight: 1.3, marginBottom:"1rem" }}>
              {previewTitle(d, lang)}
            </div>
            <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr", gap: 10, fontSize:".74rem", color:"rgba(240,235,224,.6)" }}>
              <TStat label={lang==="en"?"Format":"Formato"} value={lang==="en"?"Linear · 1 file":"Lineal · 1 archivo"}/>
              <TStat label={lang==="en"?"World":"Mundo"}   value={WORLD_LABEL[d.world][lang]}/>
              <TStat label={lang==="en"?"Length":"Duración"} value={DUR_LABEL[d.duration][lang]}/>
              <TStat label={lang==="en"?"Voice":"Voz"}     value={TAILOR_VOICES.find(v=>v.id===d.voice)?.label || "—"}/>
              <TStat label={lang==="en"?"Language":"Idioma"} value={LANG_LABEL[d.language][lang]}/>
              <TStat label={lang==="en"?"Mood":"Tono"}     value={MOOD_LABEL[d.brief.mood][lang]}/>
            </div>
            {d.char.traits.length > 0 && (
              <div style={{ marginTop:"1rem", paddingTop:"1rem", borderTop:"1px solid rgba(255,255,255,.06)" }}>
                <div className="label-tiny" style={{ color:"rgba(240,235,224,.4)", marginBottom: 6 }}>{lang==="en"?"Traits":"Rasgos"}</div>
                <div style={{ display:"flex", flexWrap:"wrap", gap: 4 }}>
                  {d.char.traits.map(t => <span key={t} style={{ fontSize:".66rem", padding:"2px 8px", borderRadius: 10, background:"rgba(125,190,90,.12)", color:"#9DC872" }}>{t}</span>)}
                </div>
              </div>
            )}
            {d.brief.avoid.length > 0 && (
              <div style={{ marginTop:".75rem" }}>
                <div className="label-tiny" style={{ color:"#E24B4A", marginBottom: 6 }}>{lang==="en"?"Avoid":"Evitar"}</div>
                <div style={{ display:"flex", flexWrap:"wrap", gap: 4 }}>
                  {d.brief.avoid.map(t => <span key={t} style={{ fontSize:".64rem", padding:"2px 8px", borderRadius: 10, background:"rgba(226,75,74,.1)", color:"#E24B4A", border:"1px solid rgba(226,75,74,.3)" }}>{t}</span>)}
                </div>
              </div>
            )}
          </div>
        </div>
      </div>

      <style>{`@media (max-width: 900px) { .tailor-grid { grid-template-columns: 1fr !important; gap: 1.5rem !important; } }`}</style>
      <Footer theme={theme} dark={dark} lang={lang} nav={nav}/>
    </div>
  );
}

// ── label dictionaries (used by side preview) ──
const WORLD_LABEL = { nature:{en:"Forest & nature",es:"Bosque y naturaleza"}, family:{en:"Home & family",es:"Casa y familia"}, fantasy:{en:"Fantasy world",es:"Fantasía"}, sea:{en:"Sea & coast",es:"Mar y costa"}, city:{en:"City",es:"Ciudad"}, mystery:{en:"Mystery",es:"Misterio"} };
const DUR_LABEL   = { flash:{en:"2–4 min",es:"2–4 min"}, standard:{en:"4–7 min",es:"4–7 min"}, extended:{en:"7–10 min",es:"7–10 min"} };
const LANG_LABEL  = { en:{en:"English",es:"Inglés"}, es:{en:"Spanish",es:"Español"}, bilingual:{en:"Bilingual EN/ES",es:"Bilingüe EN/ES"}, de:{en:"German",es:"Alemán"}, fr:{en:"French",es:"Francés"} };
const MOOD_LABEL  = { calm:{en:"Calm",es:"Calma"}, energise:{en:"Energise",es:"Energía"}, contemplate:{en:"Reflective",es:"Reflexivo"}, adventure:{en:"Adventure",es:"Aventura"} };

function previewTitle(d, lang) {
  const name = d.char.name?.trim() || (lang==="en" ? "Your child" : "Tu hijo/a");
  const worldWord = {
    nature:  { en:"Forest", es:"Bosque" },
    family:  { en:"House", es:"Casa" },
    fantasy: { en:"Kingdom", es:"Reino" },
    sea:     { en:"Sea", es:"Mar" },
    city:    { en:"City", es:"Ciudad" },
    mystery: { en:"Secret", es:"Secreto" },
  }[d.world][lang];
  const t = d.char.traits[0];
  return `"${name} ${lang==="en"?"and the":"y el"} ${worldWord} ${lang==="en"?"of":"de"} ${t || (lang==="en"?"Light":"la luz")}"`;
}

// ── shared step UI helpers ──
function TStat({ label, value }) {
  return (
    <div>
      <div className="label-tiny" style={{ color:"rgba(240,235,224,.4)", marginBottom: 2 }}>{label}</div>
      <div style={{ color:"#F0EBE0" }}>{value}</div>
    </div>
  );
}
function TFld({ label, v, on, type="text", placeholder, min, max }) {
  return (
    <label style={{ display:"flex", flexDirection:"column", gap: 6 }}>
      <span className="label-tiny" style={{ color:"rgba(240,235,224,.45)" }}>{label}</span>
      <input type={type} value={v} placeholder={placeholder || ""} min={min} max={max}
        onChange={e => on(type === "number" ? +e.target.value : e.target.value)} style={{
        padding:"10px 14px", background:"rgba(255,255,255,.04)",
        border:"1px solid rgba(255,255,255,.12)", borderRadius: 4,
        color:"#F0EBE0", fontSize:".95rem", outline:"none",
      }}/>
    </label>
  );
}
function OptCard({ selected, onClick, icon, title, body, badge, badgeColor }) {
  return (
    <button onClick={onClick} style={{
      padding:"1rem 1.1rem", borderRadius: 6, textAlign:"left", cursor:"pointer",
      background: selected ? "rgba(125,190,90,.14)" : "rgba(255,255,255,.03)",
      border:`1px solid ${selected ? "#9DC872" : "rgba(255,255,255,.08)"}`,
      color:"inherit", display:"flex", flexDirection:"column", gap: 5,
      position:"relative", minHeight: 90,
    }}>
      <span className="serif" style={{ fontSize:"1.3rem", color:"#9DC872", marginBottom: 2 }}>{icon}</span>
      <div className="serif italic" style={{ fontSize:"1.05rem", color:"#F0EBE0" }}>{title}</div>
      <div style={{ fontSize:".78rem", color:"rgba(240,235,224,.6)", fontWeight: 300, lineHeight: 1.45 }}>{body}</div>
      {badge && <span style={{
        position:"absolute", top: 12, right: 12,
        fontSize:".56rem", letterSpacing:".14em", textTransform:"uppercase",
        padding:"2px 8px", borderRadius: 8,
        background: `${badgeColor || "#C8A85A"}22`, color: badgeColor || "#C8A85A",
        border: `1px solid ${badgeColor || "#C8A85A"}55`, fontWeight: 700,
      }}>{badge}</span>}
    </button>
  );
}

// ── STEP 1 · WORLD ─────────────────────────────────────────
function S2World({ d, set, lang }) {
  const items = [
    { id:"nature",  icon:"🌿", t:{en:"Forest & nature",        es:"Bosque y naturaleza"}, b:{en:"Trees, rivers, animals, seasons.",     es:"Árboles, ríos, animales, estaciones."} },
    { id:"family",  icon:"🏡", t:{en:"Home & family",          es:"Casa y familia"},      b:{en:"Kitchen, garden, grandparents, siblings.", es:"Cocina, jardín, abuelos, hermanos."} },
    { id:"fantasy", icon:"✨", t:{en:"Fantasy world",          es:"Mundo de fantasía"},   b:{en:"Invented creatures, bespoke rules.",   es:"Criaturas inventadas, reglas propias."} },
    { id:"sea",     icon:"🌊", t:{en:"Sea & coast",            es:"Mar y costa"},         b:{en:"Waves, lighthouses, tide-pools.",      es:"Olas, faros, charcos de marea."} },
    { id:"city",    icon:"🏙️", t:{en:"City & neighbourhood",   es:"Ciudad y barrio"},     b:{en:"Streets, markets, parks, community.",  es:"Calles, mercados, parques, vecinos."} },
    { id:"mystery", icon:"🔍", t:{en:"Mystery & discovery",    es:"Misterio y descubrimiento"}, b:{en:"Something hidden. The child finds it.", es:"Algo oculto. El niño lo descubre."} },
  ];
  return (
    <>
      <StepHead lang={lang}
        ey={lang==="en"?"World & tone":"Mundo y tono"}
        title={lang==="en"?"Where does it happen?":"¿Dónde ocurre?"}
        sub={lang==="en"?"The world defines the sensory palette — scents, light, sounds.":"El mundo define la paleta sensorial — aromas, luz, sonidos."}/>
      <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr", gap: 8 }}>
        {items.map(it => (
          <OptCard key={it.id} selected={d.world === it.id} onClick={() => set("world", it.id)}
            icon={it.icon} title={it.t[lang]} body={it.b[lang]}/>
        ))}
      </div>
    </>
  );
}

// ── STEP 3 · CHARACTERS ────────────────────────────────────
function S3Chars({ d, set, toggle, lang }) {
  const ch = d.char;
  const traits = lang === "es" ? TAILOR_TRAITS_ES : TAILOR_TRAITS_EN;
  return (
    <>
      <StepHead lang={lang}
        ey={lang==="en"?"Characters":"Personajes"}
        title={lang==="en"?"Who is at the centre?":"¿Quién está en el centro?"}
        sub={lang==="en"?"The protagonist can share your child's name and traits. Sibling, friend, or pet can join.":"El protagonista puede compartir el nombre y rasgos de tu hijo. Hermanos, amigos o mascotas pueden unirse."}/>
      <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr", gap:"1rem" }}>
        <TFld label={lang==="en"?"Name (optional)":"Nombre (opcional)"} v={ch.name} on={v => set("char.name", v)} placeholder={lang==="en"?"e.g. Sofia":"ej. Sofía"}/>
        <TFld label={lang==="en"?"Age":"Edad"} v={ch.age} on={v => set("char.age", v || 6)} type="number" min="1" max="18"/>
      </div>
      <div style={{ marginTop:"1rem" }}>
        <div className="label-tiny" style={{ color:"rgba(240,235,224,.45)", marginBottom: 6, display:"flex", justifyContent:"space-between" }}>
          <span>{lang==="en"?"Traits (pick up to 3)":"Rasgos (máx. 3)"}</span>
          {ch.traits.length >= 3 && <span style={{ color:"#C8A85A" }}>{lang==="en"?"max reached":"máximo alcanzado"}</span>}
        </div>
        <div style={{ display:"flex", flexWrap:"wrap", gap: 6 }}>
          {traits.map(t => {
            const sel = ch.traits.includes(t);
            const disabled = !sel && ch.traits.length >= 3;
            return (
              <button key={t} onClick={() => toggle("char.traits", t, 3)} disabled={disabled} style={{
                padding:"5px 11px", borderRadius: 14,
                background: sel ? "rgba(125,190,90,.18)" : "rgba(255,255,255,.03)",
                border: `1px solid ${sel ? "#9DC872" : "rgba(255,255,255,.1)"}`,
                color: sel ? "#F0EBE0" : disabled ? "rgba(240,235,224,.2)" : "rgba(240,235,224,.55)",
                fontSize:".74rem", cursor: disabled ? "not-allowed" : "pointer",
                opacity: disabled ? .45 : 1,
              }}>{sel ? "✓ " : ""}{t}</button>
            );
          })}
        </div>
      </div>
      <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr", gap:"1rem", marginTop:"1rem" }}>
        <TFld label={lang==="en"?"Sibling / friend":"Hermano/a · amigo/a"} v={ch.sib} on={v => set("char.sib", v)} placeholder={lang==="en"?"name or blank":"nombre o vacío"}/>
        <TFld label={lang==="en"?"Pet / companion":"Mascota / compañero"} v={ch.pet} on={v => set("char.pet", v)} placeholder={lang==="en"?"e.g. Socks":"ej. Manchas"}/>
      </div>
      <div style={{ marginTop:"1rem" }}>
        <div className="label-tiny" style={{ color:"rgba(240,235,224,.45)", marginBottom: 6 }}>
          {lang==="en"?"Anything else about the protagonist?":"¿Algo más sobre el protagonista?"}
        </div>
        <textarea value={ch.desc} onChange={e => set("char.desc", e.target.value)} rows={3}
          placeholder={lang==="en"?"e.g. She loves drawing but is scared of the dark.":"ej. Le encanta dibujar pero le asusta la oscuridad."}
          style={{
            width:"100%", resize:"vertical", padding:"10px 14px",
            background:"rgba(255,255,255,.04)", border:"1px solid rgba(255,255,255,.12)",
            borderRadius: 4, color:"#F0EBE0", fontSize:".9rem", outline:"none",
            fontFamily:"inherit", lineHeight: 1.5,
          }}/>
      </div>
    </>
  );
}

// ── STEP 4 · VOICE ────────────────────────────────────────
function S4Voice({ d, set, lang, nav }) {
  const presets = [
    { id:"warm_female_1", icon:"👩", t:{en:"Warm female",es:"Femenina cálida"}, b:{en:"Sofia · calm, close. Single voice for the whole story.",es:"Sofia · calma, cercana. Una voz para toda la historia."} },
    { id:"warm_male_1",   icon:"👨", t:{en:"Warm male",es:"Masculino cálido"},  b:{en:"Marco · grounded, steady. Single voice for the whole story.",es:"Marco · estable, firme. Una voz para toda la historia."} },
    { id:"child_1",       icon:"🧒", t:{en:"Child narrator",es:"Narrador niño"}, b:{en:"Kai · a child of similar age tells the story from inside.",es:"Kai · un niño narra desde dentro de la historia."} },
    { id:"neutral",       icon:"📻", t:{en:"Classic neutral",es:"Clásico neutro"}, b:{en:"Measured, timeless bedtime register.",es:"Mesurado, atemporal. Registro clásico."} },
  ];
  return (
    <>
      <StepHead lang={lang}
        ey={lang==="en"?"Narrator voice":"Voz del narrador"}
        title={lang==="en"?"Who narrates?":"¿Quién narra?"}
        sub={lang==="en"?"Audiobooks use a single voice across the whole story — one emotional arc, one resolution. For multi-voice dialogue, see the Podcast builder.":"Los audiolibros usan una sola voz — un arco emocional, una resolución. Para diálogos multi-voz, ve al constructor de Podcast."}/>
      <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr", gap: 8 }}>
        {presets.map(p => (
          <OptCard key={p.id} selected={d.voice === p.id} onClick={() => set("voice", p.id)}
            icon={p.icon} title={p.t[lang]} body={p.b[lang]}/>
        ))}
      </div>
      <div style={{
        marginTop:"1.25rem", padding:"12px 16px",
        background:"rgba(125,190,90,.06)", border:"1px dashed rgba(125,190,90,.3)",
        borderRadius: 6, display:"flex", justifyContent:"space-between", alignItems:"center", gap: "1rem", flexWrap:"wrap",
      }}>
        <div style={{ fontSize:".82rem", color:"rgba(240,235,224,.7)", lineHeight: 1.55 }}>
          {lang==="en"
            ? <>Want a <strong style={{ color:"#9DC872" }}>multi-voice cast</strong> (narrator + named characters with their own ElevenLabs voices)? That format lives on the Podcast page.</>
            : <>¿Quieres un <strong style={{ color:"#9DC872" }}>reparto multi-voz</strong> (narrador + personajes con voz propia)? Ese formato vive en la página de Podcast.</>}
        </div>
        <button onClick={() => nav("audiobooks")} style={{
          background:"transparent", color:"#9DC872", border:"1px solid #9DC872",
          padding:"7px 14px", borderRadius: 18, fontSize:".76rem", cursor:"pointer", whiteSpace:"nowrap",
        }}>{lang==="en"?"Podcast builder":"Constructor Podcast"} →</button>
      </div>
    </>
  );
}

// ── STEP 5 · DURATION ─────────────────────────────────────
function S5Duration({ d, set, lang, isMember, priceFor }) {
  const items = [
    { id:"flash",    icon:"⚡", t:{en:"Flash",   es:"Flash"},     m:{en:"2–4 min",es:"2–4 min"}, b:{en:"~350–600 words. One tight story.",es:"~350–600 palabras. Una historia concisa."} },
    { id:"standard", icon:"📖", t:{en:"Standard",es:"Estándar"}, m:{en:"4–7 min",es:"4–7 min"}, b:{en:"~650–1,100 words. Full arc.",es:"~650–1.100 palabras. Arco completo."} },
    { id:"extended", icon:"🌙", t:{en:"Extended",es:"Extendido"},m:{en:"7–10 min",es:"7–10 min"}, b:{en:"~1,100–1,600 words. Rich world.",es:"~1.100–1.600 palabras. Mundo rico."} },
  ];
  return (
    <>
      <StepHead lang={lang}
        ey={lang==="en"?"Duration":"Duración"}
        title={lang==="en"?"How long should it last?":"¿Cuánto debe durar?"}
        sub={lang==="en"?"Shorter is better for most children under 7. Members save 20%.":"Más corto es mejor para menores de 7. Los miembros ahorran 20%."}/>
      <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr", gap: 8 }}>
        {items.map(it => {
          const stdP = priceFor(it.id, "std"), memP = priceFor(it.id, "member");
          const sel = d.duration === it.id;
          return (
            <button key={it.id} onClick={() => set("duration", it.id)} style={{
              padding:"1.1rem 1.2rem", borderRadius: 6, textAlign:"left", cursor:"pointer",
              background: sel ? "rgba(125,190,90,.14)" : "rgba(255,255,255,.03)",
              border:`1px solid ${sel ? "#9DC872" : "rgba(255,255,255,.08)"}`,
              color:"inherit", display:"flex", flexDirection:"column", gap: 5,
            }}>
              <div style={{ display:"flex", justifyContent:"space-between", alignItems:"center" }}>
                <span className="serif" style={{ fontSize:"1.3rem", color:"#9DC872" }}>{it.icon}</span>
                <span className="mono" style={{ fontSize:".66rem", color:"rgba(240,235,224,.5)" }}>{it.m[lang]}</span>
              </div>
              <div className="serif italic" style={{ fontSize:"1.1rem", color:"#F0EBE0" }}>{it.t[lang]}</div>
              <div style={{ fontSize:".78rem", color:"rgba(240,235,224,.6)", fontWeight: 300, lineHeight: 1.45 }}>{it.b[lang]}</div>
              <div style={{ marginTop:6, display:"flex", gap: 10, alignItems:"baseline" }}>
                <span style={{ fontSize:".82rem", color:"#C8A85A" }}>€{stdP}</span>
                <span style={{ fontSize:".68rem", color:"#9DC872" }}>· WiseTip+ €{memP}</span>
              </div>
            </button>
          );
        })}
      </div>
    </>
  );
}

// ── STEP 6 · LANGUAGE ─────────────────────────────────────
function S6Language({ d, set, lang }) {
  const items = [
    { id:"en",        icon:"🇬🇧", t:{en:"English",       es:"Inglés"},        b:{en:"British or American — specify in brief.",es:"Británico o americano — especifica en tu brief."} },
    { id:"es",        icon:"🇪🇸", t:{en:"Spanish",       es:"Español"},       b:{en:"Standard Castilian.",es:"Castellano estándar."} },
    { id:"bilingual", icon:"🌍", t:{en:"Bilingual EN/ES", es:"Bilingüe EN/ES"}, b:{en:"Natural interweaving — not translations.",es:"Entrelazado natural — no traducciones."} },
    { id:"de",        icon:"🇩🇪", t:{en:"German",        es:"Alemán"},        b:{en:"Standard Hochdeutsch.",es:"Hochdeutsch estándar."} },
    { id:"fr",        icon:"🇫🇷", t:{en:"French",        es:"Francés"},       b:{en:"Standard French, literary register.",es:"Francés estándar, literario."} },
  ];
  return (
    <>
      <StepHead lang={lang}
        ey={lang==="en"?"Language":"Idioma"}
        title={lang==="en"?"In which language?":"¿En qué idioma?"}
        sub={lang==="en"?"Bilingual editions are written originally in both — never machine-translated.":"Las bilingües se escriben originalmente en ambos — nunca traducción automática."}/>
      <div style={{ display:"grid", gridTemplateColumns:"repeat(auto-fill,minmax(160px,1fr))", gap: 8 }}>
        {items.map(it => (
          <OptCard key={it.id} selected={d.language === it.id} onClick={() => set("language", it.id)}
            icon={it.icon} title={it.t[lang]} body={it.b[lang]}/>
        ))}
      </div>
    </>
  );
}

// ── STEP 7 · CREATIVE BRIEF ───────────────────────────────
function S7Brief({ d, set, toggle, lang }) {
  const br = d.brief;
  const moods = lang === "es"
    ? [["calm","🌙 Calma"],["energise","⚡ Energía"],["contemplate","🌿 Reflexivo"],["adventure","🗺️ Aventura"]]
    : [["calm","🌙 Calm"],["energise","⚡ Energise"],["contemplate","🌿 Reflective"],["adventure","🗺️ Adventure"]];
  const avoids = lang === "es" ? TAILOR_AVOID_ES : TAILOR_AVOID_EN;
  const editionPicks = [12, 51, 76, 88, 100, 150, 161];

  return (
    <>
      <StepHead lang={lang}
        ey={lang==="en"?"Creative brief":"Encargo creativo"}
        title={lang==="en"?"Write to the storyteller.":"Escríbele al narrador."}
        sub={lang==="en"?"This is where the story becomes yours. A short note overrides every default above.":"Aquí la historia se vuelve tuya. Esta nota tiene prioridad sobre todo lo anterior."}/>
      <div className="label-tiny" style={{ color:"#C8A85A", marginBottom: 6 }}>
        {lang==="en"?"Dear storyteller…":"Estimado narrador…"}
      </div>
      <textarea value={br.body} maxLength={600}
        onChange={e => set("brief.body", e.target.value)}
        rows={6} placeholder={lang==="en"?"Describe the story you imagine, the mood you want her to land in, anything specific that matters this week…":"Describe la historia que imaginas, el tono que quieres dejarle, lo que importa esta semana…"}
        style={{
          width:"100%", resize:"vertical", padding:"12px 16px",
          background:"rgba(255,255,255,.04)", border:"1px solid rgba(255,255,255,.12)",
          borderRadius: 4, color:"#F0EBE0", fontSize:".95rem", outline:"none",
          fontFamily:"'Cormorant Garamond', Georgia, serif", fontStyle:"italic", lineHeight: 1.6,
        }}/>
      <div style={{ textAlign:"right", fontSize:".68rem", color:"rgba(240,235,224,.4)", marginTop: 4 }}>
        {br.body.length}/600
      </div>

      <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr", gap:"1rem", marginTop:"1rem" }}>
        <div>
          <div className="label-tiny" style={{ color:"rgba(240,235,224,.45)", marginBottom: 6 }}>
            {lang==="en"?"Mood target":"Tono objetivo"}
          </div>
          <div style={{ display:"flex", flexWrap:"wrap", gap: 6 }}>
            {moods.map(([id, l]) => (
              <button key={id} onClick={() => set("brief.mood", id)} style={{
                padding:"5px 11px", borderRadius: 14,
                background: br.mood === id ? "rgba(125,190,90,.18)" : "rgba(255,255,255,.03)",
                border: `1px solid ${br.mood === id ? "#9DC872" : "rgba(255,255,255,.1)"}`,
                color: br.mood === id ? "#F0EBE0" : "rgba(240,235,224,.55)",
                fontSize:".74rem", cursor:"pointer",
              }}>{l}</button>
            ))}
          </div>
        </div>
        <TFld label={lang==="en"?"Cultural context":"Contexto cultural"} v={br.cultural} on={v => set("brief.cultural", v)} placeholder={lang==="en"?"e.g. Barcelona, autumn":"ej. Barcelona, otoño"}/>
      </div>

      <div style={{ marginTop:"1rem" }}>
        <div className="label-tiny" style={{ color:"#E24B4A", marginBottom: 6 }}>
          {lang==="en"?"Things to avoid (hard — never appear)":"Cosas a evitar (estricto — nunca aparecen)"}
        </div>
        <div style={{ display:"flex", flexWrap:"wrap", gap: 6 }}>
          {avoids.map(a => (
            <button key={a} onClick={() => toggle("brief.avoid", a)} style={{
              padding:"5px 11px", borderRadius: 14,
              background: br.avoid.includes(a) ? "rgba(226,75,74,.16)" : "rgba(255,255,255,.03)",
              border: `1px solid ${br.avoid.includes(a) ? "#E24B4A" : "rgba(255,255,255,.1)"}`,
              color: br.avoid.includes(a) ? "#E24B4A" : "rgba(240,235,224,.55)",
              fontSize:".74rem", cursor:"pointer",
            }}>{br.avoid.includes(a) ? "✕ " : ""}{a}</button>
          ))}
        </div>
      </div>

      <div style={{ marginTop:"1rem" }}>
        <div className="label-tiny" style={{ color:"rgba(240,235,224,.45)", marginBottom: 6 }}>
          {lang==="en"?"Link to a WiseTip edition (optional)":"Vincular a una edición WiseTip (opcional)"}
        </div>
        <div style={{ display:"flex", flexWrap:"wrap", gap: 6 }}>
          <button onClick={() => set("brief.edition", "")} style={{
            padding:"5px 11px", borderRadius: 14,
            background: !br.edition ? "rgba(125,190,90,.18)" : "rgba(255,255,255,.03)",
            border: `1px solid ${!br.edition ? "#9DC872" : "rgba(255,255,255,.1)"}`,
            color: !br.edition ? "#F0EBE0" : "rgba(240,235,224,.55)",
            fontSize:".74rem", cursor:"pointer",
          }}>{lang==="en"?"None":"Ninguna"}</button>
          {editionPicks.map(n => {
            const m = WS_MILESTONES.find(x => x.num === n);
            return (
              <button key={n} onClick={() => set("brief.edition", `#${n}`)} style={{
                padding:"5px 11px", borderRadius: 14, textAlign:"left",
                background: br.edition === `#${n}` ? "rgba(125,190,90,.18)" : "rgba(255,255,255,.03)",
                border:`1px solid ${br.edition === `#${n}` ? "#9DC872" : "rgba(255,255,255,.1)"}`,
                color:"#F0EBE0", fontSize:".74rem", cursor:"pointer",
              }}>#{n}{m ? ` · ${m.title[lang].slice(0,18)}` : ""}</button>
            );
          })}
        </div>
        <div style={{ fontSize:".7rem", color:"rgba(240,235,224,.4)", marginTop: 6 }}>
          {lang==="en"?"Themes from that edition will echo through the story — never retold.":"Los temas de esa edición resonarán en la historia — sin contarla."}
        </div>
      </div>
    </>
  );
}

// ── STEP 8 · ORDER + master-prompt reveal ─────────────────
function S8Order({ d, lang, isMember, priceFor, nav }) {
  const [showPrompt, setShowPrompt] = useState(false);
  const [confirmed, setConfirmed] = useState(false);
  const prompt = useMemo(() => buildTailorPrompt(d), [d]);
  const stdP = priceFor(d.duration, "std"), memP = priceFor(d.duration, "member");
  const summary = [
    [lang==="en"?"Format":"Formato",   lang==="en"?"Linear · single MP3":"Lineal · un MP3"],
    [lang==="en"?"World":"Mundo",     WORLD_LABEL[d.world][lang]],
    [lang==="en"?"Voice":"Voz",       TAILOR_VOICES.find(v=>v.id===d.voice)?.label || "—"],
    [lang==="en"?"Duration":"Duración", DUR_LABEL[d.duration][lang]],
    [lang==="en"?"Language":"Idioma",   LANG_LABEL[d.language][lang]],
    [lang==="en"?"Mood":"Tono",         MOOD_LABEL[d.brief.mood][lang]],
    [lang==="en"?"Protagonist":"Protagonista", d.char.name || (lang==="en"?"unnamed":"sin nombre")],
    [lang==="en"?"Edition link":"Edición vinculada", d.brief.edition || "—"],
  ];

  if (confirmed) {
    return (
      <div style={{ textAlign:"center", padding:"2.5rem 1rem 1rem" }}>
        <div style={{ fontSize:"3rem", marginBottom:".75rem" }}>🎧</div>
        <h2 className="serif italic" style={{ fontWeight: 400, fontSize:"1.8rem", color:"#F0EBE0", marginBottom: 8 }}>
          {lang==="en"?"Your story is being written.":"Tu historia se está escribiendo."}
        </h2>
        <p style={{ color:"rgba(240,235,224,.6)", fontSize:".92rem", maxWidth: 440, margin:"0 auto", lineHeight: 1.65 }}>
          {lang==="en"
            ? `Claude is formatting the script now. ElevenLabs will narrate it. You'll get an email and a library entry within 12 minutes. Order ID: TAB-${Date.now().toString().slice(-6)}.`
            : `Claude está formateando el guion. ElevenLabs lo narrará. Recibirás un email y una entrada en tu biblioteca en 12 minutos. ID: TAB-${Date.now().toString().slice(-6)}.`}
        </p>
        <button onClick={() => nav("profile")} style={{
          marginTop:"1.5rem", background:"transparent", color:"#9DC872",
          border:"1px solid #9DC87266", padding:"10px 20px", borderRadius: 22,
          fontSize:".82rem", cursor:"pointer",
        }}>{lang==="en"?"Open my library":"Ir a mi biblioteca"} →</button>
      </div>
    );
  }

  return (
    <>
      <StepHead lang={lang}
        ey={lang==="en"?"Review & order":"Revisar y encargar"}
        title={lang==="en"?"Seven quiet choices — assembled.":"Siete elecciones tranquilas — listas."}
        sub={lang==="en"?"Last chance to edit. The master prompt below is exactly what we'll send to the generation engine.":"Última oportunidad de editar. El prompt maestro de abajo es exactamente lo que enviamos al motor."}/>

      {/* ── Summary table ── */}
      <div style={{
        background:"rgba(255,255,255,.03)", border:"1px solid rgba(125,190,90,.16)",
        borderRadius: 6, overflow:"hidden", marginBottom:"1rem",
      }}>
        {summary.map(([k, v], i) => (
          <div key={k} style={{
            display:"flex", justifyContent:"space-between", alignItems:"center",
            padding:"10px 16px",
            borderBottom: i === summary.length - 1 ? "none" : "1px solid rgba(255,255,255,.05)",
          }}>
            <span className="label-tiny" style={{ color:"rgba(240,235,224,.45)" }}>{k}</span>
            <span style={{ fontSize:".88rem", color:"#F0EBE0" }}>{v}</span>
          </div>
        ))}
      </div>

      {/* ── Pricing strip (dual: pay-per-item OR included in WiseTip+) ── */}
      <div className="tailor-pricing" style={{ display:"grid", gridTemplateColumns:"1fr 1fr", gap: 10, marginBottom:"1rem" }}>
        <div style={{
          background:"rgba(255,255,255,.03)", border:"1px solid rgba(255,255,255,.08)",
          borderRadius: 8, padding:"1.1rem",
        }}>
          <div className="label-tiny" style={{ color:"#C8A85A", marginBottom: 4 }}>{lang==="en"?"Pay per story":"Pago por historia"}</div>
          <div className="serif" style={{ fontSize:"1.8rem", fontWeight: 300, color:"#F0EBE0", lineHeight: 1 }}>€{stdP}</div>
          <div style={{ fontSize:".7rem", color:"rgba(240,235,224,.5)", marginTop: 4 }}>{lang==="en"?"One-off · keep forever":"Pago único · para siempre"}</div>
        </div>
        <div style={{
          background:"rgba(125,190,90,.08)", border:"1px solid #9DC872",
          borderRadius: 8, padding:"1.1rem", position:"relative",
        }}>
          <div style={{
            position:"absolute", top:".6rem", right:".6rem",
            fontSize:".55rem", letterSpacing:".18em", padding:"2px 7px",
            background:"#9DC872", color:"#050F07", borderRadius: 8, fontWeight: 700,
          }}>WISETIP+</div>
          <div className="label-tiny" style={{ color:"#9DC872", marginBottom: 4 }}>{lang==="en"?"Member price":"Precio miembro"}</div>
          <div className="serif" style={{ fontSize:"1.8rem", fontWeight: 300, color:"#F0EBE0", lineHeight: 1 }}>€{memP}</div>
          <div style={{ fontSize:".7rem", color:"rgba(240,235,224,.5)", marginTop: 4 }}>{lang==="en"?"Save 20% · priority queue":"Ahorra 20% · cola prioritaria"}</div>
        </div>
      </div>
      <style>{`@media (max-width: 600px) { .tailor-pricing { grid-template-columns: 1fr !important; } }`}</style>

      {/* ── Master prompt reveal ── */}
      <div style={{
        border:"1px solid rgba(200,168,90,.3)", borderRadius: 8, overflow:"hidden", marginBottom:"1rem",
      }}>
        <button onClick={() => setShowPrompt(s => !s)} style={{
          width:"100%", padding:"12px 16px", background:"rgba(200,168,90,.08)",
          border:"none", color:"#C8A85A", cursor:"pointer",
          display:"flex", justifyContent:"space-between", alignItems:"center",
          fontSize:".78rem", letterSpacing:".14em", textTransform:"uppercase",
        }}>
          <span>◐ {lang==="en"?"Master prompt sent to engine":"Prompt maestro enviado al motor"}</span>
          <span style={{ fontSize:"1rem" }}>{showPrompt ? "−" : "+"}</span>
        </button>
        {showPrompt && (
          <pre style={{
            margin: 0, padding:"1.25rem 1.5rem",
            background:"rgba(0,0,0,.25)", color:"rgba(240,235,224,.7)",
            fontFamily:"'JetBrains Mono', monospace", fontSize:".7rem", lineHeight: 1.8,
            whiteSpace:"pre-wrap", maxHeight: 380, overflowY:"auto",
          }}>{prompt}</pre>
        )}
      </div>

      <button onClick={() => setConfirmed(true)} style={{
        width:"100%", padding:"14px", background:"#9DC872", color:"#050F07",
        border:"none", borderRadius: 6, fontSize:".95rem", fontWeight: 700, cursor:"pointer",
      }}>{lang==="en" ? `▶ Confirm & generate · €${isMember?memP:stdP}` : `▶ Confirmar y generar · ${(isMember?memP:stdP).toString().replace(".",",")} €`}</button>
    </>
  );
}

function StepHead({ ey, title, sub }) {
  return (
    <div style={{ marginBottom:"1.5rem" }}>
      <div className="label-tiny" style={{ color:"#9DC872", marginBottom: 6 }}>{ey}</div>
      <h2 className="serif italic" style={{
        fontWeight: 400, fontSize:"clamp(1.5rem, 3vw, 2.2rem)",
        color:"#F0EBE0", lineHeight: 1.1, marginBottom: 8,
      }}>{title}</h2>
      <p style={{ color:"rgba(240,235,224,.55)", fontSize:".88rem", lineHeight: 1.6, fontWeight: 300, maxWidth: 520 }}>{sub}</p>
    </div>
  );
}

// ── Master prompt builder ────────────────────────────────
// Outputs a two-part payload:
//   (1) the human-readable instructions sent to Claude
//   (2) a JSON contract matching the WiseTip Audio System spec §1.3
//       — Claude returns {title, subtitle, duration_estimate, word_count,
//          language, narrator_voice, script, voice_cast, production_notes,
//          disclaimer}, which is fed to ElevenLabs for a single MP3.

function buildTailorPrompt(d) {
  const WORLD = {
    nature:  "a forest and natural world — ground every scene in specific sensory detail: scent of bark, sound of water, quality of light through leaves.",
    family:  "a home and family setting — named smells, textures, voices. Warm and specific.",
    fantasy: "an invented world with consistent rules. Name at least one creature, one place, one rule that surprises.",
    sea:     "a coastal/maritime setting — salty air, sound of waves, quality of sea light, texture of sand.",
    city:    "an urban neighbourhood — streets, community, warmth. The city is human and safe.",
    mystery: "something hidden or unknown. The child discovers it through observation, not luck.",
  };
  const NARR = {
    warm_female_1: "Warm female narrator (Sofia). Vary sentence length. Mark [PAUSE X.X] at natural breath points. Use [BREATH] where a narrator inhales.",
    warm_male_1:   "Warm male narrator (Marco). Steady, unhurried. Use [PAUSE X.X] and [BREATH] naturally.",
    child_1:       "Child narrator (Kai), similar age. Close first or third person. Match vocabulary to age.",
    neutral:       "Classic neutral narrator. Measured, timeless bedtime register.",
  };
  const DUR = {
    flash:    { label: "Flash 2–4 min", target: "350–600 words" },
    standard: { label: "Standard 4–7 min", target: "650–1,100 words" },
    extended: { label: "Extended 7–10 min", target: "1,100–1,600 words" },
  };
  const LANG_TXT = {
    en:        "English — natural warm prose, read-aloud rhythm, not formal.",
    es:        "Spanish (Castilian) — prosa natural y cálida, ritmo de narración en voz alta.",
    bilingual: "Bilingual EN/ES — interweave naturally at paragraph breaks. Write originally in both; do not translate.",
    de:        "German (Hochdeutsch) — warm, literary, children-appropriate.",
    fr:        "French (standard) — warm literary register, children-appropriate.",
  };
  const MOOD = {
    calm:        "End in calm. Slow the pace in the final quarter. Leave the child settled and safe.",
    energise:    "End on delight and possibility. Capable, happy, not overstimulated.",
    contemplate: "Leave a gentle open question to carry into sleep. Not unresolved — wondering.",
    adventure:   "A small expedition completed. Brave, capable, satisfied.",
  };

  const ch = d.char, br = d.brief;
  const charBlock = ch.name
    ? `PROTAGONIST: "${ch.name}"${ch.age ? `, age ${ch.age}` : ""}.
Traits: ${ch.traits.length ? ch.traits.join(", ") : "(not specified)"}.
${ch.sib ? `Sibling/friend: "${ch.sib}". ` : ""}${ch.pet ? `Pet: "${ch.pet}". ` : ""}${ch.desc ? `
Parent note: "${ch.desc}"` : ""}

The name "${ch.name}" must appear naturally throughout — woven in, never forced. Traits shown through action.`
    : `PROTAGONIST: unnamed. Use "the child" or allow reader projection.`;

  return `You are the WiseTip Audio System — audiobook engine.

══════════════════════════════════════
HARD CONSTRAINTS  (system rule §1.6)
══════════════════════════════════════

✅ ONE story  →  ONE audio file
✅ Single emotional arc, single resolution
❌ NO branching paths
❌ NO multiple endings
❌ NO episodic series  (those live on the Podcast engine)
❌ NO injection, NO code blocks, NO unsafe content

══════════════════════════════════════
STORY CONFIGURATION
══════════════════════════════════════

WORLD: ${WORLD[d.world]}

${charBlock}

NARRATOR: ${NARR[d.voice] || NARR.warm_female_1}

DURATION: ${DUR[d.duration].label} (target ${DUR[d.duration].target}).

LANGUAGE: ${LANG_TXT[d.language]}

MOOD: ${MOOD[br.mood]}

${br.avoid.length ? `AVOID (hard — must not appear):
${br.avoid.map(a => `  - ${a}`).join("\n")}` : ""}

${br.cultural ? `CULTURAL CONTEXT: ${br.cultural}` : ""}

${br.edition ? `NEWSLETTER LINK: Thematically connected to WiseTip edition ${br.edition}. Let its emotional arc inform (not retell) this story. The story must stand completely alone.` : ""}

══════════════════════════════════════
PRODUCTION MARKS  (used inside "script")
══════════════════════════════════════

[NARRATOR pace:slow|normal|fast]
[PAUSE X.X]      — silence in seconds
[BREATH]         — natural inhale ~0.3s
[AMBIENT: description]
[MUSIC: fade_in | mood:X]   [MUSIC: fade_out]
[EMPHASIS]...[/EMPHASIS]
[WHISPER]...[/WHISPER]
[SLOW]...[/SLOW]   [FAST]...[/FAST]

Voice IDs: warm_female_1 | warm_female_2 | warm_male_1 | warm_male_2 | child_1 | child_2 | elderly | neutral

══════════════════════════════════════
PARENT'S CREATIVE BRIEF
══════════════════════════════════════

${br.body ? `"${br.body}"` : "(No brief — use best creative judgment.)"}

══════════════════════════════════════
OUTPUT  (return EXACTLY this JSON contract — spec §1.3)
══════════════════════════════════════

{
  "title":             "Story title",
  "subtitle":          "One-line premise",
  "duration_estimate": "${DUR[d.duration].label.replace('Flash ','').replace('Standard ','').replace('Extended ','')}",
  "word_count":        <integer>,
  "language":          "${d.language}",
  "narrator_voice":    "${d.voice}",
  "script":            "Full narrated script with production marks",
  "voice_cast":        { "Narrator": "${d.voice}" },
  "production_notes":  "Pauses, timing, pacing instructions",
  "disclaimer":        "Names, characters, and events in this story are fictional."
}

Begin script with [MUSIC: fade_in | mood:${br.mood}] and end with [MUSIC: fade_out].
The script will be rendered by ElevenLabs into a single 320 kbps MP3.`;
}

// ── ADMIN SCRIPT UPLOAD ──────────────────────────────────
// Power-user surface (spec §1.2.B): paste a fully authored script,
// scan for injection patterns, preview voice assignment.

function AdminScriptUpload({ theme, dark, lang, nav, onBack }) {
  const [raw, setRaw] = useState("");
  const [scan, setScan] = useState(null);
  const wc = raw.trim().split(/\s+/).filter(Boolean).length;

  const runScan = () => {
    const t = raw.toLowerCase();
    const issues = [];
    const inj = [
      ["ignore previous instructions","Prompt injection"],
      ["disregard your","Instruction override"],
      ["forget your instructions","Instruction override"],
      ["new system prompt","System injection"],
      ["jailbreak","Jailbreak keyword"],
      ["<script","XSS: script tag"],
      ["javascript:","XSS: javascript URI"],
      ["onerror=","XSS: event handler"],
      ["eval(","Code evaluation"],
      ["document.cookie","Cookie theft"],
      ["; drop table","SQL injection"],
    ];
    for (const [p, m] of inj) if (t.includes(p)) issues.push({ sev:"critical", msg: m, pat: p });
    const tags = ["[narrator","[character:","[pause","[breath","[ambient:","[music:","[emphasis]","[whisper]"];
    const used = tags.filter(k => t.includes(k));
    setScan({ issues, used, wc, clean: issues.length === 0 });
  };

  return (
    <div style={{ background: dark ? theme.panel : "#0E2318", color:"#F0EBE0", paddingTop:"5rem", minHeight:"100vh" }}>
      <div style={{ padding:"3.5rem clamp(1.5rem, 5vw, 5vw) 1.5rem" }}>
        <div className="wrap">
          <button onClick={onBack} style={{
            background:"none", border:"none", color:"#9DC872", cursor:"pointer",
            fontSize:".78rem", marginBottom:"1rem", letterSpacing:".06em",
          }}>← {lang==="en"?"Back to guided builder":"Volver al constructor guiado"}</button>
          <Pill color="#C8A85A" bd="#C8A85A66" style={{ marginBottom:"1rem" }}>⚙ Admin · script upload</Pill>
          <h1 className="serif" style={{
            fontStyle:"italic", fontWeight: 300, fontSize:"clamp(1.8rem, 4vw, 2.8rem)",
            color:"#F0EBE0", lineHeight: 1.1, letterSpacing:"-.02em", maxWidth: 760,
          }}>{lang==="en"
            ? <>For editors and power-users. <em style={{ color:"#9DC872" }}>Paste a fully authored script.</em></>
            : <>Para editores y usuarios avanzados. <em style={{ color:"#9DC872" }}>Pega un guion ya escrito.</em></>}</h1>
          <p style={{ color:"rgba(240,235,224,.55)", fontSize:".92rem", lineHeight: 1.7, fontWeight: 300, marginTop: 14, maxWidth: 600 }}>
            {lang==="en"
              ? "Use WiseTip production marks: [NARRATOR pace:normal], [PAUSE 0.6], [AMBIENT: …], [MUSIC: …]. Every paste is security-scanned for injection patterns and unsafe content before reaching the engine."
              : "Usa las marcas WiseTip: [NARRATOR pace:normal], [PAUSE 0.6], [AMBIENT: …], [MUSIC: …]. Cada entrada se escanea por patrones de inyección y contenido inseguro antes de llegar al motor."}
          </p>
        </div>
      </div>

      <div style={{ padding:"1.5rem clamp(1.5rem, 5vw, 5vw) 5rem" }}>
        <div className="wrap" style={{
          background:"rgba(8,15,10,.5)", border:"1px solid rgba(125,190,90,.16)",
          borderRadius: 8, padding:"2rem 2.25rem",
        }}>
          <div className="label-tiny" style={{ color:"#9DC872", marginBottom: 8 }}>
            {lang==="en"?"Paste your script":"Pega tu guion"}
          </div>

          {/* Format reference */}
          <pre style={{
            background:"rgba(0,0,0,.25)", border:"1px solid rgba(255,255,255,.05)",
            borderRadius: 4, padding:"12px 14px",
            fontFamily:"'JetBrains Mono', monospace", fontSize:".72rem", lineHeight: 1.8,
            color:"rgba(240,235,224,.55)", marginBottom: 14, whiteSpace:"pre-wrap",
          }}>{`[NARRATOR pace:normal]
Once upon a time…

[CHARACTER: Leo voice:warm_male_1 emotion:curious]
"What do you hear?"

[PAUSE 0.6]
[AMBIENT: forest wind]`}</pre>

          <textarea value={raw} onChange={e => setRaw(e.target.value)} rows={14}
            placeholder={lang==="en"?"Paste your WiseTip-formatted script here…":"Pega aquí tu guion con formato WiseTip…"}
            style={{
              width:"100%", resize:"vertical",
              padding:"14px 16px",
              background:"rgba(255,255,255,.04)", border:"1px solid rgba(255,255,255,.12)",
              borderRadius: 4, color:"#F0EBE0",
              fontFamily:"'JetBrains Mono', monospace", fontSize:".78rem", lineHeight: 1.7,
              outline:"none",
            }}/>

          <div style={{ display:"flex", justifyContent:"space-between", alignItems:"center", marginTop: 12, flexWrap:"wrap", gap: 12 }}>
            <span className="mono" style={{ fontSize:".72rem", color:"rgba(240,235,224,.5)" }}>{wc} {lang==="en"?"words":"palabras"}</span>
            <button disabled={!raw.trim()} onClick={runScan} style={{
              background: raw.trim() ? "#9DC872" : "rgba(125,190,90,.2)",
              color: raw.trim() ? "#050F07" : "rgba(240,235,224,.5)",
              border:"none", padding:"10px 22px", borderRadius: 22,
              fontSize:".84rem", fontWeight: 600, cursor: raw.trim() ? "pointer" : "not-allowed",
            }}>🔍 {lang==="en"?"Validate & preview":"Validar y previsualizar"} →</button>
          </div>

          {/* Scan results */}
          {scan && (
            <div style={{ marginTop:"1.5rem" }}>
              <div style={{
                background: scan.clean ? "rgba(125,190,90,.06)" : "rgba(226,75,74,.08)",
                border: `1px solid ${scan.clean ? "rgba(125,190,90,.3)" : "rgba(226,75,74,.4)"}`,
                borderRadius: 6, padding:"14px 18px",
              }}>
                <div style={{ fontSize:".82rem", color: scan.clean ? "#9DC872" : "#E24B4A", fontWeight: 500, marginBottom: 8 }}>
                  {scan.clean
                    ? `✓ ${lang==="en"?"Passed security scan":"Pasó el escaneo de seguridad"} · ${scan.wc} ${lang==="en"?"words":"palabras"}`
                    : `🚨 ${scan.issues.length} ${lang==="en"?"critical issue(s) — cannot proceed":"problema(s) crítico(s) — no se puede continuar"}`}
                </div>
                {scan.issues.map((iss, i) => (
                  <div key={i} style={{ fontSize:".76rem", color:"rgba(240,235,224,.7)", marginTop: 4 }}>
                    <strong style={{ color:"#E24B4A" }}>{iss.msg}</strong> · pattern: <code>"{iss.pat}"</code>
                  </div>
                ))}
                {scan.used.length > 0 && (
                  <div style={{ fontSize:".74rem", color:"rgba(240,235,224,.55)", marginTop: 8 }}>
                    {scan.used.length} {lang==="en"?"WiseTip production tags detected":"marcas de producción WiseTip detectadas"}
                  </div>
                )}
              </div>
              {scan.clean && (
                <button style={{
                  marginTop: 14, background:"#9DC872", color:"#050F07", border:"none",
                  padding:"12px 26px", borderRadius: 24, fontSize:".88rem", fontWeight: 600, cursor:"pointer",
                }}>{lang==="en"?"Confirm & send to ElevenLabs":"Confirmar y enviar a ElevenLabs"} →</button>
              )}
            </div>
          )}
        </div>
      </div>
      <Footer theme={theme} dark={dark} lang={lang} nav={nav}/>
    </div>
  );
}

Object.assign(window, { TailorPage });
