/* global React, ReactDOM */
const { useState, useEffect } = React;

// Real cut-out plates from the menu (Israel bucket), the same strip as the
// club page. Decorative only.
const PLATE_BASE = "https://storage.googleapis.com/sm_rest_menu_il/opt/wo_background/";
const PLATES = [
  "wob_g_quinoa_shiitake_gyoza_9_tmp.webp",
  "wob_g_beef_9.webp",
  "wob_g_cheeseburger_9.webp",
  "wob_g_chicken_adobo_9.webp",
  "wob_dsrt_cherry_gyoza.webp",
];

// Today in Israel as YYYY-MM-DD (earliest sensible start date).
const todayIL = () => new Date().toLocaleDateString("en-CA", { timeZone: "Asia/Jerusalem" });

// ==================== i18n ====================
const TRANSLATIONS = {
  en: {
    dir: "ltr",
    nav: { brand: "San Mai Gyoza Bar", langToggle: "עברית", langLabel: "עברית" },
    hero: {
      title: "Join our team",
      subtitle: "San Mai is a small gyoza bar in Carmel Market, Tel Aviv. We're looking for warm, sharp people who care about food and hospitality.",
    },
    filters: { all: "All", kitchen: "Kitchen", service: "Service", bar: "Bar", management: "Management" },
    list: {
      title: "Open positions",
      empty: "No open positions right now. Check back soon.",
      loading: "Loading positions…",
      loadError: "We couldn't load the positions. Check your connection and try again.",
      retry: "Try again",
      fulltime: "Full-time", parttime: "Part-time", shift: "Shift work",
    },
    place: {
      title: "What we do here",
      facts: [
        "Gyoza is the whole menu, on purpose.",
        "We make the dough, the fillings and the chilli oil ourselves.",
        "Everything is cooked to order, on the plancha.",
      ],
      address: "Yom Tov 17, Carmel Market, Tel Aviv",
    },
    details: {
      back: "Back to all positions",
      apply: "Apply for this role",
      about: "About the role",
      responsibilities: "What you'll do",
      requirements: "What we're looking for",
      location: "Location", schedule: "Schedule",
    },
    apply: {
      title: "Apply for", subtitle: "A few quick details and we'll be in touch within a couple of days.",
      back: "Back to role",
      sectionPersonal: "Personal details", sectionAddress: "Address",
      sectionExperience: "Experience", sectionAvailability: "Availability", sectionEligibility: "Eligibility",
      fullName: "Full name", fullNamePh: "Yael Cohen",
      email: "Email address", emailPh: "yael@example.com",
      phone: "Phone number", phonePh: "050 123 4567",
      city: "City", cityPh: "Tel Aviv",
      street: "Street address", streetPh: "12 Rothschild Blvd, Apt 4",
      experience: "A few words about your experience",
      experiencePh: "Where you've worked, what you enjoy…",
      startDate: "When can you start?", startDatePh: "Pick a date",
      salaryLabel: "Salary", salaryValue: "50 ₪ / hour for the first two months",
      citizenship: "I have Israeli citizenship",
      english: "I confirm that I can communicate in English",
      submit: "Submit application", submitting: "Submitting…",
      submitFailed: "Could not submit. Please try again.",
      required: "Required",
      invalidEmail: "Please enter a valid email",
      invalidPhone: "Please enter a valid phone number",
      tooShort: "Please write at least a sentence or two",
      mustConfirm: "Please confirm to continue",
    },
    success: {
      title: "Application received",
      body: "Thanks. We'll review your details and get back to you within a few days. Meanwhile, come say hi at the bar.",
      another: "See other roles",
    },
    footer: {
      copy: "© 2026 San Mai Gyoza Bar. All rights reserved.",
      privacy: "Privacy", terms: "Terms", menu: "View menu",
    },
  },
  he: {
    dir: "rtl",
    nav: { brand: "סאן מאי גיוזה בר", langToggle: "English", langLabel: "English" },
    hero: {
      title: "בואו להצטרף לצוות",
      subtitle: "סאן מאי הוא גיוזה בר קטן בשוק הכרמל בתל אביב. אנחנו מחפשים אנשים חמים וחדים שאוהבים אוכל ואירוח.",
    },
    filters: { all: "הכול", kitchen: "מטבח", service: "שירות", bar: "בר", management: "ניהול" },
    list: {
      title: "משרות פתוחות",
      empty: "אין כרגע משרות פתוחות. נחזור בקרוב.",
      loading: "טוען משרות…",
      loadError: "לא הצלחנו לטעון את המשרות. בדקו את החיבור ונסו שוב.",
      retry: "נסו שוב",
      fulltime: "משרה מלאה", parttime: "משרה חלקית", shift: "משמרות",
    },
    place: {
      title: "מה עושים אצלנו",
      facts: [
        "גיוזה זה כל התפריט, בכוונה.",
        "את הבצק, המילויים ושמן הצ׳ילי אנחנו מכינים בעצמנו.",
        "הכול מתבשל לפי הזמנה, על הפלנצ׳ה.",
      ],
      address: "יום טוב 17, שוק הכרמל, תל אביב",
    },
    details: {
      back: "חזרה לכל המשרות",
      apply: "הגשת מועמדות",
      about: "על התפקיד",
      responsibilities: "מה תעשו",
      requirements: "מה אנחנו מחפשים",
      location: "מיקום", schedule: "היקף",
    },
    apply: {
      title: "הגשת מועמדות ל", subtitle: "כמה פרטים קצרים ונחזור אליכם תוך מספר ימים.",
      back: "חזרה לתפקיד",
      sectionPersonal: "פרטים אישיים", sectionAddress: "כתובת",
      sectionExperience: "ניסיון", sectionAvailability: "זמינות", sectionEligibility: "זכאות",
      fullName: "שם מלא", fullNamePh: "יעל כהן",
      email: "כתובת אימייל", emailPh: "name@example.com",
      phone: "מספר טלפון", phonePh: "050 123 4567",
      city: "עיר", cityPh: "תל אביב",
      street: "רחוב ומספר", streetPh: "רוטשילד 12, דירה 4",
      experience: "כמה מילים על הניסיון שלכם",
      experiencePh: "איפה עבדתם, מה אתם אוהבים…",
      startDate: "מתי תוכלו להתחיל?", startDatePh: "בחירת תאריך",
      salaryLabel: "שכר", salaryValue: "50 ₪ לשעה בחודשיים הראשונים",
      citizenship: "יש לי אזרחות ישראלית",
      english: "אני מאשר/ת שאני יכול/ה לתקשר באנגלית",
      submit: "שליחת מועמדות", submitting: "שולח…",
      submitFailed: "השליחה נכשלה. אנא נסו שוב.",
      required: "שדה חובה",
      invalidEmail: "נא להזין אימייל תקין",
      invalidPhone: "נא להזין מספר טלפון תקין",
      tooShort: "נא לכתוב לפחות משפט או שניים",
      mustConfirm: "יש לאשר על מנת להמשיך",
    },
    success: {
      title: "המועמדות התקבלה",
      body: "תודה! נבחן את הפרטים ונחזור אליכם תוך מספר ימים. בינתיים, בואו לומר שלום בבר.",
      another: "למשרות נוספות",
    },
    footer: {
      copy: "© 2026 סאן מאי גיוזה בר. כל הזכויות שמורות.",
      privacy: "פרטיות", terms: "תנאים", menu: "לתפריט",
    },
  },
};

