// shared.jsx — i18n, booking wizard, language toggle, glitch hero
// Each variation imports these via window.*

// ─────────────────────────────────────────────────────────────
// i18n — ES/EN dictionary, loaded from inline JSON blocks in index.html.
// Source of truth: i18n/en.json (edit this). Run `npm run translate` to
// regenerate i18n/es.json + the inline JSON blocks via DeepL.
// ─────────────────────────────────────────────────────────────
function loadDict(id) {
  const el = document.getElementById(id);
  if (!el) {
    console.warn("[i18n] missing block:", id);
    return {};
  }
  try { return JSON.parse(el.textContent); }
  catch (e) { console.error("[i18n] parse error for", id, e); return {}; }
}
const DICT = {
  en: loadDict("i18n-en"),
  es: loadDict("i18n-es"),
};

// Detect browser language, default to Spanish if neither.
function detectLang() {
  try {
    const stored = localStorage.getItem("andrei_lang");
    if (stored === "es" || stored === "en") return stored;
  } catch {}
  const nav = (navigator.language || "en").toLowerCase();
  return nav.startsWith("es") ? "es" : "en";
}

function useLang() {
  const [lang, setLangState] = React.useState(() => detectLang());
  const setLang = React.useCallback((next) => {
    setLangState(next);
    try { localStorage.setItem("andrei_lang", next); } catch {}
  }, []);
  return [lang, setLang, DICT[lang]];
}

