// Registration-form steps. Each step is a React component that receives
// the form data, an updater, and per-field errors. The orchestrator in
// registration-app.jsx decides which step to show.

// Countries list (separate from nationalities — what country are you based
// in right now, regardless of passport).
const REG_COUNTRIES = [
  "Argentina", "Australia", "Austria", "Belgium", "Brazil", "Canada",
  "Chile", "China", "Colombia", "Croatia", "Czech Republic", "Denmark",
  "Egypt", "Estonia", "Finland", "France", "Germany", "Greece", "Hong Kong",
  "Hungary", "Iceland", "India", "Indonesia", "Ireland", "Israel", "Italy",
  "Japan", "Jordan", "Kazakhstan", "Latvia", "Lithuania", "Luxembourg",
  "Malaysia", "Mexico", "Monaco", "Morocco", "Netherlands", "New Zealand",
  "Norway", "Philippines", "Poland", "Portugal", "Qatar", "Romania",
  "Russia", "Saudi Arabia", "Serbia", "Singapore", "Slovakia", "Slovenia",
  "South Africa", "South Korea", "Spain", "Sweden", "Switzerland",
  "Thailand", "Turkey", "UAE", "UK", "Ukraine", "USA", "Vietnam", "Other",
];

function cleanRateInput(value) {
  return String(value || "").replace(/[^\d]/g, "").slice(0, 6);
}

function rateValue(data, side, key) {
  return String(data.rates?.[side]?.[key] || "");
}

function updateRate(data, update, side, key, value) {
  update("rates", {
    ...(data.rates || {}),
    [side]: {
      ...(data.rates?.[side] || {}),
      [key]: cleanRateInput(value),
    },
  });
}

function rateRows(data) {
  const rows = window.REG_RATE_ROWS || [];
  return rows.map((row) => ({
    ...row,
    incallValue: rateValue(data, "incall", row.key),
    outcallValue: rateValue(data, "outcall", row.key),
  }));
}

function money(value) {
  const number = Number(cleanRateInput(value));
  return number > 0 ? `£${number.toLocaleString("en-GB")}` : "";
}

function modelPriceRows(data) {
  return rateRows(data)
    .map((row) => ({
      key: row.key,
      label: row.label,
      incall: money(row.incallValue),
      outcall: money(row.outcallValue),
    }))
    .filter((row) => row.incall || row.outcall);
}

function priceFrom(data) {
  const values = rateRows(data)
    .flatMap((row) => [row.incallValue, row.outcallValue])
    .map((value) => Number(cleanRateInput(value)))
    .filter((value) => Number.isFinite(value) && value > 0);
  return values.length ? `From ${money(Math.min(...values))}` : "";
}

function formatRates(data) {
  const rows = modelPriceRows(data);
  return rows.length
    ? rows.map((row) => `${row.label}: ${[row.incall && `Incall ${row.incall}`, row.outcall && `Outcall ${row.outcall}`].filter(Boolean).join(" / ")}`).join("; ")
    : "—";
}

function RegRates({ data, update, error }) {
  return (
    <div className="reg-row">
      <label className="reg-label">
        <span>Rates</span><span className="req">required</span>
      </label>
      <div className="reg-help">
        Enter whole GBP amounts. Leave a cell blank only when that booking type is not offered.
      </div>
      <div className="reg-rates">
        <div className="reg-rate-head" aria-hidden="true">
          <span>Duration</span>
          <span>Incall</span>
          <span>Outcall</span>
        </div>
        {(window.REG_RATE_ROWS || []).map((row) => (
          <div className="reg-rate-row" key={row.key}>
            <div className="reg-rate-label">{row.label}</div>
            <label className="reg-rate-input">
              <span className="sr-only">{row.label} incall rate</span>
              <input
                type="text"
                inputMode="numeric"
                className={"reg-input" + (error ? " error" : "")}
                value={rateValue(data, "incall", row.key)}
                onChange={(event) => updateRate(data, update, "incall", row.key, event.target.value)}
                placeholder="800"
                aria-invalid={error ? "true" : "false"}
              />
            </label>
            <label className="reg-rate-input">
              <span className="sr-only">{row.label} outcall rate</span>
              <input
                type="text"
                inputMode="numeric"
                className={"reg-input" + (error ? " error" : "")}
                value={rateValue(data, "outcall", row.key)}
                onChange={(event) => updateRate(data, update, "outcall", row.key, event.target.value)}
                placeholder="950"
                aria-invalid={error ? "true" : "false"}
              />
            </label>
          </div>
        ))}
      </div>
      {error && <div className="reg-error" role="alert">{error}</div>}
    </div>
  );
}