// ==================== Components ====================
function Nav({ lang, setLang, t }) {
  return (
    <header className="bar">
      <a className="bar-home" href="/" aria-label={t.nav.brand}>
        <img src="/menu/logo-trimmed.png" alt={t.nav.brand} width="259" height="207" />
      </a>
      {/* The site's accessibility button is mounted in here by index.html.
          React never renders children into this span. */}
      <span id="a11y-slot" className="a11y-slot"></span>
      <button
        type="button"
        className="bar-lang"
        onClick={() => setLang(lang === "en" ? "he" : "en")}
        aria-label={t.nav.langLabel}
        lang={lang === "en" ? "he" : "en"}
      >
        {t.nav.langToggle}
      </button>
    </header>
  );
}

function Footer({ t }) {
  return (
    <footer className="footer">
      <div className="container footer-inner">
        <img src="/menu/logo-trimmed.png" alt="" width="130" height="104" loading="lazy" />
        <nav className="footer-links" aria-label="Links">
          <a href="/menu/">{t.footer.menu}</a>
          <a href="/rest/privacy_policy/">{t.footer.privacy}</a>
          <a href="/rest/terms/">{t.footer.terms}</a>
        </nav>
        <p className="footer-copy">{t.footer.copy}</p>
      </div>
    </footer>
  );
}