// ─────────────────────────────────────────────────────────────
// LangToggle — pill with EN | ES
// ─────────────────────────────────────────────────────────────
function LangToggle({ lang, setLang, accent = "#3a8dff", inverse = false }) {
  const base = {
    display: "inline-flex",
    alignItems: "center",
    gap: 0,
    fontFamily: "'JetBrains Mono', ui-monospace, monospace",
    fontSize: 11,
    letterSpacing: 0.4,
    padding: 3,
    borderRadius: 999,
    border: `1px solid ${inverse ? "rgba(0,0,0,.15)" : "rgba(255,255,255,.18)"}`,
    background: inverse ? "rgba(0,0,0,.04)" : "rgba(255,255,255,.04)",
    backdropFilter: "blur(8px)",
  };
  const btn = (active) => ({
    border: 0,
    cursor: "pointer",
    padding: "5px 11px",
    borderRadius: 999,
    fontFamily: "inherit",
    fontSize: 11,
    fontWeight: 600,
    letterSpacing: 0.6,
    background: active ? accent : "transparent",
    color: active ? "#0a0a0a" : (inverse ? "#0a0a0a" : "rgba(255,255,255,.7)"),
    transition: "background .15s, color .15s",
  });
  return (
    <div style={base}>
      <button style={btn(lang === "en")} onClick={() => setLang("en")}>EN</button>
      <button style={btn(lang === "es")} onClick={() => setLang("es")}>ES</button>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// GlitchHero — animated noise/glitch over a placeholder photo
// SVG turbulence + RGB channel split + scanlines + random clip slices
// ─────────────────────────────────────────────────────────────
function GlitchHero({ accent = "#3a8dff", children, photo = "portrait" }) {
  const [tick, setTick] = React.useState(0);
  React.useEffect(() => {
    let raf;
    const loop = () => { setTick((t) => t + 1); raf = requestAnimationFrame(loop); };
    raf = requestAnimationFrame(loop);
    return () => cancelAnimationFrame(raf);
  }, []);

  // Slow random slice offsets — re-roll every ~700ms
  const [slices, setSlices] = React.useState([]);
  React.useEffect(() => {
    const reroll = () => {
      const n = 4 + Math.floor(Math.random() * 3);
      setSlices(
        Array.from({ length: n }, () => ({
          top: Math.random() * 100,
          h: 1 + Math.random() * 8,
          dx: (Math.random() - 0.5) * 24,
        }))
      );
    };
    reroll();
    const iv = setInterval(reroll, 700 + Math.random() * 800);
    return () => clearInterval(iv);
  }, []);

  return (
    <div style={{ position: "relative", width: "100%", height: "100%", overflow: "hidden", background: "#0a0a0a" }}>
      {/* placeholder photo — striped, labelled */}
      <div
        style={{
          position: "absolute",
          inset: 0,
          background:
            "repeating-linear-gradient(135deg, #161616 0 14px, #1c1c1c 14px 28px)",
          display: "flex",
          alignItems: "center",
          justifyContent: "center",
          color: "rgba(255,255,255,.18)",
          fontFamily: "'JetBrains Mono', ui-monospace, monospace",
          fontSize: 11,
          letterSpacing: 2,
        }}
      >
        [ {photo.toUpperCase()} PHOTO ]
      </div>

      {/* RGB channel split — three offset copies of the photo placeholder */}
      <div
        style={{
          position: "absolute",
          inset: 0,
          background:
            "repeating-linear-gradient(135deg, rgba(255,0,80,.15) 0 14px, rgba(255,0,80,.05) 14px 28px)",
          mixBlendMode: "screen",
          transform: `translate(${Math.sin(tick / 30) * 3 - 2}px, 0)`,
        }}
      />
      <div
        style={{
          position: "absolute",
          inset: 0,
          background:
            "repeating-linear-gradient(135deg, rgba(0,180,255,.12) 0 14px, transparent 14px 28px)",
          mixBlendMode: "screen",
          transform: `translate(${Math.cos(tick / 25) * 3 + 2}px, 0)`,
        }}
      />

      {/* glitch slices */}
      {slices.map((s, i) => (
        <div
          key={i}
          style={{
            position: "absolute",
            left: 0,
            right: 0,
            top: `${s.top}%`,
            height: `${s.h}%`,
            background: "rgba(255,255,255,.04)",
            transform: `translateX(${s.dx}px)`,
            mixBlendMode: "overlay",
          }}
        />
      ))}

      {/* SVG turbulence noise overlay */}
      <svg
        style={{
          position: "absolute",
          inset: 0,
          width: "100%",
          height: "100%",
          opacity: 0.18,
          mixBlendMode: "overlay",
          pointerEvents: "none",
        }}
        aria-hidden="true"
      >
        <filter id="hero-noise">
          <feTurbulence type="fractalNoise" baseFrequency="0.9" numOctaves="2" stitchTiles="stitch" seed={tick % 11} />
          <feColorMatrix values="0 0 0 0 1  0 0 0 0 1  0 0 0 0 1  0 0 0 0.55 0" />
        </filter>
        <rect width="100%" height="100%" filter="url(#hero-noise)" />
      </svg>

      {/* scanlines */}
      <div
        style={{
          position: "absolute",
          inset: 0,
          background:
            "repeating-linear-gradient(0deg, rgba(0,0,0,.18) 0 1px, transparent 1px 3px)",
          pointerEvents: "none",
        }}
      />

      {/* vignette */}
      <div
        style={{
          position: "absolute",
          inset: 0,
          background:
            "radial-gradient(ellipse at center, transparent 30%, rgba(0,0,0,.65) 100%)",
          pointerEvents: "none",
        }}
      />

      {/* accent flash bar — rare */}
      {tick % 240 < 4 && (
        <div
          style={{
            position: "absolute",
            left: 0,
            right: 0,
            top: `${(tick * 7) % 100}%`,
            height: 2,
            background: accent,
            opacity: 0.5,
            pointerEvents: "none",
          }}
        />
      )}

      {/* content overlay */}
      <div style={{ position: "relative", zIndex: 2, width: "100%", height: "100%" }}>{children}</div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// BookingWizard — 4-step (service → date → details → review)
// ─────────────────────────────────────────────────────────────

// Validation helpers
const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/;

function isoToday() {
  const d = new Date();
  return new Date(d.getFullYear(), d.getMonth(), d.getDate());
}
function isoMinDate() {
  // Allow today selection in the picker; validation enforces 48h lead time.
  return isoToday().toISOString().slice(0, 10);
}
function isoMaxDate() {
  const d = isoToday();
  d.setFullYear(d.getFullYear() + 2);
  return d.toISOString().slice(0, 10);
}

function validateBooking(data, t) {
  const e = {};
  const E = t.book.errors;

  if (!data.service) e.service = E.service;

  if (!data.date) {
    e.date = E.date_required;
  } else {
    const picked = new Date(data.date + "T00:00:00");
    const today = isoToday();
    const diffDays = Math.floor((picked - today) / 86400000);
    if (diffDays < 0) e.date = E.date_past;
    else if (diffDays < 2) e.date = E.date_too_soon;
    else if (diffDays > 365 * 2) e.date = E.date_too_far;
  }

  const name = (data.name || "").trim();
  if (!name) e.name = E.name_required;
  else if (name.length < 2) e.name = E.name_short;

  const email = (data.email || "").trim();
  if (!email) e.email = E.email_required;
  else if (!EMAIL_RE.test(email)) e.email = E.email_invalid;

  const venue = (data.venue || "").trim();
  if (venue && venue.length < 2) e.venue = E.venue_short;

  if (data.guests !== "" && data.guests != null) {
    const n = Number(data.guests);
    if (!Number.isFinite(n) || n < 1 || !Number.isInteger(n)) e.guests = E.guests_invalid;
    else if (n > 10000) e.guests = E.guests_too_many;
  }

  if ((data.message || "").length > 1000) e.message = E.message_long;

  return e;
}

function BookingWizard({ t, accent = "#3a8dff", variant = "a" }) {
  const [step, setStep] = React.useState(0);
  const [data, setData] = React.useState({
    service: "",
    date: "",
    name: "",
    email: "",
    venue: "",
    guests: "",
    message: "",
  });
  const [touched, setTouched] = React.useState({});
  const [sent, setSent] = React.useState(false);
  const [shake, setShake] = React.useState(false);

  const SERVICES = t.services.items.map((s) => s.t);
  const errors = validateBooking(data, t);

  // Which errors block leaving the current step?
  const stepErrors = (s) => {
    if (s === 0) return errors.service ? ["service"] : [];
    if (s === 1) return errors.date ? ["date"] : [];
    if (s === 2) return ["name", "email", "venue", "guests", "message"].filter((k) => errors[k]);
    return [];
  };
  const stepFields = (s) => {
    if (s === 0) return ["service"];
    if (s === 1) return ["date"];
    if (s === 2) return ["name", "email", "venue", "guests", "message"];
    return [];
  };
  const blocked = stepErrors(step).length > 0;

  const markTouched = (...keys) => {
    setTouched((prev) => {
      const next = { ...prev };
      keys.forEach((k) => (next[k] = true));
      return next;
    });
  };
  const tryAdvance = () => {
    const sErr = stepErrors(step);
    if (sErr.length) {
      markTouched(...sErr);
      setShake(true);
      setTimeout(() => setShake(false), 320);
      return;
    }
    setStep(step + 1);
  };
  const trySend = () => {
    const allErr = Object.keys(errors);
    if (allErr.length) {
      markTouched(...allErr);
      // jump to first step with an error
      for (const s of [0, 1, 2]) {
        if (stepErrors(s).length) { setStep(s); break; }
      }
      setShake(true);
      setTimeout(() => setShake(false), 320);
      return;
    }
    setSent(true);
  };

  const isDark = variant === "a" || variant === "b";
  const fieldBg = isDark ? "rgba(255,255,255,.03)" : "#fff";
  const fieldBorder = isDark ? "rgba(255,255,255,.12)" : "rgba(0,0,0,.15)";
  const fieldText = isDark ? "#fff" : "#0a0a0a";
  const labelClr = isDark ? "rgba(255,255,255,.55)" : "rgba(0,0,0,.55)";
  const errColor = "#ff6b6b";

  const fld = (errorKey) => ({
    background: fieldBg,
    border: `1px solid ${touched[errorKey] && errors[errorKey] ? errColor : fieldBorder}`,
    color: fieldText,
    padding: "14px 16px",
    fontSize: 15,
    fontFamily: "inherit",
    width: "100%",
    outline: "none",
    transition: "border-color .15s, background .15s",
    borderRadius: variant === "b" ? 4 : 0,
  });

  const showError = (key) => touched[key] && errors[key];

  const errMsg = (key) => showError(key) ? (
    <div role="alert" style={{
      marginTop: 6,
      fontSize: 12,
      color: errColor,
      fontFamily: "'JetBrains Mono', ui-monospace, monospace",
      letterSpacing: 0.3,
      display: "flex",
      alignItems: "flex-start",
      gap: 6,
    }}>
      <span aria-hidden="true">⚠</span>
      <span>{errors[key]}</span>
    </div>
  ) : null;

  const stepDot = (i) => ({
    width: 28,
    height: 28,
    borderRadius: variant === "b" ? 14 : 0,
    background: i === step ? accent : i < step ? "rgba(255,255,255,.18)" : "transparent",
    border: i === step ? "none" : `1px solid ${i < step ? "rgba(255,255,255,.25)" : "rgba(255,255,255,.15)"}`,
    color: i === step ? "#0a0a0a" : "rgba(255,255,255,.5)",
    display: "flex",
    alignItems: "center",
    justifyContent: "center",
    fontSize: 12,
    fontFamily: "'JetBrains Mono', ui-monospace, monospace",
    fontWeight: 600,
    flexShrink: 0,
  });

  if (sent) {
    return (
      <div style={{ textAlign: "center", padding: "60px 20px" }}>
        <div style={{
          fontSize: 12,
          fontFamily: "'JetBrains Mono', ui-monospace, monospace",
          letterSpacing: 3,
          color: accent,
          marginBottom: 16,
        }}>✓ {t.book.kicker.toUpperCase()}</div>
        <div style={{ fontSize: 32, fontWeight: 600, color: "#fff", letterSpacing: -0.5, marginBottom: 12 }}>
          {t.book.sent}
        </div>
        <div style={{ fontSize: 15, color: "rgba(255,255,255,.6)" }}>{t.book.sent_sub}</div>
      </div>
    );
  }

  return (
    <div>
      <style>{`@keyframes b-shake { 0%,100%{transform:translateX(0)} 25%{transform:translateX(-6px)} 75%{transform:translateX(6px)} }`}</style>
      {/* step indicator */}
      <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 36, flexWrap: "wrap" }}>
        {t.book.steps.map((label, i) => (
          <React.Fragment key={i}>
            <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
              <div style={stepDot(i)}>{i < step ? "✓" : i + 1}</div>
              <div style={{
                fontSize: 11,
                fontFamily: "'JetBrains Mono', ui-monospace, monospace",
                letterSpacing: 1.5,
                color: i === step ? "#fff" : "rgba(255,255,255,.45)",
                textTransform: "uppercase",
              }}>{label}</div>
            </div>
            {i < t.book.steps.length - 1 && (
              <div style={{ flex: 1, height: 1, background: "rgba(255,255,255,.1)", minWidth: 20 }} />
            )}
          </React.Fragment>
        ))}
      </div>

      {/* step body */}
      <div style={{
        minHeight: 280,
        animation: shake ? "b-shake .32s ease" : "none",
      }}>
        {step === 0 && (
          <div>
            <div style={{ fontSize: 24, fontWeight: 500, color: "#fff", marginBottom: 8, letterSpacing: -0.3 }}>{t.book.step1.q}</div>
            <div style={{ fontSize: 14, color: labelClr, marginBottom: 24 }}>{t.book.step1.help}</div>
            <div style={{ display: "grid", gap: 10 }}>
              {SERVICES.map((s) => {
                const sel = data.service === s;
                return (
                  <button
                    key={s}
                    onClick={() => { setData({ ...data, service: s }); markTouched("service"); }}
                    style={{
                      ...fld("service"),
                      borderColor: sel ? accent : (showError("service") ? errColor : fieldBorder),
                      textAlign: "left",
                      cursor: "pointer",
                      background: sel ? accent : fieldBg,
                      color: sel ? "#0a0a0a" : "#fff",
                      fontWeight: sel ? 600 : 400,
                      display: "flex",
                      justifyContent: "space-between",
                      alignItems: "center",
                    }}
                  >
                    <span>{s}</span>
                    <span style={{ fontFamily: "'JetBrains Mono', ui-monospace, monospace", fontSize: 12, opacity: 0.6 }}>
                      0{SERVICES.indexOf(s) + 1}
                    </span>
                  </button>
                );
              })}
            </div>
            {errMsg("service")}
          </div>
        )}

        {step === 1 && (
          <div>
            <div style={{ fontSize: 24, fontWeight: 500, color: "#fff", marginBottom: 8, letterSpacing: -0.3 }}>{t.book.step2.q}</div>
            <div style={{ fontSize: 14, color: labelClr, marginBottom: 24 }}>{t.book.step2.help}</div>
            <input
              type="date"
              min={isoMinDate()}
              max={isoMaxDate()}
              value={data.date}
              onChange={(e) => { setData({ ...data, date: e.target.value }); markTouched("date"); }}
              onBlur={() => markTouched("date")}
              aria-invalid={!!showError("date")}
              style={{ ...fld("date"), fontSize: 18, colorScheme: "dark" }}
            />
            {errMsg("date")}
            <div style={{
              marginTop: 18,
              padding: 16,
              background: "rgba(255,255,255,.03)",
              border: `1px dashed ${fieldBorder}`,
              fontSize: 13,
              color: labelClr,
              fontFamily: "'JetBrains Mono', ui-monospace, monospace",
            }}>
              <span style={{ color: accent }}>{data.service || "—"}</span>
              <span style={{ margin: "0 8px", opacity: 0.4 }}>/</span>
              <span>{data.date || "yyyy-mm-dd"}</span>
            </div>
          </div>
        )}

        {step === 2 && (
          <div>
            <div style={{ fontSize: 24, fontWeight: 500, color: "#fff", marginBottom: 24, letterSpacing: -0.3 }}>{t.book.step3.q}</div>
            <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 14 }}>
              <div>
                <input
                  placeholder={t.book.step3.name + " *"}
                  value={data.name}
                  onChange={(e) => { setData({ ...data, name: e.target.value }); markTouched("name"); }}
                  onBlur={() => markTouched("name")}
                  aria-invalid={!!showError("name")}
                  style={fld("name")}
                />
                {errMsg("name")}
              </div>
              <div>
                <input
                  type="email"
                  inputMode="email"
                  autoComplete="email"
                  placeholder={t.book.step3.email + " *"}
                  value={data.email}
                  onChange={(e) => { setData({ ...data, email: e.target.value }); markTouched("email"); }}
                  onBlur={() => markTouched("email")}
                  aria-invalid={!!showError("email")}
                  style={fld("email")}
                />
                {errMsg("email")}
              </div>
              <div>
                <input
                  placeholder={t.book.step3.venue}
                  value={data.venue}
                  onChange={(e) => { setData({ ...data, venue: e.target.value }); markTouched("venue"); }}
                  onBlur={() => markTouched("venue")}
                  aria-invalid={!!showError("venue")}
                  style={fld("venue")}
                />
                {errMsg("venue")}
              </div>
              <div>
                <input
                  type="number"
                  inputMode="numeric"
                  min="1"
                  max="10000"
                  step="1"
                  placeholder={t.book.step3.guests}
                  value={data.guests}
                  onChange={(e) => { setData({ ...data, guests: e.target.value }); markTouched("guests"); }}
                  onBlur={() => markTouched("guests")}
                  aria-invalid={!!showError("guests")}
                  style={fld("guests")}
                />
                {errMsg("guests")}
              </div>
              <div style={{ gridColumn: "1 / -1" }}>
                <textarea
                  placeholder={t.book.step3.message}
                  value={data.message}
                  onChange={(e) => { setData({ ...data, message: e.target.value }); markTouched("message"); }}
                  onBlur={() => markTouched("message")}
                  aria-invalid={!!showError("message")}
                  rows={4}
                  style={{ ...fld("message"), resize: "vertical", fontFamily: "inherit" }}
                />
                <div style={{
                  display: "flex",
                  justifyContent: "space-between",
                  alignItems: "center",
                  marginTop: 6,
                  fontSize: 11,
                  fontFamily: "'JetBrains Mono', ui-monospace, monospace",
                  color: (data.message || "").length > 1000 ? errColor : labelClr,
                }}>
                  <div>{errMsg("message") || <span>&nbsp;</span>}</div>
                  <div>{(data.message || "").length}/1000</div>
                </div>
              </div>
            </div>
          </div>
        )}

        {step === 3 && (
          <div>
            <div style={{ fontSize: 24, fontWeight: 500, color: "#fff", marginBottom: 8, letterSpacing: -0.3 }}>{t.book.step4.q}</div>
            <div style={{ fontSize: 14, color: labelClr, marginBottom: 24 }}>{t.book.step4.note}</div>
            <div style={{
              border: `1px solid ${fieldBorder}`,
              padding: 24,
              background: "rgba(255,255,255,.02)",
              borderRadius: variant === "b" ? 4 : 0,
              fontFamily: "'JetBrains Mono', ui-monospace, monospace",
              fontSize: 13,
              lineHeight: 1.9,
              color: "rgba(255,255,255,.85)",
            }}>
              {[
                [t.book.service, data.service || "—"],
                [t.book.date, data.date || "—"],
                [t.book.step3.name, data.name || "—"],
                [t.book.step3.email, data.email || "—"],
                [t.book.step3.venue, data.venue || "—"],
                [t.book.step3.guests, data.guests || "—"],
              ].map(([k, v]) => (
                <div key={k} style={{ display: "flex", justifyContent: "space-between", borderBottom: "1px dashed rgba(255,255,255,.08)", padding: "4px 0" }}>
                  <span style={{ color: labelClr, textTransform: "uppercase", letterSpacing: 1, fontSize: 11 }}>{k}</span>
                  <span style={{ color: "#fff", textAlign: "right", maxWidth: "60%" }}>{v}</span>
                </div>
              ))}
              {data.message && (
                <div style={{ marginTop: 12, paddingTop: 12, borderTop: "1px solid rgba(255,255,255,.1)", color: "rgba(255,255,255,.75)", fontFamily: "inherit" }}>
                  "{data.message}"
                </div>
              )}
            </div>
          </div>
        )}
      </div>

      {/* nav */}
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginTop: 36, gap: 12 }}>
        <button
          onClick={() => setStep(Math.max(0, step - 1))}
          disabled={step === 0}
          style={{
            padding: "14px 22px",
            background: "transparent",
            border: `1px solid ${fieldBorder}`,
            color: step === 0 ? "rgba(255,255,255,.25)" : "#fff",
            cursor: step === 0 ? "default" : "pointer",
            fontFamily: "'JetBrains Mono', ui-monospace, monospace",
            fontSize: 12,
            letterSpacing: 2,
            textTransform: "uppercase",
            borderRadius: variant === "b" ? 4 : 0,
          }}
        >← {t.book.back}</button>

        {step < 3 ? (
          <button
            onClick={tryAdvance}
            aria-disabled={blocked}
            style={{
              padding: "14px 26px",
              background: blocked ? "rgba(255,255,255,.08)" : accent,
              border: "none",
              color: blocked ? "rgba(255,255,255,.5)" : "#0a0a0a",
              cursor: "pointer",
              fontFamily: "'JetBrains Mono', ui-monospace, monospace",
              fontSize: 12,
              letterSpacing: 2,
              textTransform: "uppercase",
              fontWeight: 700,
              borderRadius: variant === "b" ? 4 : 0,
              transition: "background .15s, color .15s",
            }}
          >{t.book.next} →</button>
        ) : (
          <button
            onClick={trySend}
            style={{
              padding: "14px 26px",
              background: accent,
              border: "none",
              color: "#0a0a0a",
              cursor: "pointer",
              fontFamily: "'JetBrains Mono', ui-monospace, monospace",
              fontSize: 12,
              letterSpacing: 2,
              textTransform: "uppercase",
              fontWeight: 700,
              borderRadius: variant === "b" ? 4 : 0,
            }}
          >{t.book.send} →</button>
        )}
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// Waveform — animated SVG bars (for footer / divider)
// ─────────────────────────────────────────────────────────────
function Waveform({ bars = 40, accent = "#3a8dff", height = 32 }) {
  const [tick, setTick] = React.useState(0);
  React.useEffect(() => {
    let raf;
    const loop = () => { setTick((t) => t + 1); raf = requestAnimationFrame(loop); };
    raf = requestAnimationFrame(loop);
    return () => cancelAnimationFrame(raf);
  }, []);
  return (
    <div style={{ display: "flex", alignItems: "center", gap: 2, height }}>
      {Array.from({ length: bars }).map((_, i) => {
        const phase = (tick / 8 + i * 0.4);
        const h = (0.3 + 0.7 * Math.abs(Math.sin(phase) * Math.cos(phase / 2))) * height;
        return (
          <div
            key={i}
            style={{
              width: 2,
              height: h,
              background: i % 5 === 0 ? accent : "rgba(255,255,255,.4)",
              transition: "height 80ms linear",
            }}
          />
        );
      })}
    </div>
  );
}

Object.assign(window, { DICT, useLang, LangToggle, GlitchHero, BookingWizard, Waveform });