// ─── Step 0 — Welcome ──────────────────────────────────────────────────
function StepWelcome({ onStart }) {
  return (
    <div style={{
      display:"flex", flexDirection:"column", alignItems:"center",
      textAlign:"center", gap: 36, maxWidth: 720,
    }}>
      <div style={{
        fontFamily:"var(--mono)", fontSize: 11, letterSpacing:".22em",
        textTransform:"uppercase", color:"rgba(243,239,233,0.55)",
      }}>
        ELARA · Applicants · 2026
      </div>

      <h1 style={{
        fontFamily:"var(--font-display)", fontWeight: 500,
        letterSpacing:"-0.04em", lineHeight: 0.9,
        fontSize: "clamp(72px, 9vw, 140px)",
        margin: 0,
      }}>
        Apply
      </h1>

      <div style={{
        fontFamily:"var(--font-display)", fontStyle:"italic",
        fontSize:"clamp(20px, 2vw, 28px)", lineHeight: 1.45,
        color:"rgba(243,239,233,0.78)",
        maxWidth: 560,
      }}>
        Tell us a little about yourself. The next eight steps build the
        editorial profile our bookers see — the more honest and specific
        you can be, the better the work we send your way.
      </div>

      <div style={{ display:"flex", gap: 12, alignItems:"center", marginTop: 8 }}>
        <button className="reg-btn primary" onClick={onStart}>Begin <span className="arrow">→</span></button>
      </div>

      <div style={{
        fontFamily:"var(--mono)", fontSize: 10.5, letterSpacing:".18em",
        textTransform:"uppercase", color:"rgba(243,239,233,0.4)", marginTop: 4,
      }}>
        — takes about 6 minutes
      </div>
    </div>
  );
}

// ─── Step 1 — Basics ───────────────────────────────────────────────────
function StepBasics({ data, update, errors }) {
  return (
    <StepShell title="Basics" eyebrow="01 / Personal">
      <RegText label="Real first name" value={data.firstName}
        onChange={(v) => update("firstName", v)}
        placeholder="Elena" required autoFocus
        help="Staff-only. Never shown on your public profile."
        error={errors.firstName}/>
      <RegText label="Real last name" value={data.lastName}
        onChange={(v) => update("lastName", v)}
        placeholder="Voss" required
        help="Staff-only. Never shown on your public profile."
        error={errors.lastName}/>
      <RegText label="Alias" value={data.alias}
        onChange={(v) => update("alias", v)}
        placeholder="Elena V." required
        help="The name members will see on your profile. Pick one you'll respond to comfortably."
        error={errors.alias}/>
      <RegDateParts label="Date of birth" value={data.dob}
        onChange={(v) => update("dob", v)} required
        minAge={18}
        help="Your age is calculated and displayed on the profile — the full date is never shown."
        error={errors.dob}/>
      <RegText label="Email" value={data.email}
        onChange={(v) => update("email", v)}
        placeholder="you@example.com" required
        help="Staff-only. Used by the desk to reach you about bookings."
        error={errors.email}/>
      <RegText label="WhatsApp number" value={data.phone}
        onChange={(v) => update("phone", v)}
        placeholder="+44 7700 900000" required
        help="Staff-only. With country code. We send urgent booking details here."
        error={errors.phone}/>
    </StepShell>
  );
}

// ─── Step 2 — Measurements ─────────────────────────────────────────────
function StepMeasurements({ data, update, errors }) {
  const O = window.REG_OPTIONS;
  return (
    <StepShell title="Measurements" eyebrow="02 / The numbers">
      <RegHeight value={data.heightCm}
        onChange={(v) => update("heightCm", v)} required
        error={errors.heightCm}/>

      <div className="reg-two-col">
        <RegDropdown label="Cup size" value={data.cupSize}
          options={O.cupSizes}
          onChange={(v) => update("cupSize", v)} required
          error={errors.cupSize}/>
        <RegDropdown label="Band size" value={data.bandSize}
          options={O.bandSizes}
          onChange={(v) => update("bandSize", v)} required
          error={errors.bandSize}/>
      </div>

      <RegRadio label="Breasts" value={data.breasts}
        options={O.breasts}
        onChange={(v) => update("breasts", v)} required
        error={errors.breasts}/>

      <RegSizeWithRegion label="Dress size" value={data.dressSize}
        table={window.REG_SIZE_TABLES.dress}
        onChange={(v) => update("dressSize", v)} required
        error={errors.dressSize}/>

      <RegSizeWithRegion label="Shoe size" value={data.shoeSize}
        table={window.REG_SIZE_TABLES.shoe}
        onChange={(v) => update("shoeSize", v)} required
        error={errors.shoeSize}/>
    </StepShell>
  );
}

