/* Camps.jsx — Full camp hub: 6 sites grid + multi-step register form */

function CampsPage({ setRoute }) {
  const [registerOpen, setRegisterOpen] = React.useState(false);
  const [selectedCamp, setSelectedCamp] = React.useState(null);
  const [sites, setSites] = React.useState(window.CAMP_SITES || []);

  React.useEffect(() => {
    let cancelled = false;
    fetch("/api/camps")
      .then(r => r.ok ? r.json() : null)
      .then(j => {
        if (cancelled) return;
        if (j && j.ok && Array.isArray(j.rows) && j.rows.length > 0) {
          setSites(j.rows.map(r => ({
            id: r.slug,
            name: r.name,
            region: r.region || "",
            type: r.type,
            ageRange: r.age_range,
            address: r.address,
            coords: (r.lat != null && r.lng != null) ? [Number(r.lat), Number(r.lng)] : null,
            color: r.type === "YPTC" ? "var(--c-plum)" : "var(--c-primary)",
            startDate: r.start_date,
            endDate: r.end_date,
            hours: r.hours,
            feeCents: r.fee_cents
          })));
        }
      })
      .catch(() => {});
    return () => { cancelled = true; };
  }, []);

  // Expose to nested components that still read window.CAMP_SITES (RegisterModal radio list).
  React.useEffect(() => { window.CAMP_SITES = sites; }, [sites]);

  const openRegister = (camp) => {
    setSelectedCamp(camp);
    setRegisterOpen(true);
  };

  return (
    <div>
      <CampsHero />
      <CampProcess />
      <CampGrid sites={sites} onRegister={openRegister} />
      <CampFAQ />
      {registerOpen && (
        <RegisterModal
          camp={selectedCamp}
          onClose={() => setRegisterOpen(false)}
        />
      )}
    </div>
  );
}

function CampsHero() {
  const scrollTo = (id) => (e) => {
    e.preventDefault();
    const el = document.getElementById(id);
    if (el) el.scrollIntoView({ behavior: "smooth", block: "start" });
  };
  return (
    <section style={{ background: "var(--c-primary)", color: "var(--c-paper)", borderBottom: "2px solid var(--c-ink)", padding: "80px 0 64px" }}>
      <div className="container">
        <div className="eyebrow" style={{ color: "var(--c-paper)", opacity: 0.8 }}>Summer Camp 2026</div>
        <h1 className="display" style={{ fontSize: "clamp(48px, 9vw, 132px)", color: "var(--c-paper)", margin: "12px 0 24px", maxWidth: 1100 }}>
          Pre-register for<br />2026 summer camp.
        </h1>
        <p style={{ fontSize: 19, maxWidth: 640, lineHeight: 1.5, opacity: 0.95 }}>
          Two camps, six locations across Philadelphia. <strong>Intro to Performing Arts</strong> for ages 6–11,
          and <strong>Young Performers Theatre Camp (YPTC)</strong> for ages 12–18. All campers must pre-register
          here before submitting a paper registration.
        </p>
        <div style={{ display: "flex", gap: 14, marginTop: 32, flexWrap: "wrap" }}>
          <a href="#sites" onClick={scrollTo("sites")} className="btn ochre">Browse 6 sites ↓</a>
          <a href="#process" onClick={scrollTo("process")} className="btn secondary">How it works</a>
        </div>
      </div>
    </section>
  );
}