function Plates() {
  return (
    <div className="plates" aria-hidden="true">
      {PLATES.map((f) => (
        <img
          key={f} className="plate" src={PLATE_BASE + f} alt="" width="400" height="400" decoding="async"
          onError={(e) => { e.currentTarget.style.visibility = "hidden"; }}
        />
      ))}
    </div>
  );
}

function DeptTag({ t, dept }) {
  if (!dept) return null;
  return <span className="dept-tag">{t.filters[dept] || dept}</span>;
}

function ListView({ lang, t, positions, loading, loadError, onSelect }) {
  return (
    <div className="page">
      <section className="hero" aria-labelledby="hero-title">
        <div className="container hero-inner">
          <h1 id="hero-title">{t.hero.title}</h1>
          <p>{t.hero.subtitle}</p>
        </div>
        <Plates />
      </section>

      <section className="container positions" aria-labelledby="open-title" aria-busy={loading ? "true" : "false"}>
        <h2 id="open-title" className="section-title">{t.list.title}</h2>
        {loading ? (
          <div className="position-list" role="status" aria-label={t.list.loading}>
            {[0, 1, 2].map((i) => <div key={i} className="skeleton" aria-hidden="true"><span></span><span></span></div>)}
          </div>
        ) : loadError ? (
          <div className="state-block" role="alert">
            <i className="ph-bold ph-wifi-slash" aria-hidden="true"></i>
            <p>{t.list.loadError}</p>
            <button type="button" className="btn btn-quiet" onClick={() => window.location.reload()}>{t.list.retry}</button>
          </div>
        ) : positions.length === 0 ? (
          <div className="state-block">
            <i className="ph-bold ph-coffee" aria-hidden="true"></i>
            <p>{t.list.empty}</p>
          </div>
        ) : (
          <ul className="position-list" role="list">
            {positions.map((p) => (
              <li key={p.id}>
                <button type="button" className="position-card" onClick={() => onSelect(p.id)}>
                  <span className="position-body">
                    <DeptTag t={t} dept={p.department} />
                    <span className="position-title">{p[`title_${lang}`] || p.title_en}</span>
                    <span className="position-meta">
                      <span className="position-meta-item">{p[`location_${lang}`] || p.location_en}</span>
                      <span className="position-meta-item">{t.list[p.workMode] || p.workMode}</span>
                    </span>
                  </span>
                  <span className="position-arrow" aria-hidden="true"><i className="ph-bold ph-arrow-right flip-rtl"></i></span>
                </button>
              </li>
            ))}
          </ul>
        )}
      </section>

      <section className="container place" aria-labelledby="place-title">
        <h2 id="place-title" className="section-title">{t.place.title}</h2>
        <ul className="place-list" role="list">
          {t.place.facts.map((f, i) => <li key={i}>{f}</li>)}
        </ul>
        <p className="place-address"><i className="ph-fill ph-map-pin" aria-hidden="true"></i>{t.place.address}</p>
      </section>
    </div>
  );
}