// ─── Step 3 — Appearance ───────────────────────────────────────────────
function StepAppearance({ data, update, errors }) {
  const O = window.REG_OPTIONS;
  return (
    <StepShell title="Appearance" eyebrow="03 / Hair, eyes, marks">
      <div className="reg-two-col">
        <RegDropdown label="Hair colour" value={data.hairColor}
          options={O.hairColors}
          onChange={(v) => update("hairColor", v)} required
          error={errors.hairColor}/>
        <RegDropdown label="Hair length" value={data.hairLength}
          options={O.hairLengths}
          onChange={(v) => update("hairLength", v)} required
          error={errors.hairLength}/>
      </div>

      <RegDropdown label="Eye colour" value={data.eyeColor}
        options={O.eyeColors}
        onChange={(v) => update("eyeColor", v)} required
        error={errors.eyeColor}/>

      <RegYesNoDetails label="Tattoos" value={data.tattoos}
        onChange={(v) => update("tattoos", v)}
        detailLabel="Where, and roughly how large?"/>

      <RegYesNoDetails label="Piercings" value={data.piercings}
        onChange={(v) => update("piercings", v)}
        detailLabel="Where? (e.g. single lobe, nose)"/>
    </StepShell>
  );
}

// ─── Step 4 — Origins ──────────────────────────────────────────────────
function StepOrigins({ data, update, errors }) {
  const O = window.REG_OPTIONS;
  return (
    <StepShell title="Origins" eyebrow="04 / Nationality, base, languages">
      <RegDropdown label="Nationality" value={data.nationality}
        options={O.nationalities}
        onChange={(v) => update("nationality", v)} required
        error={errors.nationality}/>

      <div className="reg-two-col">
        <RegText label="Based in — city" value={data.basedInCity}
          onChange={(v) => update("basedInCity", v)}
          placeholder="Your city" required
          error={errors.basedInCity}/>
        <RegText label="Based in — area" value={data.basedInArea}
          onChange={(v) => update("basedInArea", v)}
          placeholder="Mayfair"
          error={errors.basedInArea}/>
      </div>

      <RegDropdown label="Based in — country" value={data.basedInCountry}
        options={REG_COUNTRIES}
        onChange={(v) => update("basedInCountry", v)} required
        error={errors.basedInCountry}/>

      <RegMultiChip label="Languages you speak"
        value={data.languages} options={O.languages}
        onChange={(v) => update("languages", v)} required
        help="Tap any you can hold a working conversation in."
        error={errors.languages}/>
    </StepShell>
  );
}

// ─── Step 5 — Working with you ─────────────────────────────────────────
function StepWork({ data, update, errors }) {
  const O = window.REG_OPTIONS;
  return (
    <StepShell title="Working with you" eyebrow="05 / Availability">
      <RegDropdown label="Availability" value={data.availability}
        options={O.availability}
        onChange={(v) => update("availability", v)} required
        help="How much heads-up bookers should give you."
        error={errors.availability}/>

      <RegDropdown label="Travel" value={data.travel}
        options={O.travel}
        onChange={(v) => update("travel", v)} required
        help="Regions you're willing to fly to."
        error={errors.travel}/>

      <RegMultiChip label="Services"
        value={data.services} options={O.services}
        onChange={(v) => update("services", v)} required
        help="Choose the services the desk should be able to review."
        error={errors.services}/>

      <RegRates data={data} update={update} error={errors.rates}/>
    </StepShell>
  );
}