function CampProcess() {
  const steps = [
    { n: "01", title: "Pre-register here", body: "Pick your site, fill the form, and submit. Takes about 4 minutes. All campers must pre-register on this site before submitting paper registration." },
    { n: "02", title: "We'll reach out", body: "A coordinator confirms eligibility and dates, and sends the paper packet plus the list of what to bring (birth certificate, letter of recommendation for new campers). Usually within a week." },
    { n: "03", title: "Attend Registration Night", body: "Visit your desired camp location during Registration Night to make a deposit and fill out the registration form in person." },
    { n: "04", title: "First day of camp", body: "Arrive at 9am ready to sing, dance, and act. Day one is orientation, warm-ups, and meeting your ensemble. Lunch and snack are provided." },
  ];
  return (
    <section id="process" className="section" style={{ background: "var(--c-cream)" }}>
      <div className="container">
        <SectionHeader eyebrow="The Camp Process" title="From sign-up to showtime." subtitle="Here's exactly what happens after you hit submit, and what to expect on day one." />
        <div style={{ display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 16 }} className="process-grid">
          {steps.map((s) => (
            <div key={s.n} className="card" style={{ padding: 24 }}>
              <div className="display" style={{ fontSize: 56, color: "var(--c-primary)", lineHeight: 1, marginBottom: 12 }}>{s.n}</div>
              <div className="display" style={{ fontSize: 22, marginBottom: 8 }}>{s.title}</div>
              <p style={{ fontSize: 14.5, color: "var(--c-ink-soft)", lineHeight: 1.55 }}>{s.body}</p>
            </div>
          ))}
        </div>
        <style>{`
          @media (max-width: 980px) { .process-grid { grid-template-columns: 1fr 1fr !important; } }
          @media (max-width: 540px) { .process-grid { grid-template-columns: 1fr !important; } }
        `}</style>
      </div>
    </section>
  );
}

function CampGrid({ sites, onRegister }) {
  const [filter, setFilter] = React.useState("all");
  const list = sites || window.CAMP_SITES || [];
  const filtered = filter === "all" ? list : list.filter(c => c.type === filter);

  return (
    <section id="sites" className="section" style={{ background: "var(--c-paper)", borderTop: "2px solid var(--c-ink)", borderBottom: "2px solid var(--c-ink)" }}>
      <div className="container">
        <SectionHeader
          eyebrow="All 6 Sites"
          title="Pick your neighborhood."
          right={
            <div style={{ display: "flex", gap: 8 }}>
              {["all", "Intro", "YPTC"].map(f => (
                <button
                  key={f}
                  onClick={() => setFilter(f)}
                  className="btn small"
                  style={{
                    background: filter === f ? "var(--c-ink)" : "var(--c-paper)",
                    color: filter === f ? "var(--c-paper)" : "var(--c-ink)",
                    boxShadow: filter === f ? "2px 2px 0 var(--c-primary)" : "2px 2px 0 var(--c-ink)",
                  }}
                >
                  {f === "all" ? "All sites" : f === "Intro" ? "Intro to Performing Arts (6–11)" : "Young Performers Theatre Camp (12–18)"}
                </button>
              ))}
            </div>
          }
        />
        <div style={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: 20 }} className="sites-grid">
          {filtered.map((camp, idx) => (
            <CampCardFull key={camp.id} camp={camp} index={idx} onRegister={() => onRegister(camp)} />
          ))}
        </div>
        <style>{`
          @media (max-width: 980px) { .sites-grid { grid-template-columns: 1fr 1fr !important; } }
          @media (max-width: 640px) { .sites-grid { grid-template-columns: 1fr !important; } }
        `}</style>
      </div>
    </section>
  );
}

