/* screen-settings.jsx — Company profile, invoice defaults, bank, language → window.QScreens.Settings */
(function () {
  const { useState } = React;
  const Q = window.Q, Icons = window.Icons;
  const { Icon } = Icons;
  const { AppBar, Card, Kicker, Button, Field, Input, Textarea, Select, Segmented, Toggle, QR, NitaMark, Collapsible } = Q;
  const t = window.t;

  function Settings({ api }) {
    const { db, setDb } = api;
    const co = db.COMPANY;
    const config = db.config || {};
    const setCo = patch => setDb(d => ({ ...d, COMPANY: { ...d.COMPANY, ...patch } }));
    /* Write through to the canonical db.config; taxes also mirror onto
       COMPANY.taxes so the legacy reads still present stay in sync. */
    const setConfig = patch => setDb(d => {
      const nextConfig = { ...d.config, ...patch };
      const nextCo = patch.taxes
        ? { ...d.COMPANY, taxes: patch.taxes.map(tx => ({ id: tx.id, name: tx.name, rate: tx.rate })) }
        : d.COMPANY;
      return { ...d, config: nextConfig, COMPANY: nextCo };
    });
    const pm = co.payment || {};
    const setPm = patch => setCo({ payment: { ...co.payment, ...patch } });
    const setMethod = (key, patch) => setPm({ [key]: { ...(co.payment[key] || {}), ...patch } });
    /* Currency names in the active locale (Intl.DisplayNames), falling back to
       the English seed name if the API or code is unsupported. */
    const curName = (() => {
      let dn = null;
      try { dn = new Intl.DisplayNames(window.getLocale() === "fr" ? "fr" : "en", { type: "currency" }); } catch (e) {}
      return c => { try { return (dn && dn.of(c.code)) || c.name; } catch (e) { return c.name; } };
    })();
    const configTaxes = config.taxes || co.taxes || [];
    const configCurrencies = config.currencies || Object.values(window.CURRENCIES);
    const curOpts = configCurrencies.map(c => ({ value: c.code, label: `${c.code} · ${curName(c)}` }));
    const termOpts = [0, 15, 30, 45, 60].map(n => ({ value: String(n), label: n === 0 ? t("set.terms.receipt") : n === 60 ? t("g.net60") : t("g.net" + n) }));
    /* The company-default tax Select keys by tax id (not rate) so two taxes at
       the same rate (e.g. GST 5% vs VAT 5%) stay distinct. Selecting one stores
       BOTH the rate AND the name so the default carries identity downstream. */
    const taxOpts = configTaxes.map(txObj => ({ value: txObj.id, label: txObj.name ? `${txObj.name} (${txObj.rate}%)` : window.LIB.fmtPct(txObj.rate) }));
    const defaultTaxId = (() => {
      const byName = configTaxes.find(tx => tx.rate === co.taxRate && (co.taxName == null || tx.name === co.taxName));
      return (byName || configTaxes.find(tx => tx.rate === co.taxRate) || {}).id || "";
    })();
    /* Numbering scheme: the dropdown writes co.numbering (the invoice pattern
       LIB.nextNumber honors), and every option previews the REAL next number
       for that pattern against the live nextSeq — so the choices and the hint
       below agree to the digit (P2-2). co.numbering may be missing on an old
       db; fall back to the first preset so the Select is never desynced. */
    const numPatterns = ["INV-{YYYY}-{seq}", "{YYYY}{seq}", "F-{seq}"];
    const numOpts = numPatterns.map(p => ({ value: p, label: window.LIB.nextNumber({ ...co, numbering: p }, "invoice") }));
    const numValue = typeof co.numbering === "string" && numPatterns.indexOf(co.numbering) !== -1 ? co.numbering : numPatterns[0];
    const locale = db.locale || "en";

    return (
      <div style={{ paddingBottom: 4 }}>
        <AppBar large title={t("set.title")} />
        <div style={{ padding: "6px 16px 0", display: "flex", flexDirection: "column", gap: 14 }}>

          {/* ── Cluster 1: Your business ── */}
          <ClusterHead>{t("set.group.business")}</ClusterHead>

          {/* Company profile */}
          <Collapsible title={t("set.company")} sub={co.name + " · " + co.email}>
            <div style={{ padding: 16 }}>
            <div style={{ display: "flex", alignItems: "center", gap: 14, marginBottom: 18 }}>
              <div style={{ position: "relative" }}>
                {co.logo
                  ? <img src={co.logo} alt="" style={{ width: 56, height: 56, borderRadius: 16, objectFit: "cover", border: "1.4px solid var(--color-border-strong)", display: "block" }} />
                  : <NitaMark size={56} radius={16} />}
              </div>
              <div style={{ flex: 1 }}>
                <div style={{ fontSize: 14, fontWeight: 600 }}>{co.name}</div>
                <div style={{ display: "flex", gap: 8, marginTop: 6 }}>
                  <label style={{ height: 40, padding: "0 14px", borderRadius: 8, border: "1.4px solid var(--color-border-strong)", background: "var(--color-surface-0)", color: "var(--color-text-1)", fontSize: 12.5, fontWeight: 600, cursor: "pointer", display: "inline-flex", alignItems: "center", gap: 6 }}>
                    <Icon name="image" size={15} />{t("set.logo.upload")}
                    <input type="file" accept="image/*" style={{ display: "none" }} onChange={e => {
                      const file = e.target.files && e.target.files[0];
                      if (!file) return;
                      const reader = new FileReader();
                      reader.onload = ev => { setCo({ logo: ev.target.result }); Q.toast(t("toast.logoSaved"), "check"); };
                      reader.readAsDataURL(file);
                      e.target.value = "";
                    }} />
                  </label>
                  {co.logo && (
                    <button onClick={() => setCo({ logo: null })} style={{ height: 40, padding: "0 14px", borderRadius: 8, border: "1.4px solid var(--color-border-strong)", background: "var(--color-surface-0)", color: "var(--color-text-2)", fontSize: 12.5, fontWeight: 600, cursor: "pointer", display: "inline-flex", alignItems: "center", gap: 6 }}>
                      <Icon name="trash" size={15} />{t("set.logo.remove")}
                    </button>
                  )}
                </div>
              </div>
            </div>
            <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
              <Field label={t("set.name")}><Input value={co.name} onChange={e => setCo({ name: e.target.value })} /></Field>
              <Field label={t("set.email")}><Input value={co.email} onChange={e => setCo({ email: e.target.value })} type="email" /></Field>
              <Field label={t("set.phone")}><Input value={co.phone} onChange={e => setCo({ phone: e.target.value })} mono /></Field>
              <Field label={t("set.address")}><Textarea value={co.address} onChange={e => setCo({ address: e.target.value })} rows={2} /></Field>
              <div style={{ display: "flex", gap: 12 }}>
                <Field label={t("set.taxid")} style={{ flex: 1, minWidth: 0 }}><Input value={co.vat} onChange={e => setCo({ vat: e.target.value })} mono /></Field>
              </div>
              <Field label={t("set.reg")}><Input value={co.siret} onChange={e => setCo({ siret: e.target.value })} mono /></Field>
            </div>
            </div>
          </Collapsible>

          {/* Invoice defaults (includes business type) */}
          <Collapsible title={t("set.invdefaults")} sub={t("set.sum.defaults", { currency: co.currency, terms: (termOpts.find(o => o.value === String(co.terms)) || {}).label, rate: window.LIB.fmtPct(co.taxRate) })}>
            <div style={{ padding: 16 }}>
            <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
              <Field label={t("set.biztype")} hint={t("set.biztype.sub")}>
                <Segmented value={co.businessType || "both"} onChange={v => setCo({ businessType: v })}
                  options={[{ value: "services", label: t("biz.services") }, { value: "products", label: t("biz.products") }, { value: "both", label: t("biz.both") }]} />
              </Field>
              <div style={{ display: "flex", gap: 12 }}>
                <Field label={t("set.currency")} style={{ flex: 1, minWidth: 0 }}><Select value={co.currency} onChange={e => setCo({ currency: e.target.value })} options={curOpts} /></Field>
              </div>
              <div style={{ display: "flex", gap: 12 }}>
                <Field label={t("set.terms")} style={{ flex: 1, minWidth: 0 }}><Select value={String(co.terms)} onChange={e => setCo({ terms: Number(e.target.value) })} options={termOpts} /></Field>
                <Field label={t("set.taxrate")} style={{ flex: 1, minWidth: 0 }}><Select value={defaultTaxId} onChange={e => { const tx = configTaxes.find(x => x.id === e.target.value); if (tx) setCo({ taxRate: tx.rate, taxName: tx.name }); }} options={taxOpts} /></Field>
              </div>
              <Field label={t("set.numbering")} hint={t("set.numbering.next", { number: window.LIB.nextNumber(co) })}><Select value={numValue} onChange={e => setCo({ numbering: e.target.value })} options={numOpts} /></Field>
              <Field label={t("set.notes")} hint={t("set.notes.hint", { days: co.terms })}><Textarea value={co.notesDefault} onChange={e => setCo({ notesDefault: e.target.value })} rows={2} placeholder={t("inv.notes.default")} /></Field>
              <Field label={t("set.legalmention")}><Textarea value={co.legalMention} onChange={e => setCo({ legalMention: e.target.value })} rows={2} placeholder={t("inv.legal.default")} /></Field>
            </div>
            </div>
          </Collapsible>

          {/* Taxes (F1) */}
          <Collapsible title={t("set.taxes")} sub={t("set.taxes.freq." + ((co.urssaf && co.urssaf.freq) || "quarterly")) + " · " + t("tax.cat." + ((co.urssaf && co.urssaf.category) || "services"))}>
            <div style={{ padding: 16 }}>
            <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
              <Field label={t("set.taxes.freq")}>
                <Segmented value={(co.urssaf && co.urssaf.freq) || "quarterly"} onChange={v => setCo({ urssaf: { ...co.urssaf, freq: v } })}
                  options={[{ value: "monthly", label: t("set.taxes.freq.monthly") }, { value: "quarterly", label: t("set.taxes.freq.quarterly") }]} />
              </Field>
              <Field label={t("set.taxes.category")}>
                <Select value={(co.urssaf && co.urssaf.category) || "services"} onChange={e => setCo({ urssaf: { ...co.urssaf, category: e.target.value } })}
                  options={[{ value: "services", label: t("tax.cat.services") }, { value: "sales", label: t("tax.cat.sales") }]} />
              </Field>

              <div style={{ borderTop: "1px solid var(--color-border)", marginTop: 12, paddingTop: 12 }}>
                <div style={{ fontSize: 13, fontWeight: 650, color: "var(--color-text-1)", marginBottom: 8 }}>{t("set.taxes.custom")}</div>
                <ListManager
                  items={configTaxes}
                  onChange={txs => setConfig({ taxes: txs })}
                  defaults={window.DATA.defaults.taxes()}
                  labelFor={tx => tx.name ? `${tx.name} (${tx.rate}%)` : window.LIB.fmtPct(tx.rate)}
                  fields={[
                    { key: "name", type: "text", label: t("set.tax.name.label"), placeholder: "GST, QST, VAT...", flex: 2 },
                    { key: "rate", type: "number", label: t("set.tax.rate.label"), placeholder: "5", step: "0.001", flex: 1 },
                  ]}
                  makeItem={vals => ({ id: "tx-" + Date.now(), name: vals.name, rate: Number(vals.rate), default: false })}
                  validate={vals => !!(vals.name && vals.name.trim()) && !isNaN(Number(vals.rate)) && vals.rate !== ""}
                  invalidMsg={t("toast.invalidTax")}
                  addedMsg={t("toast.taxAdded")}
                  deletedMsg={t("toast.taxDeleted")}
                />
              </div>
            </div>
            </div>
          </Collapsible>

          {/* Expense categories — config.expenseCats (single source w/ ai-backend) */}
          <Collapsible title={t("set.cats")} sub={t("set.cats.sub", { count: (config.expenseCats || []).length })}>
            <div style={{ padding: 16 }}>
              <ListManager
                items={config.expenseCats || []}
                onChange={cats => setConfig({ expenseCats: cats })}
                defaults={window.DATA.defaults.expenseCats()}
                labelFor={c => t(c.label)}
                iconFor={c => c.icon}
                fields={[
                  { key: "label", type: "text", label: t("set.cat.name.label"), placeholder: "Marketing", flex: 2 },
                ]}
                makeItem={vals => { const id = "cat-" + Date.now(); return { id, label: vals.label, icon: "tag" }; }}
                validate={vals => !!(vals.label && vals.label.trim())}
                invalidMsg={t("set.cat.invalid")}
                addedMsg={t("set.cat.added")}
                deletedMsg={t("set.cat.deleted")}
              />
            </div>
          </Collapsible>

          {/* Currencies — config.currencies (user-extensible superset of builtins) */}
          <Collapsible title={t("set.currencies")} sub={t("set.currencies.sub", { count: configCurrencies.length })}>
            <div style={{ padding: 16 }}>
              <ListManager
                items={configCurrencies}
                onChange={curs => setConfig({ currencies: curs })}
                defaults={window.DATA.defaults.currencies()}
                keyOf={c => c.code}
                labelFor={c => `${c.code} · ${c.symbol} · ${c.name}`}
                fields={[
                  { key: "code", type: "text", label: t("set.cur.code.label"), placeholder: "JPY", flex: 1 },
                  { key: "symbol", type: "text", label: t("set.cur.symbol.label"), placeholder: "¥", flex: 1 },
                  { key: "name", type: "text", label: t("set.cur.name.label"), placeholder: "Japanese Yen", flex: 2 },
                ]}
                makeItem={vals => ({ code: String(vals.code || "").toUpperCase().trim(), symbol: vals.symbol || vals.code, name: vals.name || vals.code })}
                validate={vals => !!(vals.code && vals.code.trim())}
                invalidMsg={t("set.cur.invalid")}
                addedMsg={t("set.cur.added")}
                deletedMsg={t("set.cur.deleted")}
              />
            </div>
          </Collapsible>

          {/* ── Cluster 2: Get paid ── */}
          <ClusterHead>{t("set.group.getpaid")}</ClusterHead>

          {/* Payment methods */}
          {(() => { const n = [pm.bank, pm.card, pm.nita].filter(m => m && m.enabled).length; return (
          <Collapsible title={t("set.pm")} sub={t(n === 1 ? "set.pm.enabled.one" : "set.pm.enabled.n", { count: n })}>
            <div style={{ padding: 16 }}>
            <div style={{ fontSize: 12.5, color: "var(--color-text-3)", lineHeight: 1.5, marginBottom: 16 }}>{t("set.pm.sub")}</div>
            <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
              {/* bank transfer */}
              <PMRow icon="bank" title={t("set.pm.bank")} sub={t("set.pm.bank.sub")} checked={!!(pm.bank && pm.bank.enabled)} onToggle={v => setMethod("bank", { enabled: v })} />
              {/* card */}
              <PMRow icon="card" title={t("set.pm.card")} sub={t("set.pm.card.sub")} checked={!!(pm.card && pm.card.enabled)} onToggle={v => setMethod("card", { enabled: v })}>
                <Field label={t("set.pm.card.link")}><Input value={(pm.card && pm.card.link) || ""} onChange={e => setMethod("card", { link: e.target.value })} mono placeholder="pay.yoursite.co" /></Field>
              </PMRow>
              {/* Nita */}
              <PMRow icon="wallet" title={t("set.pm.nita")} sub={t("set.pm.nita.sub")} sage checked={!!(pm.nita && pm.nita.enabled)} onToggle={v => setMethod("nita", { enabled: v })}>
                <div style={{ display: "flex", gap: 12 }}>
                  <Field label={t("set.pm.nita.handle")} style={{ flex: 1, minWidth: 0 }}><Input value={(pm.nita && pm.nita.handle) || ""} onChange={e => setMethod("nita", { handle: e.target.value })} mono /></Field>
                  <Field label={t("set.pm.nita.phone")} style={{ flex: 1, minWidth: 0 }}><Input value={(pm.nita && pm.nita.phone) || ""} onChange={e => setMethod("nita", { phone: e.target.value })} mono /></Field>
                </div>
              </PMRow>
            </div>

            {/* QR method + live preview */}
            <div style={{ marginTop: 16, paddingTop: 16, borderTop: "1px solid var(--color-border)" }}>
              <div style={{ fontSize: 14, fontWeight: 600, color: "var(--color-text-1)" }}>{t("set.pm.qr")}</div>
              <div style={{ fontSize: 12.5, color: "var(--color-text-3)", lineHeight: 1.5, margin: "4px 0 12px" }}>{t("set.pm.qr.sub")}</div>
              <div style={{ display: "flex", gap: 14, alignItems: "center" }}>
                <div style={{ flex: 1 }}>
                  <Segmented value={pm.qrMethod || "bank"} onChange={v => setPm({ qrMethod: v })} options={[{ value: "bank", label: t("set.pm.qr.bank") }, { value: "nita", label: t("set.pm.qr.nita") }]} />
                </div>
                {(() => { const qr = window.LIB.payQR(co, { id: "INV-2026-015", currency: co.currency }, 1250); return qr ? (
                  <div style={{ padding: 7, background: "#fff", borderRadius: 10, border: "1.4px solid var(--color-border-strong)", flexShrink: 0 }}><QR value={qr.value} size={68} color="hsl(35 20% 12%)" /></div>
                ) : null; })()}
              </div>
            </div>
            </div>
          </Collapsible>
          ); })()}

          {/* Bank */}
          <Collapsible title={t("set.bank")} sub={co.iban ? <span style={{ fontFamily: "var(--font-mono)" }}>{"···" + co.iban.replace(/\s/g, "").slice(-4)}</span> : "-"}>
            <div style={{ padding: 16 }}>
            <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
              <Field label={t("set.accountname")}><Input value={co.accountName} onChange={e => setCo({ accountName: e.target.value })} /></Field>
              <Field label={t("set.iban")}><Input value={co.iban} onChange={e => setCo({ iban: e.target.value })} mono /></Field>
              <Field label={t("set.bic")}><Input value={co.bic} onChange={e => setCo({ bic: e.target.value })} mono /></Field>
            </div>
            </div>
          </Collapsible>

          {/* Autopilot dunning (F4) */}
          <Section label={t("chase.autopilot")}>
            <PMRow icon="bell" sage title={t("chase.settings.toggle")} sub={t("chase.settings.sub")}
              checked={!!(co.dunning && co.dunning.auto)}
              onToggle={v => setCo({ dunning: { ...co.dunning, auto: v } })}>
              <Segmented value={(co.dunning && co.dunning.preset) || "standard"}
                onChange={p => setCo({ dunning: { ...co.dunning, preset: p, offsets: { relaxed: [5, 14, 30], standard: [3, 10, 21], persistent: [1, 7, 14] }[p] } })}
                options={["relaxed", "standard", "persistent"].map(p => ({ value: p, label: t("chase.preset." + p) }))} />
              <div style={{ fontSize: 12, color: "var(--color-text-3)", lineHeight: 1.5, marginTop: 10 }}>{t("chase.preset.hint." + ((co.dunning && co.dunning.preset) || "standard"))}</div>
            </PMRow>
          </Section>

          {/* ── Cluster 3: Nita & data ── */}
          <ClusterHead>{t("set.group.nita")}</ClusterHead>

          {/* AI engine (demo / local Ollama) */}
          <AiEngineCard />

          {/* Privacy & offline */}
          <Collapsible title={t("set.privacy")} sub={t("set.privacy.tag")}>
            <div style={{ padding: 16, display: "flex", flexDirection: "column", gap: 14 }}>
              <div style={{ fontSize: 13, color: "var(--color-text-3)", lineHeight: 1.45 }}>{t("set.privacy.desc")}</div>
              {/* Save-state pill (FIX 1): honest signal that the local-only db
                  is being written. Idle is hidden so it never nags. */}
              {(() => {
                const ss = api.saveState;
                if (!ss || ss === "idle") return null;
                const map = {
                  saving: { label: t("set.save.saving"), bg: "var(--color-surface-2)", fg: "var(--color-text-2)" },
                  saved: { label: t("set.save.saved"), bg: "var(--color-surface-2)", fg: "var(--color-primary)" },
                  full: { label: t("set.save.full"), bg: "color-mix(in oklab, var(--color-accent) 14%, transparent)", fg: "var(--color-accent)" },
                  error: { label: t("set.save.error"), bg: "color-mix(in oklab, var(--color-accent) 14%, transparent)", fg: "var(--color-accent)" },
                };
                const m = map[ss]; if (!m) return null;
                return (
                  <div style={{ display: "inline-flex", alignItems: "center", alignSelf: "flex-start", gap: 6, padding: "4px 10px", borderRadius: 999, fontSize: 12, fontWeight: 600, background: m.bg, color: m.fg }}>
                    {m.label}
                  </div>
                );
              })()}
              {/* Reset to a pristine demo: wipes the local stores, reloads. */}
              <Button size="sm" variant="secondary" style={{ color: "var(--color-accent)" }} onClick={() => {
                if (window.confirm(t("set.reset.confirm"))) {
                  api.resetToSeed();
                }
              }}>
                {t("set.reset")}
              </Button>
            </div>
          </Collapsible>

          {/* ── Cluster 4: App ── */}
          <ClusterHead>{t("set.group.app")}</ClusterHead>

          {/* Preferences: appearance + language (always open, 10-second demo moves) */}
          <Section label={t("set.prefs")}>
            <div style={{ fontSize: 12.5, color: "var(--color-text-3)", lineHeight: 1.5, marginBottom: 12 }}>{t("set.theme.sub")}</div>
            <Segmented value={(co.theme) || "light"} onChange={v => api.setTheme(v)}
              options={[
                { value: "light", label: <span style={{ display: "inline-flex", alignItems: "center", gap: 6 }}><Icon name="sun" size={14} />{t("set.theme.light")}</span> },
                { value: "dark", label: <span style={{ display: "inline-flex", alignItems: "center", gap: 6 }}><Icon name="moon" size={14} />{t("set.theme.dark")}</span> },
              ]} />
            <div style={{ marginTop: 12 }}>
              <Field label={t("set.language")}>
                <Segmented value={locale} onChange={loc => setDb(d => ({ ...d, locale: loc }))} options={[{ value: "en", label: "English" }, { value: "fr", label: "Français" }]} />
              </Field>
            </div>
          </Section>

          {/* Misc */}
          <Card pad={0}>
            <Q.Row onClick={() => api.go("taxes")}>
              <div style={{ width: 30, height: 30, borderRadius: 9, background: "var(--color-surface-1)", display: "flex", alignItems: "center", justifyContent: "center", color: "var(--color-text-2)" }}><Icon name="bank" size={16} /></div>
              <span style={{ flex: 1, fontSize: 14.5, fontWeight: 500 }}>{t("set.taxes.open")}</span>
              <Icon name="chevronRight" size={18} color="var(--color-text-3)" />
            </Q.Row>
            <Q.Row onClick={api.replayOnboarding}>
              <div style={{ width: 30, height: 30, borderRadius: 9, background: "var(--color-surface-1)", display: "flex", alignItems: "center", justifyContent: "center", color: "var(--color-text-2)" }}><Icon name="refresh" size={16} /></div>
              <span style={{ flex: 1, fontSize: 14.5, fontWeight: 500 }}>{t("set.replay")}</span>
              <Icon name="chevronRight" size={18} color="var(--color-text-3)" />
            </Q.Row>
          </Card>

          {/* Demo clock (F2) — neutral demo machinery, not AI, not money */}
          <Section label={t("clock.kicker")}>
            <div style={{ display: "flex", alignItems: "center", gap: 12 }}>
              <div style={{ width: 30, height: 30, borderRadius: 9, background: "var(--color-surface-1)", display: "flex", alignItems: "center", justifyContent: "center", color: "var(--color-text-2)", flexShrink: 0 }}><Icon name="calendar" size={16} /></div>
              <span style={{ flex: 1, fontSize: 14.5, fontWeight: 500 }}>{t("clock.today", { date: window.fmtDate(api.today) })}</span>
            </div>
            <div style={{ display: "flex", gap: 8, marginTop: 12 }}>
              <Button size="sm" variant="secondary" onClick={() => api.advanceClock(1)} style={{ flex: 1 }}>{t("clock.plus1")}</Button>
              <Button size="sm" variant="secondary" onClick={() => api.advanceClock(7)} style={{ flex: 1 }}>{t("clock.plus7")}</Button>
              <Button size="sm" variant="secondary" onClick={() => api.advanceClock(30)} style={{ flex: 1 }}>{t("clock.plus30")}</Button>
            </div>
            <div style={{ fontSize: 12, color: "var(--color-text-3)", lineHeight: 1.5, marginTop: 10 }}>{t("clock.hint")}</div>
          </Section>

          {/* Help & support */}
          <Collapsible title={t("set.help")} sub={t("set.help.tag")}>
            <div style={{ padding: 16, display: "flex", flexDirection: "column", gap: 14 }}>
              <div style={{ borderBottom: "1px solid var(--color-border)", paddingBottom: 10 }}>
                <div style={{ fontSize: 13.5, fontWeight: 600, color: "var(--color-text-1)", marginBottom: 4 }}>{t("set.help.q1")}</div>
                <div style={{ fontSize: 12.5, color: "var(--color-text-3)", lineHeight: 1.4 }}>{t("set.help.a1")}</div>
              </div>
              <div style={{ borderBottom: "1px solid var(--color-border)", paddingBottom: 10 }}>
                <div style={{ fontSize: 13.5, fontWeight: 600, color: "var(--color-text-1)", marginBottom: 4 }}>{t("set.help.q2")}</div>
                <div style={{ fontSize: 12.5, color: "var(--color-text-3)", lineHeight: 1.4 }}>{t("set.help.a2")}</div>
              </div>
              {/* Contact is now a REAL mailto anchor (was a toast that merely
                  printed the literal "mailto:…" text and opened nothing). It
                  opens the user's mail app with a prefilled subject that
                  carries the build so support has context. Styled byte-for-byte
                  like a secondary Q.Button. The recipient is URL-encoded. */}
              <a
                href={"mailto:" + encodeURIComponent("support@nita.app") + "?subject=" + encodeURIComponent("Nita v" + t("set.version") + " — support")}
                className="q-btn"
                style={{
                  display: "inline-flex", alignItems: "center", justifyContent: "center", gap: 6,
                  height: 36, padding: "0 14px", fontSize: 13, width: "100%", boxSizing: "border-box",
                  fontFamily: "var(--font-sans)", fontWeight: 600, borderRadius: "var(--radius-md)",
                  cursor: "pointer", whiteSpace: "nowrap", letterSpacing: "-0.01em", textDecoration: "none",
                  background: "var(--color-surface-0)", color: "var(--color-text-1)", border: "1.4px solid var(--color-border-strong)",
                }}>
                <Icon name="mail" size={15} />{t("set.contact")}
              </a>
              {/* Version status is now an HONEST static line (was a fake "Check
                  for Updates" button firing an instant "up to date" toast with
                  no real check). The single canonical set.version drives it, so
                  it can never disagree with the footer. */}
              <div style={{ display: "flex", alignItems: "center", gap: 8, marginTop: 2 }}>
                <Icon name="checkCircle" size={15} color="var(--color-primary)" style={{ flexShrink: 0 }} />
                <span style={{ fontSize: 12.5, color: "var(--color-text-3)", lineHeight: 1.4 }}>{t("set.update.ok")}</span>
              </div>
            </div>
          </Collapsible>

          {/* FacturX note */}
          <div style={{ display: "flex", gap: 11, padding: "14px 16px", background: "var(--color-primary-muted)", borderRadius: 12, border: "1px solid color-mix(in oklab, var(--color-primary) 25%, transparent)" }}>
            <Icon name="checkCircle" size={18} color="var(--color-primary)" style={{ flexShrink: 0, marginTop: 1 }} />
            <div style={{ fontSize: 12.5, color: "var(--color-text-2)", lineHeight: 1.5 }}>{t("set.legal")}</div>
          </div>

          <div style={{ textAlign: "center", padding: "8px 0 4px" }}>
            <div style={{ display: "inline-flex", alignItems: "center", gap: 7, color: "var(--color-text-3)" }}>
              <NitaMark size={18} />
              <span style={{ fontFamily: "var(--font-mono)", fontSize: 11, letterSpacing: "0.06em" }}>{t("set.about")}</span>
            </div>
          </div>
        </div>
      </div>
    );
  }

  /* ── AI engine card ─────────────────────────────────────────────
     Picks the backend the composer + receipt OCR talk to (window.NITA_AI):
     demo = the scripted agent in ai.js, local = the user's own Ollama.
     Config lives in localStorage (nita_ai_cfg), not in db. */
  function AiEngineCard() {
    const AI = window.NITA_AI;
    const [cfg, setCfg] = useState(() => AI ? AI.getConfig() : { mode: "demo", url: "", model: "" });
    const [testing, setTesting] = useState(false);
    const [diag, setDiag] = useState(null);
    if (!AI) return null;
    // Any edit to url/model/mode invalidates the last test result.
    const update = patch => { setDiag(null); setCfg(AI.setConfig(patch)); };
    const test = () => {
      if (testing) return;
      setTesting(true);
      AI.testConnection().then(r => {
        setTesting(false);
        if (r.ok) {
          const fresh = AI.getConfig();
          setCfg(fresh);
          setDiag({ ok: true, count: r.models.length, model: fresh.model || t("ai.settings.model.ph") });
        } else {
          setDiag({ ok: false, reason: r.reason || "blocked" });
        }
      });
    };
    return (
      <Section label={t("ai.settings.title")}>
        <div style={{ display: "flex", alignItems: "flex-start", gap: 11, marginBottom: 12 }}>
          <div style={{ width: 30, height: 30, borderRadius: 9, background: "var(--color-primary-muted)", display: "flex", alignItems: "center", justifyContent: "center", color: "var(--color-primary)", flexShrink: 0 }}>
            <Q.Sparkle size={14} />
          </div>
          <div style={{ fontSize: 12.5, color: "var(--color-text-3)", lineHeight: 1.5 }}>{t("ai.settings.body")}</div>
        </div>
        <Segmented value={cfg.mode} onChange={v => update({ mode: v })}
          options={[{ value: "demo", label: t("ai.settings.mode.demo") }, { value: "local", label: t("ai.settings.mode.local") }]} />
        <div style={{ fontSize: 12, color: "var(--color-text-3)", lineHeight: 1.5, marginTop: 9 }}>{t("ai.settings.default.note")}</div>
        {cfg.mode === "local" && (
          <div className="q-fade" style={{ display: "flex", flexDirection: "column", gap: 12, marginTop: 12 }}>
            <Field label={t("ai.settings.url")} hint={t("ai.settings.url.hint")}>
              <Input value={cfg.url} onChange={e => update({ url: e.target.value })} mono placeholder="http://localhost:11434" />
            </Field>
            <Field label={t("ai.settings.model")}>
              <Input value={cfg.model} onChange={e => update({ model: e.target.value })} mono placeholder={t("ai.settings.model.ph")} />
            </Field>
            <Button variant="secondary" size="md" full icon="refresh" disabled={testing} onClick={test}>
              {testing ? t("ai.settings.testing") : t("ai.settings.test")}
            </Button>
            {diag && <AiDiag diag={diag} />}
          </div>
        )}
      </Section>
    );
  }

  /* Persistent diagnostics panel — replaces the one-line pass/fail toast so a
     presenter can read the REAL failure mode (mixed-content vs CORS/host) and
     the exact shell remediation. sage border = success, terracotta = failure. */
  function AiDiag({ diag }) {
    const ok = diag.ok;
    const reason = diag.reason || "blocked";
    const accent = ok ? "var(--color-primary)" : "var(--color-accent)";
    const mono = { fontFamily: "var(--font-mono)", fontSize: 11.5, lineHeight: 1.6, color: "var(--color-text-2)", background: "var(--color-surface-1)", borderRadius: 8, padding: "8px 10px", whiteSpace: "pre-wrap", wordBreak: "break-word", marginTop: 8 };
    return (
      <div className="q-fade" style={{ borderLeft: "3px solid " + accent, background: "var(--color-surface-0)", borderRadius: 10, padding: "11px 13px" }}>
        <div style={{ fontSize: 13, fontWeight: 600, color: "var(--color-text-1)", marginBottom: 4 }}>{t("ai.settings.diag.title")}</div>
        {ok ? (
          <div style={{ fontSize: 12.5, color: "var(--color-text-2)", lineHeight: 1.5 }}>{t("ai.settings.diag.ok", { count: diag.count, model: diag.model })}</div>
        ) : (
          <div>
            <div style={{ fontSize: 12.5, fontWeight: 600, color: "var(--color-text-1)", marginBottom: 3 }}>{t("ai.settings.diag." + reason + ".title")}</div>
            <div style={{ fontSize: 12.5, color: "var(--color-text-2)", lineHeight: 1.5 }}>{t("ai.settings.diag." + reason + ".body")}</div>
            <div style={mono}>{t("ai.settings.diag." + reason + ".fix")}</div>
            <div style={{ fontSize: 12, color: "var(--color-text-3)", lineHeight: 1.5, marginTop: 8 }}>{t("ai.settings.diag.safety")}</div>
          </div>
        )}
      </div>
    );
  }

  /* Cluster header — labels one of the 4 Settings chapters. Sits in the flex
     column with ~18px top breathing room (none for the first, which the column
     padding already spaces); pulls the gap closer to the block beneath it. */
  function ClusterHead({ children }) {
    return <Kicker style={{ marginTop: 18, marginBottom: -4 }}>{children}</Kicker>;
  }

  function Section({ label, children }) {
    return (
      <div>
        <Kicker style={{ marginBottom: 10 }}>{label}</Kicker>
        <Card pad={16}>{children}</Card>
      </div>
    );
  }

  function PMRow({ icon, title, sub, checked, onToggle, children, sage }) {
    return (
      <div style={{ border: "1.4px solid var(--color-border-strong)", borderRadius: 12, overflow: "hidden" }}>
        <div style={{ display: "flex", alignItems: "center", gap: 12, padding: "13px 14px" }}>
          <div style={{ width: 36, height: 36, borderRadius: 10, background: sage ? "var(--color-primary-muted)" : "var(--color-surface-1)", color: sage ? "var(--color-primary)" : "var(--color-text-2)", display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0 }}><Icon name={icon} size={17} /></div>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ fontSize: 14, fontWeight: 600 }}>{title}</div>
            <div style={{ fontSize: 12, color: "var(--color-text-3)", marginTop: 1 }}>{sub}</div>
          </div>
          <Q.Toggle checked={checked} onChange={onToggle} label={title} />
        </div>
        {checked && children && (
          <div className="q-fade" style={{ padding: "4px 14px 14px", borderTop: "1px solid var(--color-border)" }}>
            <div style={{ paddingTop: 12 }}>{children}</div>
          </div>
        )}
      </div>
    );
  }

  /* ── ListManager — one reusable add/edit/delete/reset primitive ──
     Generalizes the bespoke tax add/delete block; reused for taxes, expense
     categories and currencies. Pure-presentational: it emits the FULL next
     array via onChange (parent writes through setDb → db.config.X), never
     touching the db itself. `keyOf` defaults to item.id; `makeItem` builds a
     new row from the inline form values; `validate` gates the add. */
  function ListManager({ items, onChange, fields, defaults, labelFor, iconFor, keyOf, makeItem, validate, invalidMsg, addedMsg, deletedMsg }) {
    const list = items || [];
    const idOf = keyOf || (it => it.id);
    const blank = () => fields.reduce((a, f) => (a[f.key] = "", a), {});
    const [form, setForm] = useState(blank);
    /* Two-step inline confirm replaces the old fire-on-click destruction:
       deleting a row or resetting to defaults wipes user data with no undo,
       so the first click only ARMS (confirmDel = the row key, confirmReset =
       true) and a second, explicit Confirm commits. Any other destructive
       intent disarms the rest, so only one thing is ever armed at a time. */
    const [confirmDel, setConfirmDel] = useState(null);
    const [confirmReset, setConfirmReset] = useState(false);
    const disarm = () => { setConfirmDel(null); setConfirmReset(false); };
    const add = () => {
      if (validate && !validate(form)) { Q.toast(invalidMsg || t("set.list.invalid"), "alert", "var(--color-accent)"); return; }
      const item = makeItem(form);
      /* keep keys unique (e.g. currency code) — replace an existing match */
      const next = [...list.filter(x => idOf(x) !== idOf(item)), item];
      onChange(next);
      setForm(blank());
      Q.toast(addedMsg || t("set.list.added"), "check");
    };
    const remove = (idx) => {
      onChange(list.filter((_, i) => i !== idx));
      disarm();
      Q.toast(deletedMsg || t("set.list.deleted"), "check");
    };
    const reset = () => { onChange(defaults); disarm(); Q.toast(t("set.list.reset.ok"), "refresh"); };
    /* shared confirm-pair styling — a quiet Cancel + a terracotta Confirm */
    const confirmBtn = { height: 32, padding: "0 12px", borderRadius: 8, border: "none", fontSize: 12.5, fontWeight: 600, cursor: "pointer", display: "inline-flex", alignItems: "center", gap: 5 };
    return (
      <div>
        <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
          {list.length === 0 && <div style={{ fontSize: 12.5, color: "var(--color-text-3)", padding: "6px 2px" }}>{t("set.list.empty")}</div>}
          {list.map((it, idx) => {
            const key = idOf(it) || idx;
            const armed = confirmDel === key;
            return (
            <div key={key} style={{ display: "flex", alignItems: "center", gap: 8, padding: "6px 10px", background: "var(--color-surface-1)", borderRadius: 8 }}>
              {iconFor && <Icon name={iconFor(it) || "tag"} size={15} color="var(--color-text-2)" />}
              <span style={{ fontSize: 13, fontWeight: 500, flex: 1, minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{labelFor(it)}</span>
              {armed ? (
                <div className="q-fade" style={{ display: "flex", gap: 6, flexShrink: 0 }}>
                  <button onClick={() => setConfirmDel(null)} style={{ ...confirmBtn, background: "var(--color-surface-2)", color: "var(--color-text-2)" }}>{t("g.cancel")}</button>
                  <button onClick={() => remove(idx)} style={{ ...confirmBtn, background: "var(--color-accent)", color: "#fff" }}>
                    <Icon name="trash" size={13} />{t("g.delete")}
                  </button>
                </div>
              ) : (
                <button aria-label={t("g.delete")} onClick={() => { setConfirmReset(false); setConfirmDel(key); }} style={{ background: "none", border: "none", color: "var(--color-accent)", padding: 4, cursor: "pointer", flexShrink: 0 }}>
                  <Icon name="trash" size={14} />
                </button>
              )}
            </div>
            );
          })}
        </div>
        {/* Inputs wrap on a narrow phone so the Add button is never pushed
            off the right edge; each field cell carries minWidth:0 so flex
            can actually shrink it, and Add becomes full-width on its own
            wrapped row (flexBasis 100%). */}
        <div style={{ display: "flex", flexWrap: "wrap", gap: 8, marginTop: 12, alignItems: "flex-end" }}>
          {fields.map(f => (
            <div key={f.key} style={{ flex: f.flex || 1, minWidth: f.type === "number" ? 96 : 120 }}>
              <Field label={f.label} style={{ margin: 0 }}>
                <Input value={form[f.key]} onChange={e => setForm(s => ({ ...s, [f.key]: e.target.value }))}
                  type={f.type === "number" ? "number" : "text"} step={f.step} mono={f.type === "number"} placeholder={f.placeholder} style={{ height: 40 }} />
              </Field>
            </div>
          ))}
          <button onClick={add} style={{ height: 40, padding: "0 16px", borderRadius: 8, border: "none", background: "var(--color-primary)", color: "var(--color-primary-fg)", fontSize: 13, fontWeight: 600, cursor: "pointer", display: "inline-flex", alignItems: "center", justifyContent: "center", flex: "1 1 100%", minWidth: 0 }}>
            <Icon name="plus" size={15} style={{ marginRight: 6 }} />{t("g.add")}
          </button>
        </div>
        {confirmReset ? (
          <div className="q-fade" style={{ display: "flex", gap: 6, marginTop: 10, alignItems: "center", flexWrap: "wrap" }}>
            <span style={{ fontSize: 12, color: "var(--color-text-2)", flex: 1, minWidth: 120 }}>{t("set.list.reset")}?</span>
            <button onClick={() => setConfirmReset(false)} style={{ ...confirmBtn, background: "var(--color-surface-2)", color: "var(--color-text-2)" }}>{t("g.cancel")}</button>
            <button onClick={reset} style={{ ...confirmBtn, background: "var(--color-accent)", color: "#fff" }}>
              <Icon name="refresh" size={13} />{t("set.list.reset")}
            </button>
          </div>
        ) : (
          <button onClick={() => { setConfirmDel(null); setConfirmReset(true); }} style={{ marginTop: 10, background: "none", border: "none", color: "var(--color-text-3)", fontSize: 12, fontWeight: 600, cursor: "pointer", padding: "2px 0", display: "inline-flex", alignItems: "center", gap: 5 }}>
            <Icon name="refresh" size={13} />{t("set.list.reset")}
          </button>
        )}
      </div>
    );
  }

  window.QScreens = Object.assign(window.QScreens || {}, { Settings });
})();