// ─── Step 6 — In your words ────────────────────────────────────────────
function StepWords({ data, update, errors }) {
  return (
    <StepShell title="In your words" eyebrow="06 / Bio + personality">
      <RegTextArea
        label="Short biography"
        value={data.bio}
        onChange={(v) => update("bio", v)}
        placeholder="A few sentences. Where you trained, the kind of work you like, what you bring to a set."
        rows={5} required
        help="The professional voice — reads under the hero of your profile."
        error={errors.bio}/>

      <RegTextArea
        label="Personality"
        value={data.personality}
        onChange={(v) => update("personality", v)}
        placeholder="Two or three lines about you off-set. What you read, where you go, the things you can't help mentioning."
        rows={4} required
        help="The casual voice — a counterpoint to the bio. Italic on the profile."
        error={errors.personality}/>
    </StepShell>
  );
}

// ─── Step 7 — Portfolio ────────────────────────────────────────────────
function StepPortfolio({ data, update, errors }) {
  const MIN = window.REG_MIN_MEDIA;
  const MAX = window.REG_MAX_MEDIA;
  return (
    <StepShell title="Portfolio" eyebrow="07 / Photos">
      <div style={{
        fontFamily:"var(--font-display)", fontStyle:"italic",
        fontSize:"clamp(17px, 1.5vw, 22px)", lineHeight: 1.5,
        color:"rgba(243,239,233,0.72)", maxWidth: 640,
      }}>
        Submit at least <strong style={{color:"#f3efe9", fontStyle:"normal"}}>{MIN}</strong> images so the
        gallery has rhythm. Aim for a mix — a close beauty, a full-length,
        and an editorial detail. Video opens later on its own secure lane.
      </div>

      <RegFileUpload
        label="Upload" value={data.media}
        onChange={(v) => update("media", v)}
        min={MIN} max={MAX} required
        error={errors.media}/>
    </StepShell>
  );
}

// ─── Step 8 — Review & submit ──────────────────────────────────────────
function StepReview({ data, onEditStep, onSubmit, submitting, uploadProgress }) {
  const fmt = window.REG_FORMATTERS;
  const lines = [
    ["Real name",     `${data.firstName || ""} ${data.lastName || ""}`.trim() || "—"],
    ["Alias",         data.alias || "—"],
    ["Age",           fmt.age(data.dob)],
    ["Email",         data.email || "—"],
    ["WhatsApp",      data.phone || "—"],
    ["Height",        fmt.height(data.heightCm)],
    ["Cup size",      data.cupSize || "—"],
    ["Band",          data.bandSize || "—"],
    ["Breasts",       data.breasts || "—"],
    ["Dress",         data.dressSize ? `EU ${data.dressSize}` : "—"],
    ["Shoe",          data.shoeSize ? `EU ${data.shoeSize}` : "—"],
    ["Hair",          [data.hairColor, data.hairLength].filter(Boolean).join(", ") || "—"],
    ["Eyes",          data.eyeColor || "—"],
    ["Marks",         fmt.marks(data.tattoos, data.piercings)],
    ["Nationality",   data.nationality || "—"],
    ["Based in",      fmt.basedIn(data.basedInArea, data.basedInCity, data.basedInCountry)],
    ["Languages",     (data.languages || []).join(", ") || "—"],
    ["Availability",  data.availability || "—"],
    ["Travel",        data.travel || "—"],
    ["Services",      (data.services || []).join(", ") || "—"],
    ["Rates",         formatRates(data)],
    ["Media",         `${(data.media || []).length} files`],
  ];

  return (
    <StepShell title="Review" eyebrow="08 / Almost done">
      <div style={{
        fontFamily:"var(--font-display)", fontStyle:"italic",
        fontSize:"clamp(18px, 1.7vw, 24px)", lineHeight: 1.5,
        color:"rgba(243,239,233,0.75)", maxWidth: 600,
      }}>
        Glance over the values below. Anything off? Jump back to fix it.
      </div>

      <div className="reg-review-grid" style={{
        marginTop: 28,
        display:"grid", gridTemplateColumns:"1fr 1fr",
        columnGap: 64, rowGap: 0,
        fontFamily:"var(--mono)", fontSize: 12,
        letterSpacing:".04em", textTransform:"uppercase",
      }}>
        {lines.map(([k, v]) => (
          <div key={k} className="reg-review-row" style={{
            display:"grid", gridTemplateColumns:"140px 1fr",
            padding:"14px 0",
            borderBottom:"1px solid rgba(243,239,233,0.10)",
            alignItems:"baseline",
          }}>
            <span style={{ color:"rgba(243,239,233,0.45)" }}>{k}</span>
            <span style={{ color:"#f3efe9", fontWeight:500 }}>{v}</span>
          </div>
        ))}
      </div>

      <div style={{ display:"flex", gap:12, marginTop: 32, alignItems:"center" }}>
        <button className="reg-btn ghost" onClick={() => onEditStep(1)}>
          ← Edit from start
        </button>
        <button className="reg-btn primary" onClick={onSubmit} disabled={submitting}>
          {submitting ? (uploadProgress?.label || "Submitting…") : <>Submit application <span className="arrow">→</span></>}
        </button>
      </div>
      {uploadProgress && (
        <div className="reg-submit-progress" role="status" aria-live="polite">
          <div className="reg-submit-progress__top">
            <span>{uploadProgress.label}</span>
            <span>{uploadProgress.done} / {uploadProgress.total}</span>
          </div>
          <div className="reg-submit-progress__bar">
            <span style={{ width: `${uploadProgress.percent}%` }}/>
          </div>
        </div>
      )}
    </StepShell>
  );
}