function CampCardFull({ camp, index, onRegister }) {
  const tints = ["#F5C6B0", "#F2D88F", "#C9D9B0", "#E8C9D2", "#C8C0E0", "#F8D4B8"];
  const cms = window.CMS || { get: (_k, fb) => fb };
  const dates = camp.type === "Intro"
    ? cms.get("camps.intro_dates_label", "July 6 – August 14, 2026")
    : cms.get("camps.yptc_dates_label", "June 30 – August 8, 2026");
  const hours = camp.type === "Intro"
    ? cms.get("camps.intro_hours", "Mon–Fri, 9am – 2pm")
    : cms.get("camps.yptc_hours", "Mon–Fri, 9am – 2pm");
  const fee = cms.get("camps.fee_intro_dollars", 300);
  return (
    <div className="card" style={{ padding: 0, overflow: "hidden", display: "flex", flexDirection: "column" }}>
      <div
        style={{
          background: tints[index % tints.length],
          padding: "24px 24px 16px",
          borderBottom: "2px solid var(--c-ink)",
          position: "relative",
        }}
      >
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", gap: 12 }}>
          <div className="display" style={{ fontSize: 22, lineHeight: 1.05, flex: 1 }}>{camp.name}</div>
          <div className="display" style={{ fontSize: 36, color: "var(--c-ink-soft)", opacity: 0.4 }}>0{index + 1}</div>
        </div>
        <div style={{ display: "flex", gap: 6, marginTop: 12, flexWrap: "wrap" }}>
          <span className={`chip ${camp.type === "Intro" ? "intro" : "yptc"}`} style={{ background: "var(--c-paper)" }}>
            {camp.type === "Intro" ? "Intro to Performing Arts" : "Young Performers Theatre Camp"}
          </span>
          <span className="chip" style={{ background: "var(--c-paper)" }}>{camp.ageRange}</span>
        </div>
      </div>
      <div style={{ padding: 22, display: "flex", flexDirection: "column", gap: 14, flex: 1 }}>
        <InfoRow label="Region" value={camp.region} />
        <InfoRow label="Address" value={camp.address} />
        <InfoRow label="Dates" value={dates} />
        <InfoRow label="Hours" value={hours} />
        <InfoRow label="Fee" value={`$${fee} for the full session`} />
        {camp.type === "YPTC" && (
          <div style={{ fontSize: 12, color: "var(--c-ink-soft)", fontStyle: "italic", lineHeight: 1.4, marginTop: -6 }}>
            An audition is required to be accepted into this camp.
          </div>
        )}
        <button onClick={onRegister} className="btn small" style={{ marginTop: "auto", width: "100%", justifyContent: "center" }}>
          Pre-register at this site →
        </button>
      </div>
    </div>
  );
}

function InfoRow({ label, value }) {
  return (
    <div style={{ display: "flex", gap: 12, fontSize: 13.5 }}>
      <div style={{
        fontFamily: "var(--f-mono)",
        fontSize: 10.5,
        letterSpacing: "0.12em",
        textTransform: "uppercase",
        color: "var(--c-ink-soft)",
        width: 70,
        flexShrink: 0,
        paddingTop: 2,
      }}>{label}</div>
      <div style={{ color: "var(--c-ink)", flex: 1 }}>{value}</div>
    </div>
  );
}

function CampFAQ() {
  const faqs = [
    { q: "What does it cost?", a: "$300 for the full Intro to Performing Arts session (six weeks). Optional aftercare until 5pm is $30/week or $150 for the whole summer (one free week if prepaid). YPTC is also $300 for the five-week session. Lunch and snack are provided daily." },
    { q: "What does my child need to bring?", a: "A water bottle, comfortable clothes they can move in, and a copy of their birth certificate. New campers also need a letter of recommendation from a teacher, neighbor, mentor, or relative who believes a performing arts camp is a good fit. We supply scripts for the final showcase." },
    { q: "What does a typical day look like?", a: "Camp runs daily from 9am to 2pm. Campers take classes in movement, drama, and music, with field trips related to the performing arts woven in. Lunch and snack are provided." },
    { q: "What happens at the end of camp?", a: "Intro campers perform at the Dell Music Center during the final week — a chance to perform in a professional space. YPTC produces a full live show at Venice Island Performing Arts Center — backed by a live band — with two evening performances on August 6 and 7, 2026." },
    { q: "Does YPTC require an audition?", a: "First-time YPTC campers audition: sing a cappella, read or act out a short text of their choosing, and submit a birth certificate copy plus a letter of recommendation from a non-relative teacher, neighbor, or mentor. Auditions can be done in person or via Zoom. Returning campers do not audition." },
    { q: "Is bus transportation provided?", a: "No — families arrange their own transportation to and from camp. (We're working on this for future summers.)" },
    { q: "I have a returning camper — anything different this year?", a: "Most staff are returning. Bill Powell has retired; your new Performing Arts Coordinator is J.J. Pospiech (Joseph.J.Pospiech@phila.gov, 215-685-3585)." },
  ];
  const [open, setOpen] = React.useState(0);
  return (
    <section className="section">
      <div className="container">
        <SectionHeader eyebrow="Questions, answered" title="The basics." />
        <div style={{ maxWidth: 880 }}>
          {faqs.map((f, i) => (
            <div
              key={i}
              style={{
                borderBottom: "2px solid var(--c-ink)",
                padding: "20px 0",
              }}
            >
              <button
                onClick={() => setOpen(open === i ? -1 : i)}
                style={{
                  width: "100%",
                  background: "none",
                  border: "none",
                  textAlign: "left",
                  display: "flex",
                  justifyContent: "space-between",
                  alignItems: "center",
                  gap: 16,
                  padding: 0,
                }}
              >
                <span className="display" style={{ fontSize: 24 }}>{f.q}</span>
                <span style={{
                  fontFamily: "var(--f-display)",
                  fontSize: 32,
                  lineHeight: 1,
                  color: "var(--c-primary)",
                  transform: open === i ? "rotate(45deg)" : "none",
                  transition: "transform 200ms",
                }}>+</span>
              </button>
              {open === i && (
                <p style={{ fontSize: 16, color: "var(--c-ink-soft)", marginTop: 12, lineHeight: 1.55, maxWidth: 720 }}>{f.a}</p>
              )}
            </div>
          ))}
        </div>
      </div>
    </section>
  );
}