function DetailView({ lang, t, position, onBack, onApply }) {
  useEffect(() => { window.scrollTo(0, 0); }, [position.id]);

  const responsibilities = position[`responsibilities_${lang}`] || position.responsibilities_en || [];
  const requirements = position[`requirements_${lang}`] || position.requirements_en || [];

  return (
    <div className="page detail-page">
      <div className="container">
        <button type="button" className="back-link" onClick={onBack}>
          <i className="ph-bold ph-arrow-left flip-rtl" aria-hidden="true"></i>{t.details.back}
        </button>

        <div className="detail-header">
          <DeptTag t={t} dept={position.department} />
          <h1>{position[`title_${lang}`] || position.title_en}</h1>
        </div>

        <div className="detail-grid">
          <aside className="detail-aside">
            <dl className="facts">
              <div className="aside-row">
                <dt>{t.details.location}</dt>
                <dd>{position[`location_${lang}`] || position.location_en}</dd>
              </div>
              <div className="aside-row">
                <dt>{t.details.schedule}</dt>
                <dd>{t.list[position.workMode] || position.workMode}</dd>
              </div>
              <div className="aside-row">
                <dt>{t.apply.salaryLabel}</dt>
                <dd>{position[`salary_${lang}`] || position.salary_en || t.apply.salaryValue}</dd>
              </div>
            </dl>
            <button type="button" className="btn btn-primary btn-lg btn-block aside-apply" onClick={onApply}>
              {t.details.apply}
            </button>
          </aside>

          <div className="detail-content">
            <section>
              <h2>{t.details.about}</h2>
              <p>{position[`summary_${lang}`] || position.summary_en}</p>
            </section>
            {responsibilities.length > 0 && (
              <section>
                <h2>{t.details.responsibilities}</h2>
                <ul>
                  {responsibilities.map((r, i) => <li key={i}>{r}</li>)}
                </ul>
              </section>
            )}
            {requirements.length > 0 && (
              <section>
                <h2>{t.details.requirements}</h2>
                <ul>
                  {requirements.map((r, i) => <li key={i}>{r}</li>)}
                </ul>
              </section>
            )}
          </div>
        </div>
      </div>

      {/* Phones: the apply action stays in reach at the bottom. */}
      <div className="apply-bar">
        <button type="button" className="btn btn-primary btn-lg btn-block" onClick={onApply}>{t.details.apply}</button>
      </div>
    </div>
  );
}

// Same limits as the careers_applications shape check in firestore.rules, so a
// long answer is capped while typing instead of failing on submit.
const FIELD_MAX = { fullName: 200, email: 320, phone: 40, city: 200, street: 300, experience: 5000 };

function Field({ name, label, placeholder, value, onChange, error, type = "text", textarea, full, autoComplete, inputMode, dir }) {
  const Tag = textarea ? "textarea" : "input";
  return (
    <div className={`field ${error ? "error" : ""} ${full ? "full" : ""}`} data-field={name}>
      <label htmlFor={name}>{label}</label>
      <Tag
        id={name} name={name}
        type={textarea ? undefined : type}
        placeholder={placeholder}
        value={value}
        onChange={onChange}
        rows={textarea ? 4 : undefined}
        maxLength={FIELD_MAX[name]}
        autoComplete={autoComplete}
        inputMode={inputMode}
        dir={dir}
        aria-invalid={error ? "true" : undefined}
        aria-describedby={error ? `${name}-error` : undefined}
      />
      {error && <span className="field-error" id={`${name}-error`}>{error}</span>}
    </div>
  );
}

// Native date input made to behave on iOS: no intrinsic width, and while
// empty its text hides behind our placeholder (iOS paints today's date in an
// empty date input).
function DateField({ name, label, placeholder, value, onChange, error, min }) {
  return (
    <div className={`field full ${error ? "error" : ""}`} data-field={name}>
      <label htmlFor={name}>{label}</label>
      <div className={`date-wrap ${value ? "" : "is-empty"}`}>
        <input
          id={name} name={name} type="date" value={value} onChange={onChange} min={min}
          aria-invalid={error ? "true" : undefined}
          aria-describedby={error ? `${name}-error` : undefined}
        />
        <span className="date-ph" aria-hidden="true">{placeholder}</span>
        <i className="ph-bold ph-calendar-blank date-ic" aria-hidden="true"></i>
      </div>
      {error && <span className="field-error" id={`${name}-error`}>{error}</span>}
    </div>
  );
}