// ─── Step 9 — Thank you + live profile preview ─────────────────────────
function StepDone({ data, onDownloadData }) {
  const model = window.REG_BUILD_MODEL(data);
  const config = {
    theme: "dark", type: "grotesque-led",
    hero: "name-overlay", gallery: "justified",
    statsDensity: "full", mediaCount: 100,
    showLedger: false,
  };
  return (
    <div style={{ width:"100%", display:"flex", flexDirection:"column", gap: 64, alignItems:"stretch" }}>
      {/* Thank-you header — centred */}
      <div style={{
        display:"flex", flexDirection:"column", alignItems:"center",
        textAlign:"center", gap: 28, maxWidth: 720, marginInline: "auto",
      }}>
        <div style={{
          fontFamily:"var(--mono)", fontSize: 11, letterSpacing:".22em",
          textTransform:"uppercase", color:"rgba(243,239,233,0.55)",
        }}>
          Application received · file logged
        </div>

        <h1 style={{
          fontFamily:"var(--font-display)", fontWeight: 500,
          letterSpacing:"-0.04em", lineHeight: 0.9,
          fontSize: "clamp(56px, 7vw, 108px)",
          margin: 0,
        }}>
          Thank you,<br/><em style={{fontStyle:"italic"}}>{data.alias || data.firstName || "applicant"}</em>.
        </h1>

        <div style={{
          fontFamily:"var(--font-display)", fontStyle:"italic",
          fontSize:"clamp(18px, 1.6vw, 24px)", lineHeight: 1.45,
          color:"rgba(243,239,233,0.78)",
          maxWidth: 580,
        }}>
          We read every file. We aim to reply within <em style={{ color: "#f3efe9", fontStyle: "normal" }}>48 hours</em>. Your draft profile is below — it's exactly
          what bookers will see.
        </div>
      </div>

      {/* Preview header strip */}
      <div style={{
        display:"flex", alignItems:"baseline", justifyContent:"space-between",
        borderTop: "1px solid rgba(243,239,233,0.16)",
        borderBottom: "1px solid rgba(243,239,233,0.16)",
        padding: "18px 0",
        fontFamily:"var(--mono)", fontSize: 10.5, letterSpacing:".22em",
        textTransform:"uppercase", color:"rgba(243,239,233,0.62)",
      }}>
        <span>Your draft profile</span>
        <span style={{ color:"rgba(243,239,233,0.4)" }}>
          Live preview · 1:1 with the booker view
        </span>
        <button
          type="button"
          onClick={onDownloadData}
          style={{
            appearance:"none", border:0, background:"transparent",
            color:"rgba(243,239,233,0.6)", cursor:"pointer",
            font:"inherit", letterSpacing:"inherit", textTransform:"inherit",
          }}
          onMouseEnter={(e) => e.currentTarget.style.color = "#f3efe9"}
          onMouseLeave={(e) => e.currentTarget.style.color = "rgba(243,239,233,0.6)"}
        >
          Download data ↓
        </button>
      </div>

      {/* The actual profile, rendered identical to the production template */}
      <div style={{
        margin: "0 calc(50% - 50vw)", /* break out of the centred form container */
        width: "100vw",
        boxShadow:"0 0 0 1px rgba(243,239,233,0.08), 0 30px 80px rgba(0,0,0,0.6)",
      }}>
        <ProfileClinical model={model} tweaks={config}/>
      </div>
    </div>
  );
}