const SHIRT_SIZES = ["Child's small","Child's medium","Child's large","Adult small","Adult medium","Adult large","Adult XL","Other"];

function RegisterModal({ camp, onClose }) {
  const [step, setStep] = React.useState(1);
  const [data, setData] = React.useState({
    site_id: camp ? camp.id : "",
    // Participant -- pre-registration only collects basics; address, school,
    // demographics, medical, and emergency contacts are filled in on the
    // City paper form at the in-person registration night.
    child_name: "", child_dob: "", child_age: "", shirt_size: "",
    // Submitter (parent/guardian who's filling out the form)
    parent_name: "", parent_email: "", parent_phone: "",
    returning: false, hear_about: "",
    // Staff alerts
    staff_alerts: ""
  });
  const [submitting, setSubmitting] = React.useState(false);
  const [submitError, setSubmitError] = React.useState("");
  const [submittedRef, setSubmittedRef] = React.useState(null);
  const update = (k, v) => setData(d => ({ ...d, [k]: v }));

  const totalSteps = 5;

  React.useEffect(() => {
    document.body.style.overflow = "hidden";
    return () => { document.body.style.overflow = ""; };
  }, []);

  // Derive age from DOB if both DOB is set and age isn't.
  React.useEffect(() => {
    if (data.child_dob && !data.child_age) {
      const d = new Date(data.child_dob);
      if (!isNaN(d)) {
        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--;
        if (age >= 0 && age < 30) update("child_age", age);
      }
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [data.child_dob]);

  const canContinue = () => {
    if (step === 1) return !!data.site_id;
    if (step === 2) return !!data.child_name;
    if (step === 3) return data.parent_name && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data.parent_email) && data.parent_phone;
    if (step === 4) return true;
    return true;
  };

  const submit = async () => {
    setSubmitting(true);
    setSubmitError("");
    try {
      const res = await fetch("/api/register-camp", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(data)
      });
      const json = await res.json().catch(() => ({}));
      if (!res.ok || !json.ok) throw new Error(json.error || `error_${res.status}`);
      setSubmittedRef(json.ref || "REG");
    } catch (e) {
      setSubmitError(`Couldn't submit (${e.message || e}). Try again or email Joseph.J.Pospiech@phila.gov.`);
    } finally {
      setSubmitting(false);
    }
  };

  return (
    <div onClick={onClose} style={{
      position: "fixed", inset: 0, background: "rgba(42,24,32,0.7)", zIndex: 100,
      display: "flex", alignItems: "center", justifyContent: "center", padding: 20, overflowY: "auto",
    }}>
      <div onClick={(e) => e.stopPropagation()} className="card"
           style={{ maxWidth: 820, width: "100%", padding: 0, background: "var(--c-paper)", maxHeight: "92vh", display: "flex", flexDirection: "column" }}>
        <div style={{ padding: "24px 28px", borderBottom: "2px solid var(--c-ink)", display: "flex", justifyContent: "space-between", alignItems: "center", background: "var(--c-cream-deep)" }}>
          <div>
            <div className="eyebrow">Pre-register · Step {step} of {totalSteps}</div>
            <div className="display" style={{ fontSize: 24, marginTop: 4 }}>
              {camp ? camp.name : "Summer Camp 2026"}
            </div>
          </div>
          <button onClick={onClose} aria-label="Close" style={{
            background: "var(--c-paper)", border: "2px solid var(--c-ink)", borderRadius: 999,
            width: 36, height: 36, fontSize: 18, fontWeight: 800, cursor: "pointer",
          }}>×</button>
        </div>

        {/* Progress */}
        <div style={{ padding: "16px 28px", display: "flex", gap: 6, borderBottom: "1px solid var(--c-line)" }}>
          {Array.from({ length: totalSteps }).map((_, i) => (
            <div key={i} style={{
              flex: 1, height: 6, borderRadius: 999,
              background: i < step ? "var(--c-primary)" : "var(--c-cream-deep)",
              border: "1.5px solid var(--c-ink)",
            }} />
          ))}
        </div>

        {/* Body */}
        <div style={{ padding: 28, overflowY: "auto", flex: 1 }}>
          {step === 1 && (
            <FormStep title="Pick your site" subtitle="Each location runs a different track. You can change later by emailing the coordinator.">
              <div style={{ display: "grid", gap: 10 }}>
                {(window.CAMP_SITES || []).map((c) => (
                  <label key={c.id} style={{
                    display: "flex", gap: 14, alignItems: "center", padding: 14,
                    border: "2px solid var(--c-ink)", borderRadius: 12,
                    background: data.site_id === c.id ? "var(--c-cream-deep)" : "var(--c-paper)",
                    cursor: "pointer",
                  }}>
                    <input type="radio" name="site" checked={data.site_id === c.id} onChange={() => update("site_id", c.id)} />
                    <div style={{ flex: 1 }}>
                      <div style={{ fontFamily: "var(--f-display)", fontWeight: 700, fontSize: 16 }}>{c.name}</div>
                      <div style={{ fontSize: 13, color: "var(--c-ink-soft)" }}>{c.region} · {c.ageRange} · {c.type}</div>
                    </div>
                  </label>
                ))}
              </div>
            </FormStep>
          )}

          {step === 2 && (
            <FormStep title="About your camper" subtitle="Just the basics for pre-registration. The rest of the City's paper participant form is collected at the in-person registration night.">
              <div className="fg-name-dob-age">
                <Field label="First and last name" value={data.child_name} onChange={v => update("child_name", v)} required />
                <Field label="Date of birth" type="date" value={data.child_dob} onChange={v => update("child_dob", v)} />
                <Field label="Age" type="number" value={data.child_age} onChange={v => update("child_age", v)} />
              </div>
              <div className="fg-2">
                <Field label="Shirt size" type="select" options={["", ...SHIRT_SIZES]} value={data.shirt_size} onChange={v => update("shirt_size", v)} />
                <Field label="Returning camper?" type="select" options={["No", "Yes"]} value={data.returning ? "Yes" : "No"} onChange={v => update("returning", v === "Yes")} />
              </div>
            </FormStep>
          )}

          {step === 3 && (
            <FormStep title="Parent & Guardian Information" subtitle="Submitting parent or guardian only. Emergency contacts are collected at the in-person registration night.">
              <div className="fg-2">
                <Field label="Submitting parent / guardian name" value={data.parent_name} onChange={v => update("parent_name", v)} required />
                <Field label="Submitter email" type="email" value={data.parent_email} onChange={v => update("parent_email", v)} required />
                <Field label="Submitter phone" type="tel" value={data.parent_phone} onChange={v => update("parent_phone", v)} required />
                <Field label="How did you hear about us?" type="select" options={["", "Word of mouth", "Rec center", "School", "Social media", "Returning family", "Other"]} value={data.hear_about} onChange={v => update("hear_about", v)} />
              </div>
            </FormStep>
          )}

          {step === 4 && (
            <FormStep title="Staff alerts" subtitle="Behavioral concerns, dietary restrictions, or anything else our staff should know. Medical details are collected on the City paper form at the in-person registration night.">
              <Field label="Notes for staff" type="textarea" value={data.staff_alerts} onChange={v => update("staff_alerts", v)} />
            </FormStep>
          )}

          {step === 5 && !submittedRef && (
            <FormStep title="Review & submit">
              <div className="card" style={{ background: "var(--c-cream)", boxShadow: "none", padding: 20 }}>
                <ReviewLine label="Site" value={(window.CAMP_SITES || []).find(c => c.id === data.site_id)?.name || data.site_id || "—"} />
                <ReviewLine label="Camper" value={`${data.child_name}${data.child_dob ? ` (DOB ${data.child_dob})` : (data.child_age ? ` (age ${data.child_age})` : "")}`} />
                <ReviewLine label="Shirt" value={`${data.shirt_size || "—"}${data.returning ? " · returning" : ""}`} />
                <ReviewLine label="Submitter" value={`${data.parent_name} · ${data.parent_email} · ${data.parent_phone}`} />
                <ReviewLine label="Heard via" value={data.hear_about || "—"} />
                {data.staff_alerts && <ReviewLine label="Notes" value={data.staff_alerts} />}
              </div>

              <div style={{ marginTop: 18, padding: 16, background: "var(--c-cream-deep)", border: "2px solid var(--c-ink)", borderRadius: 12 }}>
                <div className="eyebrow" style={{ marginBottom: 8 }}>What happens next</div>
                <p style={{ fontSize: 14, lineHeight: 1.55, color: "var(--c-ink)", margin: 0 }}>
                  A coordinator will reach out within a week to let you know the next steps for registration
                  and when to attend a registration night at your desired location (based on availability).
                </p>
              </div>

              {submitError && (
                <div style={{ marginTop: 14, padding: 12, background: "#FBE4DC", border: "2px solid var(--c-primary)", borderRadius: 12, fontSize: 13.5 }}>{submitError}</div>
              )}
            </FormStep>
          )}

          {submittedRef && (
            <FormStep title="Pre-registered. ✦">
              <p style={{ fontSize: 16, lineHeight: 1.55 }}>
                We got it. Your reference number is <strong style={{ fontFamily: "var(--f-mono)" }}>{submittedRef}</strong>.
                A coordinator will reach out within a week with the next steps.
                A confirmation email is on its way to <strong>{data.parent_email}</strong>.
              </p>
            </FormStep>
          )}
        </div>

        {/* Footer */}
        <div style={{ padding: "20px 28px", borderTop: "2px solid var(--c-ink)", display: "flex", justifyContent: "space-between", gap: 12, background: "var(--c-cream-deep)" }}>
          {!submittedRef ? (
            <>
              <button onClick={() => step === 1 ? onClose() : setStep(step - 1)} className="btn secondary small" disabled={submitting}>
                {step === 1 ? "Cancel" : "← Back"}
              </button>
              {step < totalSteps ? (
                <button onClick={() => setStep(step + 1)} className="btn small" disabled={!canContinue()}>
                  Continue →
                </button>
              ) : (
                <button onClick={submit} className="btn small" disabled={submitting}>
                  {submitting ? "Submitting…" : "Submit pre-registration ✓"}
                </button>
              )}
            </>
          ) : (
            <button onClick={onClose} className="btn small" style={{ marginLeft: "auto" }}>Close</button>
          )}
        </div>
      </div>
    </div>
  );
}