function Checkbox({ name, label, checked, onChange, error }) {
  return (
    <label
      className={`checkbox ${checked ? "checked" : ""} ${error ? "error" : ""}`}
      data-field={name}
    >
      <input type="checkbox" id={name} name={name} checked={checked} onChange={onChange}
        aria-invalid={error ? "true" : undefined} aria-describedby={error ? `${name}-error` : undefined} />
      <span className="checkbox-box" aria-hidden="true"><i className="ph-bold ph-check"></i></span>
      <span className="checkbox-label">
        {label}
        {error && <span className="field-error" id={`${name}-error`}>{error}</span>}
      </span>
    </label>
  );
}

function ApplyView({ lang, t, position, onBack, onSuccess }) {
  const [form, setForm] = useState({
    fullName: "", email: "", phone: "",
    city: "", street: "",
    experience: "",
    startDate: "",
    citizenship: false, english: false,
  });
  const [errors, setErrors] = useState({});
  const [submitting, setSubmitting] = useState(false);
  const [submitError, setSubmitError] = useState("");

  useEffect(() => { window.scrollTo(0, 0); }, []);

  const set = (key) => (e) => {
    const v = e.target.type === "checkbox" ? e.target.checked : e.target.value;
    setForm(f => ({ ...f, [key]: v }));
    if (errors[key]) setErrors(er => ({ ...er, [key]: null }));
  };

  const validate = () => {
    const e = {};
    if (!form.fullName.trim()) e.fullName = t.apply.required;
    if (!form.email.trim()) e.email = t.apply.required;
    else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(form.email)) e.email = t.apply.invalidEmail;
    if (!form.phone.trim()) e.phone = t.apply.required;
    else if (!/^[\d\s+()-]{7,}$/.test(form.phone)) e.phone = t.apply.invalidPhone;
    if (!form.city.trim()) e.city = t.apply.required;
    if (!form.street.trim()) e.street = t.apply.required;
    if (!form.experience.trim()) e.experience = t.apply.required;
    else if (form.experience.trim().length < 20) e.experience = t.apply.tooShort;
    if (!form.startDate) e.startDate = t.apply.required;
    if (!form.citizenship) e.citizenship = t.apply.mustConfirm;
    if (!form.english) e.english = t.apply.mustConfirm;
    return e;
  };

  const handleSubmit = async (ev) => {
    ev.preventDefault();
    const e = validate();
    setErrors(e);
    if (Object.keys(e).length > 0) {
      const firstKey = Object.keys(e)[0];
      const el = document.querySelector(`[data-field="${firstKey}"]`);
      if (el) {
        const reduce = matchMedia("(prefers-reduced-motion: reduce)").matches;
        el.scrollIntoView({ block: "center", behavior: reduce ? "auto" : "smooth" });
        // Keyboard and screen-reader users land on the field; a date input
        // is left alone (focusing it pops the iOS picker by surprise).
        const input = document.getElementById(firstKey);
        if (input && input.type !== "date") input.focus({ preventScroll: true });
      }
      return;
    }
    setSubmitting(true);
    setSubmitError("");
    try {
      await window.careersApi.submitApplication({
        position_id: position.id,
        position_title_en: position.title_en || "",
        position_title_he: position.title_he || "",
        fullName: form.fullName.trim(),
        email: form.email.trim(),
        phone: form.phone.trim(),
        city: form.city.trim(),
        street: form.street.trim(),
        experience: form.experience.trim(),
        startDate: form.startDate,
        citizenship: !!form.citizenship,
        english: !!form.english,
        lang,
      });
      onSuccess();
    } catch (err) {
      console.error("submit failed", err);
      setSubmitError(t.apply.submitFailed);
    } finally {
      setSubmitting(false);
    }
  };

  return (
    <div className="page apply-page">
      <div className="container narrow">
        <button type="button" className="back-link" onClick={onBack}>
          <i className="ph-bold ph-arrow-left flip-rtl" aria-hidden="true"></i>{t.apply.back}
        </button>

        <header className="apply-header">
          <DeptTag t={t} dept={position.department} />
          <h1>{t.apply.title} <span>{position[`title_${lang}`] || position.title_en}</span></h1>
          <p>{t.apply.subtitle}</p>
        </header>

        <form className="form" onSubmit={handleSubmit} noValidate>
          <fieldset className="form-section">
            <legend>{t.apply.sectionPersonal}</legend>
            <div className="form-grid">
              <Field name="fullName" label={t.apply.fullName} placeholder={t.apply.fullNamePh} value={form.fullName} onChange={set("fullName")} error={errors.fullName} full autoComplete="name" />
              <Field name="email" type="email" label={t.apply.email} placeholder={t.apply.emailPh} value={form.email} onChange={set("email")} error={errors.email} autoComplete="email" inputMode="email" dir="ltr" />
              <Field name="phone" type="tel" label={t.apply.phone} placeholder={t.apply.phonePh} value={form.phone} onChange={set("phone")} error={errors.phone} autoComplete="tel" inputMode="tel" dir="ltr" />
            </div>
          </fieldset>

          <fieldset className="form-section">
            <legend>{t.apply.sectionAddress}</legend>
            <div className="form-grid">
              <Field name="city" label={t.apply.city} placeholder={t.apply.cityPh} value={form.city} onChange={set("city")} error={errors.city} autoComplete="address-level2" />
              <Field name="street" label={t.apply.street} placeholder={t.apply.streetPh} value={form.street} onChange={set("street")} error={errors.street} autoComplete="street-address" />
            </div>
          </fieldset>

          <fieldset className="form-section">
            <legend>{t.apply.sectionExperience}</legend>
            <Field name="experience" textarea label={t.apply.experience} placeholder={t.apply.experiencePh} value={form.experience} onChange={set("experience")} error={errors.experience} full />
          </fieldset>

          <fieldset className="form-section">
            <legend>{t.apply.sectionAvailability}</legend>
            <DateField name="startDate" label={t.apply.startDate} placeholder={t.apply.startDatePh} value={form.startDate} onChange={set("startDate")} error={errors.startDate} min={todayIL()} />
          </fieldset>

          <fieldset className="form-section">
            <legend>{t.apply.sectionEligibility}</legend>
            <div className="checkbox-group">
              <Checkbox name="citizenship" label={t.apply.citizenship} checked={form.citizenship} onChange={set("citizenship")} error={errors.citizenship} />
              <Checkbox name="english" label={t.apply.english} checked={form.english} onChange={set("english")} error={errors.english} />
            </div>
          </fieldset>

          {submitError && (
            <p className="form-error" role="alert">{submitError}</p>
          )}

          <div className="form-actions">
            <button type="button" className="btn btn-quiet" onClick={onBack}>{t.apply.back}</button>
            <button type="submit" className="btn btn-primary btn-lg" disabled={submitting} aria-busy={submitting ? "true" : "false"}>
              {submitting && <i className="ph-bold ph-circle-notch spin" aria-hidden="true"></i>}
              {submitting ? t.apply.submitting : t.apply.submit}
            </button>
          </div>
        </form>
      </div>
    </div>
  );
}