// ─── StepShell — title + content frame ─────────────────────────────────
function StepShell({ title, eyebrow, children }) {
  return (
    <div style={{ width:"100%", maxWidth: 880, display:"flex", flexDirection:"column", gap: 32 }}>
      <header style={{ display:"flex", flexDirection:"column", gap: 14 }}>
        <span style={{
          fontFamily:"var(--mono)", fontSize: 10.5, letterSpacing:".22em",
          textTransform:"uppercase", color:"rgba(243,239,233,0.5)",
        }}>
          {eyebrow}
        </span>
        <h2 style={{
          fontFamily:"var(--font-display)", fontWeight: 500,
          letterSpacing:"-0.035em", lineHeight: 0.9,
          fontSize:"clamp(52px, 6.5vw, 96px)",
          margin: 0,
        }}>
          {title}
        </h2>
      </header>

      <div style={{ display:"flex", flexDirection:"column", gap: 32 }}>{children}</div>
    </div>
  );
}

// ─── Formatters — used in Review + when building the live model ────────
window.REG_FORMATTERS = {
  age(dob) {
    if (!dob) return "—";
    const d = new Date(dob);
    if (isNaN(d)) return "—";
    const now = new Date();
    let age = now.getFullYear() - d.getFullYear();
    const m = now.getMonth() - d.getMonth();
    if (m < 0 || (m === 0 && now.getDate() < d.getDate())) age--;
    return String(age);
  },
  height(cm) {
    if (!cm) return "—";
    const totalIn = cm / 2.54;
    const ft = Math.floor(totalIn / 12);
    const inch = Math.round(totalIn - ft * 12);
    return `${cm} cm · ${ft} ft ${inch} in`;
  },
  marks(t, p) {
    const parts = [];
    if (!t?.has) parts.push("No tattoos");
    else parts.push(t.detail ? `Tattoos: ${t.detail}` : "Tattoos");
    if (!p?.has) parts.push("no piercings");
    else parts.push(p.detail ? `piercings: ${p.detail}` : "piercings");
    return parts.join(", ");
  },
  basedIn(area, city, country) {
    if (!area && !city && !country) return "—";
    return [area, city, country].filter(Boolean).join(", ");
  },
};

// ─── REG_BUILD_MODEL — shape registration data into the profile model ──
// Mirrors profile-data.js exactly so the live preview reads identical to
// what the production template renders.
const LANG_CODE = {
  English:"EN", French:"FR", Italian:"IT", Spanish:"ES", German:"DE",
  Portuguese:"PT", Russian:"RU", Polish:"PL", Ukrainian:"UA", Czech:"CZ",
  Hungarian:"HU", Greek:"GR", Turkish:"TR", Arabic:"AR", Hebrew:"HE",
  Persian:"FA", Hindi:"HI", Urdu:"UR", Bengali:"BN", Mandarin:"ZH",
  Cantonese:"YUE", Japanese:"JP", Korean:"KO", Thai:"TH",
  Vietnamese:"VN", Indonesian:"ID", Tagalog:"TL", Swahili:"SW", Dutch:"NL",
};