function CheckboxRow({ label, value, onChange }) {
  return (
    <label style={{ display: "inline-flex", alignItems: "center", gap: 8, fontSize: 13.5, cursor: "pointer" }}>
      <input type="checkbox" checked={!!value} onChange={e => onChange(e.target.checked)} />
      <span>{label}</span>
    </label>
  );
}


function FormStep({ title, subtitle, children }) {
  return (
    <div>
      <div className="display" style={{ fontSize: 28, marginBottom: subtitle ? 6 : 18, lineHeight: 1.1 }}>{title}</div>
      {subtitle && <p style={{ fontSize: 14, color: "var(--c-ink-soft)", marginBottom: 18 }}>{subtitle}</p>}
      <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>{children}</div>
    </div>
  );
}

function Field({ label, value, onChange, type = "text", options, optionLabels, required, placeholder, pattern, inputMode, maxLength, formatter, title, min, max }) {
  const handleInput = (raw) => onChange(formatter ? formatter(raw) : raw);
  return (
    <div className="field">
      <label>{label}{required && <span style={{ color: "var(--c-primary)" }}> *</span>}</label>
      {type === "textarea" ? (
        <textarea value={value || ""} onChange={e => handleInput(e.target.value)} rows={3} />
      ) : type === "select" ? (
        <select value={value || ""} onChange={e => onChange(e.target.value)}>
          {(options || []).map((o, i) => <option key={String(o) + i} value={o}>{(optionLabels && optionLabels[i]) || (o || "—")}</option>)}
        </select>
      ) : (
        <input
          type={type}
          value={value == null ? "" : value}
          onChange={e => handleInput(e.target.value)}
          required={required}
          placeholder={placeholder}
          pattern={pattern}
          inputMode={inputMode}
          maxLength={maxLength}
          title={title}
          min={min}
          max={max}
        />
      )}
    </div>
  );
}