function SuccessView({ t, onAnother }) {
  useEffect(() => {
    window.scrollTo(0, 0);
    const h = document.getElementById("success-title");
    if (h) h.focus({ preventScroll: true });
  }, []);
  return (
    <div className="page success-page">
      <div className="container narrow">
        <section className="success" aria-labelledby="success-title">
          <img className="success-plate" src={PLATE_BASE + "wob_dsrt_cherry_gyoza.webp"} alt="" width="300" height="300" decoding="async"
            onError={(e) => { e.currentTarget.style.visibility = "hidden"; }} />
          <span className="success-check" aria-hidden="true"><i className="ph-bold ph-check"></i></span>
          <h1 id="success-title" tabIndex="-1">{t.success.title}</h1>
          <p>{t.success.body}</p>
          <div className="success-actions">
            <button type="button" className="btn btn-primary btn-lg btn-block" onClick={onAnother}>{t.success.another}</button>
            <a className="btn btn-line btn-block" href="/menu/">{t.footer.menu}</a>
          </div>
        </section>
      </div>
    </div>
  );
}

// ==================== App ====================
function App() {
  const initialLang = (() => {
    // A language in the link wins, then this page's own choice, then the one
    // picked anywhere on the site ("lang", shared with the menu and home).
    const q = new URLSearchParams(window.location.search).get("lang");
    if (q === "he" || q === "en") return q;
    try {
      const stored = localStorage.getItem("careers_lang") || localStorage.getItem("lang");
      if (stored === "he" || stored === "en") return stored;
    } catch (_) { /* storage blocked */ }
    return navigator.language?.startsWith("he") ? "he" : "en";
  })();

  const [lang, setLang] = useState(initialLang);
  const [view, setView] = useState({ name: "list" });
  const [positions, setPositions] = useState([]);
  const [loading, setLoading] = useState(true);
  const [loadError, setLoadError] = useState(false);

  const t = TRANSLATIONS[lang];

  useEffect(() => {
    document.documentElement.setAttribute("dir", t.dir);
    document.documentElement.setAttribute("lang", lang);
    document.documentElement.setAttribute("data-lang", lang);
    document.title = lang === "he" ? "דרושים | סאן מאי" : "Careers | San Mai";
    try {
      localStorage.setItem("careers_lang", lang);
      localStorage.setItem("lang", lang);
    } catch (_) { /* storage blocked */ }
    // The accessibility menu relabels itself on this event.
    window.dispatchEvent(new Event("sm:lang"));
  }, [lang]);

  useEffect(() => {
    let unsub = null;
    (async () => {
      // Wait for the firestore module to register the API on window
      while (!window.careersApi) await new Promise(r => setTimeout(r, 50));
      unsub = window.careersApi.subscribePositions((rows) => {
        setPositions(rows);
        setLoadError(false);
        setLoading(false);
      }, (err) => {
        console.error("positions subscribe failed", err);
        setLoadError(true);
        setLoading(false);
      });
    })();
    return () => { if (unsub) unsub(); };
  }, []);

  const currentPosition = view.name === "list"
    ? null
    : positions.find(p => p.id === view.id);

  // If the position vanishes (deactivated) while user is viewing it, return to list
  useEffect(() => {
    if (view.name !== "list" && view.name !== "success" && !currentPosition && !loading) {
      setView({ name: "list" });
    }
  }, [currentPosition, view.name, loading]);

  return (
    <div className="app">
      <Nav lang={lang} setLang={setLang} t={t} />
      <main id="main" className="main">
        {view.name === "list" && (
          <ListView
            lang={lang} t={t}
            positions={positions}
            loading={loading}
            loadError={loadError}
            onSelect={(id) => setView({ name: "detail", id })}
          />
        )}
        {view.name === "detail" && currentPosition && (
          <DetailView
            lang={lang} t={t} position={currentPosition}
            onBack={() => setView({ name: "list" })}
            onApply={() => setView({ name: "apply", id: currentPosition.id })}
          />
        )}
        {view.name === "apply" && currentPosition && (
          <ApplyView
            lang={lang} t={t} position={currentPosition}
            onBack={() => setView({ name: "detail", id: currentPosition.id })}
            onSuccess={() => setView({ name: "success" })}
          />
        )}
        {view.name === "success" && (
          <SuccessView t={t} onAnother={() => setView({ name: "list" })} />
        )}
      </main>
      <Footer t={t} />
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