window.REG_BUILD_MODEL = function(data) {
  const F = window.REG_FORMATTERS;
  const tables = window.REG_SIZE_TABLES || {};

  const lookup = (table, eu) => {
    if (!eu || !table) return { eu: "", us: "", uk: "" };
    const i = table.EU.indexOf(String(eu));
    if (i < 0) return { eu, us: "", uk: "" };
    return { eu: table.EU[i], us: table.US[i], uk: table.UK[i] };
  };

  const dress = lookup(tables.dress, data.dressSize);
  const shoe  = lookup(tables.shoe,  data.shoeSize);

  const cmH = data.heightCm ? `${data.heightCm} cm` : "";
  const ftIn = data.heightCm
    ? (() => { const t = data.heightCm / 2.54; const ft = Math.floor(t / 12); const i = Math.round(t - ft * 12); return `${ft} ft ${i} in`; })()
    : "";

  const marks = (() => {
    const t = data.tattoos, p = data.piercings;
    if (!t?.has && !p?.has) return { v: "No tattoos", sub: "No piercings" };
    const parts = [];
    if (t?.has) parts.push(t.detail || "yes");  else parts.push("no tattoos");
    if (p?.has) parts.push(p.detail || "piercings"); else parts.push("no piercings");
    return { v: t?.has ? "Tattoos" : "Piercings", sub: parts.join(" · ") };
  })();

  const langs = data.languages || [];
  const langCodes = langs.slice(0, 4)
    .map((l) => LANG_CODE[l] || l.slice(0,2).toUpperCase())
    .join(" · ");

  const stats = [];
  const ageS = F.age(data.dob);
  if (ageS !== "—")    stats.push({ k:"Age",         v: ageS, sub: data.dob ? `b. ${new Date(data.dob).getFullYear()}` : "" });
  if (cmH)             stats.push({ k:"Height",      v: cmH, sub: ftIn });
  if (data.cupSize)    stats.push({ k:"Cup size",    v: data.cupSize, sub: data.bandSize });
  if (data.breasts)    stats.push({ k:"Breasts",     v: data.breasts });
  if (dress.eu)        stats.push({ k:"Dress",       v: `EU ${dress.eu}`, sub: `US ${dress.us} · UK ${dress.uk}` });
  if (shoe.eu)         stats.push({ k:"Shoe",        v: `EU ${shoe.eu}`,  sub: `US ${shoe.us} · UK ${shoe.uk}` });
  if (data.hairColor)  stats.push({ k:"Hair",        v: data.hairColor, sub: data.hairLength });
  if (data.eyeColor)   stats.push({ k:"Eyes",        v: data.eyeColor });
  if (data.nationality) stats.push({ k:"Nationality", v: data.nationality });
  if (data.basedInArea || data.basedInCity || data.basedInCountry) {
    stats.push({ k:"Based in", v: [data.basedInArea, data.basedInCity, data.basedInCountry].filter(Boolean).join(", ") });
  }
  if (langs.length)    stats.push({ k:"Languages",   v: langCodes, sub: langs.join(", ") });
  stats.push({ k:"Marks", v: marks.v, sub: marks.sub });
  if (data.availability) stats.push({ k:"Availability", v: data.availability });
  if (data.travel)       stats.push({ k:"Travel",      v: data.travel });
  if ((data.services || []).length) stats.push({ k:"Services", v: (data.services || []).join(", ") });

  // Always lead with an image so the hero isn't a video poster.
  const raw    = data.media || [];
  const images = raw.filter((m) => m.kind !== "video");
  const videos = raw.filter((m) => m.kind === "video");
  const ordered = images.length ? [...images, ...videos] : raw;

  const media = ordered.map((m, idx) => {
    const out = {
      i: idx + 1,
      src: m.src,
      w: m.w,
      h: m.h,
      aspectRatio: m.w && m.h ? m.w / m.h : null,
      alt: `${data.alias || data.firstName || "Applicant"} portfolio image ${String(idx + 1).padStart(2, "0")}`,
    };
    if (m.kind === "video") out.kind = "video"; // poster left undefined — MediaItem handles it
    return out;
  });

  // Split the alias on the first whitespace so the hero can render its
  // two-line lockup. If the model picked a mononym ("Elena"), the second
  // line stays empty and the hero gracefully tightens.
  const aliasRaw = (data.alias || "").trim();
  const [aliasFirst, ...aliasRest] = aliasRaw.split(/\s+/);
  const aliasLast = aliasRest.join(" ");

  return {
    // Public-facing names — what every member, booker, and screenshot
    // ever sees. Real first/last live below in `private` (staff-only,
    // stripped before the profile is served to anyone other than the desk).
    firstName: aliasFirst || data.firstName || "First name",
    lastName:  aliasLast  || "",
    agency:    "ELARA",
    city:      data.basedInCity || "—",
    area:      data.basedInArea || "",
    stats,
    bio:          window.normalizeProfileText ? window.normalizeProfileText(data.bio) : (data.bio || ""),
    personality:  window.normalizeProfileText ? window.normalizeProfileText(data.personality) : (data.personality || ""),
    availability: { notice: data.availability || "", travel: data.travel || "" },
    services: data.services || [],
    prices: modelPriceRows(data),
    priceFrom: priceFrom(data),
    media,

    // Staff-only block — kept on the talent record in the DB but never
    // serialised onto a member-facing /talent/[slug] response. Used by
    // the desk when arranging an actual introduction.
    private: {
      realFirstName: data.firstName || "",
      realLastName:  data.lastName  || "",
      email:         data.email     || "",
      phone:         data.phone     || "",
    },
  };
};

Object.assign(window, {
  StepWelcome, StepBasics, StepMeasurements, StepAppearance, StepOrigins,
  StepWork, StepWords, StepPortfolio, StepReview, StepDone,
});