/* CR #219: shared input mask helpers. Each formatter strips non-digits and
   re-inserts the canonical separator so the value always conforms to its
   pattern. Pair with HTML5 `pattern` + `required` for hard validation on
   submit (the browser blocks submit when the field's value doesn't match). */
function formatPhone(raw) {
  const d = String(raw || "").replace(/\D/g, "").slice(0, 10);
  if (d.length <= 3) return d;
  if (d.length <= 6) return `${d.slice(0, 3)}-${d.slice(3)}`;
  return `${d.slice(0, 3)}-${d.slice(3, 6)}-${d.slice(6)}`;
}
function formatDateMDY(raw) {
  const d = String(raw || "").replace(/\D/g, "").slice(0, 8);
  if (d.length <= 2) return d;
  if (d.length <= 4) return `${d.slice(0, 2)}/${d.slice(2)}`;
  return `${d.slice(0, 2)}/${d.slice(2, 4)}/${d.slice(4)}`;
}
function formatMonthYear(raw) {
  const d = String(raw || "").replace(/\D/g, "").slice(0, 4);
  if (d.length <= 2) return d;
  return `${d.slice(0, 2)}/${d.slice(2)}`;
}
function formatSSN(raw) {
  const d = String(raw || "").replace(/\D/g, "").slice(0, 9);
  if (d.length <= 3) return d;
  if (d.length <= 5) return `${d.slice(0, 3)}-${d.slice(3)}`;
  return `${d.slice(0, 3)}-${d.slice(3, 5)}-${d.slice(5)}`;
}

function ReviewLine({ label, value }) {
  return (
    <div style={{ display: "flex", gap: 16, padding: "8px 0", borderBottom: "1px dashed var(--c-line)", fontSize: 14 }}>
      <div style={{
        fontFamily: "var(--f-mono)", fontSize: 11, letterSpacing: "0.1em",
        textTransform: "uppercase", color: "var(--c-ink-soft)", width: 110, flexShrink: 0,
      }}>{label}</div>
      <div>{value || "—"}</div>
    </div>
  );
}

Object.assign(window, { CampsPage, RegisterModal, Field, FormStep, CheckboxRow, formatPhone, formatDateMDY, formatMonthYear, formatSSN });
