/* Översikt (redesign 2026-09).
 *
 * Monteras "bare" av rxWorkspaces i index.html och renderar därför sin egen
 * rubrik. Varje datakälla hämtas för sig med useRxFetch, så att en trasig
 * endpoint bara påverkar sitt eget kort.
 *
 * Källor (se src/routes):
 *   /v1/admin/auth/me                      admin.auth.ts      username
 *   /v1/admin/dashboard                    admin.dashboard.ts summary, trends, upcomingDeliveries,
 *                                                             factoringBasis, deliveryCapacity,
 *                                                             topCustomers, recentOrders, deliveryDateCoverage
 *   /v1/admin/finance/pipeline-funnel      admin.finance.ts   stages (Shopify-belopp exkl. moms)
 *   /v1/admin/finance/liquidity-forecast   admin.finance.ts   summary (inkl. moms)
 *   /v1/admin/fortnox/summary              admin.fortnox.ts   ar, ap, netPosition, lastSync (inkl. moms)
 *   /v1/admin/fortnox/intelligence         admin.fortnox.ts   dso, dpo, ccc, cashflow, arByCustomer
 *   /v1/admin/fortnox/reconciliation-gaps  admin.fortnox.ts   count (max 200), gaps
 *   /v1/admin/reports/atp                  admin.reports.ts   skus, atRiskCount
 *   /v1/admin/opter/deliveries             admin.opter.ts     deliveries[].shopifyId/status
 *   /v1/admin/opter/unmatched              admin.opter.ts     count
 *   /v1/admin/sync-health                  admin.syncHealth.ts jobs
 */

/* ── Hjälpare ───────────────────────────────────────────── */
function rxOvwIso(d) {
  return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
}

function rxOvwAddDays(d, n) {
  const x = new Date(d.getFullYear(), d.getMonth(), d.getDate());
  x.setDate(x.getDate() + n);
  return x;
}

/** sync_health och Fortnox skriver UTC utan zon ('2026-09-23 06:00:00'). */
function rxOvwUtc(v) {
  if (!v) return null;
  let s = String(v);
  if (/^\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}(:\d{2}(\.\d+)?)?$/.test(s)) s = s.replace(' ', 'T') + 'Z';
  const d = new Date(s);
  return isNaN(d) ? null : d;
}

/** '08:00' om idag, annars '22/9 08:00'. */
function rxOvwStamp(v) {
  const d = rxOvwUtc(v);
  if (!d) return '';
  const hm = `${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`;
  if (rxOvwIso(d) === rxOvwIso(new Date())) return hm;
  return `${d.getDate()}/${d.getMonth() + 1} ${hm}`;
}

function rxOvwCap(s) { return s ? s.charAt(0).toUpperCase() + s.slice(1) : ''; }

function rxOvwName(username) {
  if (!username) return '';
  const base = String(username).split('@')[0].split(/[._\s-]/)[0];
  return rxOvwCap(base);
}

function rxOvwGreeting(h) {
  if (h < 12) return 'God morgon';
  if (h < 18) return 'God eftermiddag';
  return 'God kväll';
}

function rxOvwPct(a, b) { return b > 0 ? Math.round(a / b * 100) : 0; }

function rxOvwDec(v) {
  return (Number(v) || 0).toLocaleString('sv-SE', { minimumFractionDigits: 1, maximumFractionDigits: 1 });
}

function rxOvwFulfilled(o) { return String(o && o.fulfillmentStatus || '').toLowerCase() === 'fulfilled'; }

/** Status från Shopify fulfillment, det som visas som "Från lager". */
function rxOvwShip(o, due) {
  const ff = String(o && o.fulfillmentStatus || '').toLowerCase();
  if (ff === 'fulfilled') return { label: 'Skickad', tone: 'ok' };
  if (ff.indexOf('partial') >= 0) return { label: 'Delvis skickad', tone: 'warn' };
  return { label: 'Ej skickad', tone: due ? 'warn' : 'neutral' };
}

/** Största negativa saldo under horisonten, som positivt tal. */
function rxOvwShortage(s) {
  let min = Number(s.currentStock) || 0;
  (s.dailyEvents || []).forEach(e => { if (e.runningBalance < min) min = e.runningBalance; });
  if (min >= 0 && s.netAvailable < 0) min = s.netAvailable;
  return min < 0 ? -min : 0;
}

function rxOvwIsoWeek(d) {
  const t = new Date(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate()));
  const day = t.getUTCDay() || 7;
  t.setUTCDate(t.getUTCDate() + 4 - day);
  const y0 = new Date(Date.UTC(t.getUTCFullYear(), 0, 1));
  return Math.ceil(((t - y0) / 864e5 + 1) / 7);
}

function rxOvwMonday(d) {
  return rxOvwAddDays(d, -((d.getDay() + 6) % 7));
}

const rxOvwPalette = ['#4338CA', '#8D86EA', '#B3AEF0', '#CFCBF5', '#E4E1F9'];

const rxOvwWeekdayLong = ['Söndag', 'Måndag', 'Tisdag', 'Onsdag', 'Torsdag', 'Fredag', 'Lördag'];
const rxOvwWeekdayShort = ['Sön', 'Mån', 'Tis', 'Ons', 'Tor', 'Fre', 'Lör'];

/* ── Små byggstenar ─────────────────────────────────────── */
function RxOvwBars({ values, highlight }) {
  if (!Array.isArray(values) || values.length === 0) return null;
  const max = Math.max.apply(null, values.map(v => Number(v) || 0));
  if (!(max > 0)) return null;
  const hi = highlight == null ? values.length - 1 : highlight;
  return <div aria-hidden="true" style={{ display: 'flex', alignItems: 'flex-end', gap: 4, height: 36, marginTop: 8 }}>
    {values.map((v, i) => <div key={i} style={{ flexGrow: 1, height: Math.round(4 + (Number(v) || 0) / max * 32), borderRadius: 3, background: i === hi ? 'var(--rx-accent)' : '#DAD7F7' }} />)}
  </div>;
}

function RxOvwKpi({ label, value, sub, bars, highlight, barsLabel }) {
  return <article className="rx-panel" style={{ gap: 4 }}>
    <div style={{ fontSize: 13, fontWeight: 500, color: 'var(--rx-ink-3)' }}>{label}</div>
    <div style={{ fontSize: 28, fontWeight: 650, letterSpacing: '-0.02em', lineHeight: 1.15, fontVariantNumeric: 'tabular-nums' }}>{value}</div>
    {sub && <div style={{ fontSize: 13, color: 'var(--rx-ink-3)', fontVariantNumeric: 'tabular-nums' }}>{sub}</div>}
    <RxOvwBars values={bars} highlight={highlight} />
    {barsLabel && Array.isArray(bars) && bars.some(v => v > 0) && <div className="rx-faint" style={{ fontSize: 11.5 }}>{barsLabel}</div>}
  </article>;
}

function RxOvwActionCard({ tone, icon, title, children, actions }) {
  const warn = tone === 'warn';
  return <article style={{ background: '#fff', border: `1px solid ${warn ? 'var(--rx-warn-border)' : 'var(--rx-border)'}`, borderRadius: 14, padding: '18px 20px', display: 'flex', flexDirection: 'column', gap: 10, minWidth: 0 }}>
    <div style={{ display: 'flex', alignItems: 'flex-start', gap: 10 }}>
      <div aria-hidden="true" style={{ width: 30, height: 30, flexShrink: 0, borderRadius: 8, background: warn ? '#FDF1DC' : 'var(--rx-accent-soft)', color: warn ? '#9A5B00' : 'var(--rx-accent-ink)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>{icon}</div>
      <h3 style={{ margin: 0, fontSize: 14.5, fontWeight: 600, lineHeight: 1.35, paddingTop: 5 }}>{title}</h3>
    </div>
    <div style={{ fontSize: 13.5, lineHeight: 1.5, color: 'var(--rx-ink-2)' }}>{children}</div>
    {actions && actions.length > 0 && <div style={{ display: 'flex', gap: 16, marginTop: 'auto', flexWrap: 'wrap' }}>
      {actions.map((a, i) => <button key={i} type="button" className="rx-link" onClick={a.onClick}
        style={{ fontSize: 13.5, color: i === 0 ? undefined : 'var(--rx-ink-2)' }}>{a.label}</button>)}
    </div>}
  </article>;
}

function RxOvwStat({ label, value }) {
  return <div style={{ display: 'flex', flexDirection: 'column', gap: 2, minWidth: 0 }}>
    <span style={{ fontSize: 12.5, color: 'var(--rx-ink-3)' }}>{label}</span>
    <span style={{ fontSize: 16, fontWeight: 600, fontVariantNumeric: 'tabular-nums' }}>{value}</span>
  </div>;
}

function RxOvwSwatch({ color }) {
  return <span aria-hidden="true" style={{ width: 10, height: 10, borderRadius: 3, background: color, display: 'inline-block', flexShrink: 0 }} />;
}

/* ── Sidan ──────────────────────────────────────────────── */
function RxOverviewPage({ onNavigate, onOpenOrder }) {
  const nav = r => { if (onNavigate) onNavigate(r); };
  const openOrder = id => { if (onOpenOrder) onOpenOrder(id); };

  const today = React.useMemo(() => rxToday(), []);
  const todayIso = rxOvwIso(today);
  const opterPath = React.useMemo(() =>
    `/v1/admin/opter/deliveries?delivery_from=${rxOvwIso(today)}&delivery_to=${rxOvwIso(rxOvwAddDays(today, 13))}&limit=500`, [today]);

  const me = useRxFetch('/v1/admin/auth/me');
  const dash = useRxFetch('/v1/admin/dashboard');
  const funnel = useRxFetch('/v1/admin/finance/pipeline-funnel');
  const fx = useRxFetch('/v1/admin/fortnox/summary');
  const intel = useRxFetch('/v1/admin/fortnox/intelligence');
  const liq = useRxFetch('/v1/admin/finance/liquidity-forecast?weeks=8');
  const atp = useRxFetch('/v1/admin/reports/atp');
  const gaps = useRxFetch('/v1/admin/fortnox/reconciliation-gaps');
  const opter = useRxFetch(opterPath);
  const health = useRxFetch('/v1/admin/sync-health');
  const unmatched = useRxFetch('/v1/admin/opter/unmatched?limit=500');

  const [selDate, setSelDate] = React.useState(null);

  const d = dash.data;
  const s = d && d.summary;

  // Dagens leveranser
  const groups = (d && d.upcomingDeliveries) || [];
  const todayGroup = groups.find(g => g.deliveryDate === todayIso) || null;
  const todayOrders = todayGroup ? todayGroup.orders : [];
  const todayShipped = todayOrders.filter(rxOvwFulfilled);
  const todayPending = todayOrders.filter(o => !rxOvwFulfilled(o));

  // Leveransrisk
  const riskSkus = React.useMemo(() => {
    if (!atp.data || !Array.isArray(atp.data.skus)) return [];
    return atp.data.skus.filter(x => x.atRisk)
      .map(x => ({ ...x, shortage: rxOvwShortage(x) }))
      .sort((a, b) => b.shortage - a.shortage);
  }, [atp.data]);
  const worst = riskSkus[0] || null;
  const in7Iso = rxOvwIso(rxOvwAddDays(today, 7));
  const riskWithin7 = riskSkus.filter(x => x.riskDate && x.riskDate <= in7Iso).length;

  // Avstämning
  const gapCount = gaps.data ? gaps.data.count || 0 : 0;
  const gapCapped = gapCount >= 200;
  const gapLabel = gapCapped ? 'Minst 200' : fmtInt(gapCount);
  const gapSum = gaps.data ? (gaps.data.gaps || []).reduce((a, g) => a + (Number(g.totalPrice) || 0), 0) : 0;

  const fortnoxJob = health.data && health.data.jobs ? health.data.jobs.fortnox_ar_ap : null;
  const fortnoxStamp = rxOvwStamp((fortnoxJob && fortnoxJob.finishedAt) || (fx.data && fx.data.lastSync));

  const stages = funnel.data && Array.isArray(funnel.data.stages) ? funnel.data.stages : null;
  const stage = k => (stages || []).find(x => x.key === k) || { count: 0, sumSek: 0 };
  const invoicedZero = !!stages && stage('invoiced').count === 0 && stage('delivered').count > 0;

  /* ── Sammanfattande mening ── */
  const sentence = [];
  if (d) {
    if (todayGroup) {
      sentence.push(<span key="d">
        <strong style={{ color: 'var(--rx-ink)', fontWeight: 600 }}>{rxPlural(todayGroup.totalOrders, 'leverans', 'leveranser')} idag</strong>
        {' '}för {fmtSek(todayGroup.totalRevenueSek)} exkl. moms: {fmtInt(todayShipped.length)} skickade, {fmtInt(todayPending.length)} väntar.
      </span>);
    } else {
      sentence.push(<span key="d">Inga leveranser idag.</span>);
    }
  }
  if (atp.data && riskSkus.length > 0) {
    sentence.push(<span key="a">{rxPlural(riskSkus.length, 'artikel räcker', 'artiklar räcker')} inte till lagda ordrar.</span>);
  }
  if (fx.data && fx.data.ar && fx.data.ar.overdueBalance > 0) {
    sentence.push(<span key="f">{rxShortSek(fx.data.ar.overdueBalance)} i kundfordringar har förfallit (inkl. moms).</span>);
  }

  /* ── Kräver åtgärd ── */
  const cards = [];
  if (worst) {
    const inb = Number(worst.inboundExpected) || 0;
    cards.push(<RxOvwActionCard key="atp" tone="warn"
      icon={<Icon d="M12 3l9 5v8l-9 5-9-5V8zM3 8l9 5 9-5M12 13v8" />}
      title={`${rxPlural(riskSkus.length, 'artikel räcker', 'artiklar räcker')} inte till lagda ordrar`}
      actions={[{ label: 'Leveransrisk →', onClick: () => nav('stock') }]}>
      Värst: {worst.title || worst.sku}, {fmtInt(worst.currentStock)} i lager, {fmtInt(worst.committedOrders)} i order,
      {' '}{inb > 0 ? `${fmtInt(inb)} på inleverans` : 'ingen inleverans'}.
      {riskWithin7 > 0 && <> {rxPlural(riskWithin7, 'artikel', 'artiklar')} tar slut inom 7 dagar.</>}
    </RxOvwActionCard>);
  }
  if (gaps.data && gapCount > 0) {
    cards.push(<RxOvwActionCard key="gaps" tone="warn"
      icon={<Icon d="M10 13a5 5 0 007.5.5l3-3a5 5 0 00-7-7l-1.7 1.7M14 11a5 5 0 00-7.5-.5l-3 3a5 5 0 007 7l1.7-1.7" />}
      title={`${gapLabel} levererade ordrar saknar avstämd kundfaktura`}
      actions={[{ label: 'Visa avvikelser →', onClick: () => nav('finance/invoices') }]}>
      Leveransdatum de senaste 180 dagarna{gapCapped ? ', de 200 senaste' : ''}: {fmtSek(gapSum)} exkl. moms.
      {fortnoxStamp && <> Fortnox uppdaterades {fortnoxStamp}.</>}
      {invoicedZero && <> Därför visar orderflödet inget fakturerat.</>}
    </RxOvwActionCard>);
  }
  if (fx.data && fx.data.ar && fx.data.ar.overdueBalance > 0) {
    let oldest = null;
    if (intel.data && Array.isArray(intel.data.arByCustomer)) {
      intel.data.arByCustomer.forEach(c => {
        if (c.oldestDue && c.oldestDue < todayIso && (!oldest || c.oldestDue < oldest.oldestDue)) oldest = c;
      });
    }
    cards.push(<RxOvwActionCard key="ar" tone="info"
      icon={<Icon d="M12 21a9 9 0 100-18 9 9 0 000 18zM12 7v5l3 2" />}
      title={`${fmtSek(fx.data.ar.overdueBalance)} i förfallna kundfordringar`}
      actions={[{ label: 'Kundreskontra →', onClick: () => nav('finance/reskontra') }]}>
      {rxPlural(fx.data.ar.overdueCount, 'faktura', 'fakturor')}, inkl. moms.
      {oldest && <> Äldst förfallen hos {oldest.customerName} (förföll {rxDay(oldest.oldestDue)}).</>}
    </RxOvwActionCard>);
  }
  if (todayPending.length > 0) {
    const one = todayPending.length === 1 ? todayPending[0] : null;
    cards.push(<RxOvwActionCard key="today" tone="info"
      icon={<Icon d="M3 7h11v9H3zM14 10h4l3 3v3h-7M7 20a1.8 1.8 0 100-3.6 1.8 1.8 0 000 3.6zM17 20a1.8 1.8 0 100-3.6 1.8 1.8 0 000 3.6z" />}
      title={one ? `${one.shopifyName} ska levereras idag, ej skickad` : `${fmtInt(todayPending.length)} av dagens ${fmtInt(todayOrders.length)} leveranser är inte skickade`}
      actions={one
        ? [{ label: 'Öppna order →', onClick: () => openOrder(one.shopifyId) }, { label: 'Leveranser', onClick: () => nav('deliveries') }]
        : [{ label: 'Leveranser →', onClick: () => nav('deliveries') }, { label: 'Alla ordrar', onClick: () => nav('orders') }]}>
      {one
        ? <>{one.clientName || one.emailDomain}{one.poNumber ? <> · kundens ordernr <span className="rx-mono">{one.poNumber}</span></> : null}.
            {todayShipped.length > 0 && <> Övriga {fmtInt(todayShipped.length)} av dagens leveranser är skickade.</>}</>
        : <>Ej skickade från lager enligt Shopify: {todayPending.slice(0, 4).map((o, i) => <React.Fragment key={o.shopifyId}>
            {i > 0 && ', '}
            <button type="button" className="rx-link rx-mono" onClick={() => openOrder(o.shopifyId)}>{o.shopifyName}</button>
          </React.Fragment>)}{todayPending.length > 4 ? ` och ${fmtInt(todayPending.length - 4)} till` : ''}.</>}
    </RxOvwActionCard>);
  }
  const actionSources = [['Lager', atp], ['Avstämning', gaps], ['Fortnox', fx], ['Ordrar', dash]];
  const actionLoading = actionSources.some(([, x]) => x.loading);
  const actionFailed = actionSources.filter(([, x]) => x.error).map(([n]) => n);

  /* ── Leveransremsa, 14 dagar ── */
  const fbMap = new Map(((d && d.factoringBasis && d.factoringBasis.rolling7Days) || []).map(x => [x.date, x]));
  const groupMap = new Map(groups.map(g => [g.deliveryDate, g]));
  const stripDays = [];
  for (let i = 0; i < 14; i++) {
    const day = rxOvwAddDays(today, i);
    const iso = rxOvwIso(day);
    const fb = fbMap.get(iso);
    const g = groupMap.get(iso);
    const deliv = fb ? fb.fulfilled.sumSek : 0;
    const undeliv = fb ? fb.notFulfilled.sumSek : 0;
    stripDays.push({
      iso, day, deliv, undeliv,
      delivN: fb ? fb.fulfilled.count : 0,
      n: g ? g.totalOrders : (fb ? fb.total.count : 0),
      weekend: day.getDay() === 0 || day.getDay() === 6,
      group: g || null,
    });
  }
  const stripMax = Math.max(1, ...stripDays.map(x => x.deliv + x.undeliv));
  const firstWithOrders = stripDays.find(x => x.n > 0);
  const activeIso = selDate || (firstWithOrders ? firstWithOrders.iso : todayIso);
  const sel = stripDays.find(x => x.iso === activeIso) || stripDays[0];
  const selTotal = sel.deliv + sel.undeliv;
  const selOrders = sel.group ? sel.group.orders : [];
  const opterMap = new Map(((opter.data && opter.data.deliveries) || []).map(x => [String(x.shopifyId), x]));
  const showOpter = !opter.error;
  const selRel = rxDay(sel.iso, { relative: true });
  const selCustomers = [...new Set(selOrders.map(o => o.clientName || o.emailDomain).filter(Boolean))];

  /* ── Faktureringsunderlag, 7 bankdagar ── */
  const bankDays = [];
  for (let i = 0; bankDays.length < 7 && i < 14; i++) {
    const day = rxOvwAddDays(today, i);
    if (day.getDay() === 0 || day.getDay() === 6) continue;
    bankDays.push(fbMap.get(rxOvwIso(day)) || null);
  }
  const fbWin = bankDays.reduce((a, x) => x ? {
    fulN: a.fulN + x.fulfilled.count, fulS: a.fulS + x.fulfilled.sumSek,
    penN: a.penN + x.notFulfilled.count, penS: a.penS + x.notFulfilled.sumSek,
    totN: a.totN + x.total.count, totS: a.totS + x.total.sumSek,
  } : a, { fulN: 0, fulS: 0, penN: 0, penS: 0, totN: 0, totS: 0 });
  const mtd = d && d.factoringBasis ? d.factoringBasis.monthToDate : null;

  /* ── Kommande 7 dagar: bekräftade ── */
  const in6Iso = rxOvwIso(rxOvwAddDays(today, 6));
  const next7 = groups.filter(g => g.deliveryDate >= todayIso && g.deliveryDate <= in6Iso);
  const next7Confirmed = next7.reduce((a, g) => a + (g.confirmedOrders || 0), 0);
  const next7Prelim = next7.reduce((a, g) => a + (g.preliminaryOrders || 0), 0);

  /* ── Kassaflöde ── */
  const thisMonday = rxOvwMonday(today);
  const cashRows = [];
  if (intel.data && Array.isArray(intel.data.cashflow)) {
    intel.data.cashflow.forEach(w => {
      if (w.week === 'overdue') {
        if (w.arExpected || w.apDue) cashRows.push({ key: 'overdue', label: 'Förfallet', inS: w.arExpected, outS: w.apDue, net: w.net, overdue: true });
        return;
      }
      const ws = rxParseDate(w.weekStart);
      if (!ws) return;
      const mon = rxOvwMonday(ws);
      if (mon < thisMonday) return;
      cashRows.push({ key: w.week, label: `v${rxOvwIsoWeek(ws)}`, inS: w.arExpected, outS: w.apDue, net: w.net, overdue: false });
    });
  }
  const cashShown = cashRows.filter(r => r.overdue).concat(cashRows.filter(r => !r.overdue).slice(0, 8));
  const cashMax = Math.max(1, ...cashShown.map(r => Math.max(r.inS || 0, r.outS || 0)));
  const cashWeeks = cashShown.filter(r => !r.overdue);
  const cashIn = cashWeeks.reduce((a, r) => a + (r.inS || 0), 0);
  const cashOut = cashWeeks.reduce((a, r) => a + (r.outS || 0), 0);

  /* ── Datum och hälsning ── */
  const now = new Date();
  const dateLine = rxOvwCap(now.toLocaleDateString('sv-SE', { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' }));
  const userName = me.data && me.data.loggedIn ? rxOvwName(me.data.username) : '';
  const monthName = now.toLocaleString('sv-SE', { month: 'long' });

  const panelLink = (label, route) => <button type="button" className="rx-link" onClick={() => nav(route)}>{label}</button>;

  return <div className="rx-ws">
    {/* 1. Rubrik */}
    <section style={{ display: 'flex', alignItems: 'flex-end', gap: 24, flexWrap: 'wrap' }}>
      <div style={{ flex: '1 1 420px', display: 'flex', flexDirection: 'column', gap: 6, minWidth: 0 }}>
        <div style={{ fontSize: 13, color: 'var(--rx-ink-3)' }}>{dateLine}</div>
        <h1 style={{ margin: 0, fontSize: 30, fontWeight: 650, letterSpacing: '-0.02em', color: 'var(--rx-ink)' }}>
          {rxOvwGreeting(now.getHours())}{userName ? `, ${userName}` : ''}
        </h1>
        {sentence.length > 0
          ? <p style={{ margin: '2px 0 0', fontSize: 15, color: 'var(--rx-ink-2)', lineHeight: 1.5, fontVariantNumeric: 'tabular-nums' }}>
              {sentence.map((x, i) => <React.Fragment key={i}>{i > 0 && ' '}{x}</React.Fragment>)}
            </p>
          : dash.loading && <p style={{ margin: '2px 0 0', fontSize: 15, color: 'var(--rx-ink-3)' }}>Hämtar dagens läge…</p>}
      </div>
      <div style={{ display: 'flex', gap: 8 }}>
        <button type="button" className="btn" style={{ height: 40, padding: '0 16px', borderRadius: 10, fontSize: 14 }} onClick={() => nav('finance/trends')}>{I.reports}<span>Försäljning</span></button>
        <button type="button" className="btn btn--primary" style={{ height: 40, padding: '0 16px', borderRadius: 10, fontSize: 14 }} onClick={() => nav('orders')}>{I.orders}<span>Alla ordrar</span></button>
      </div>
    </section>

    {/* 2. Kräver åtgärd */}
    <section aria-labelledby="rx-ovw-att" style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
        <h2 id="rx-ovw-att" className="rx-section-title" style={{ margin: 0 }}>Kräver åtgärd</h2>
        {cards.length > 0 && <span className="rx-count rx-count--warn">{cards.length}</span>}
        {cards.length > 1 && <span style={{ marginLeft: 'auto', fontSize: 13, color: 'var(--rx-ink-3)' }}>Sorterat efter påverkan</span>}
      </div>
      {cards.length > 0 && <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(240px, 1fr))', gap: 16 }}>{cards}</div>}
      {cards.length === 0 && !actionLoading && actionFailed.length === 0 &&
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '12px 16px', background: '#fff', border: '1px solid var(--rx-border)', borderRadius: 12, fontSize: 13.5, color: 'var(--rx-ink-2)' }}>
          <span className="rx-dot" />Inget kräver åtgärd just nu.
        </div>}
      {cards.length === 0 && actionLoading && <RxLoading label="Kontrollerar lager, avstämning och reskontra…" />}
      {actionFailed.length > 0 && <div style={{ fontSize: 12.5, color: 'var(--rx-warn)' }}>
        Kunde inte kontrollera: {actionFailed.join(', ')}.
      </div>}
    </section>

    {/* 3. Nyckeltal */}
    {dash.error
      ? <RxError error={dash.error} onRetry={dash.reload} />
      : !s
        ? <RxLoading label="Hämtar nyckeltal…" />
        : <section aria-label="Nyckeltal" className="rx-grid rx-grid--4">
            <RxOvwKpi label="Ordrar denna vecka" value={fmtInt(s.ordersThisWeek)} sub="Inkomna sedan måndag"
              bars={s.trends && s.trends.orders} barsLabel="Inkomna per dag, senaste 7 dagarna" />
            <RxOvwKpi label={`Omsättning ${monthName}`} value={fmtSek(s.revenueThisMonth)} sub={`${rxPlural(s.ordersThisMonth, 'order', 'ordrar')} · exkl. moms`}
              bars={s.trends && s.trends.revenue} barsLabel="Per dag, senaste 7 dagarna" />
            <RxOvwKpi label="Skickade senaste 7 dagarna" value={fmtInt(((s.trends && s.trends.shipped) || []).reduce((a, b) => a + b, 0))} sub="Per leveransdatum"
              bars={s.trends && s.trends.shipped} barsLabel="Per leveransdatum" />
            <RxOvwKpi label="Kommande 7 dagarna" value={fmtInt(((s.trends && s.trends.upcoming) || []).reduce((a, b) => a + b, 0))}
              sub={`${fmtInt(next7Confirmed)} bekräftade · ${fmtInt(next7Prelim)} preliminära`}
              bars={s.trends && s.trends.upcoming} highlight={0} barsLabel="Idag och sex dagar framåt" />
          </section>}

    {/* 4 och 5. Orderflöde och kundkoncentration */}
    <section className="rx-grid rx-grid--3">
      <RxPanel title={`Orderflöde · ${monthName}`} sub="Ordrar med leveransdatum denna månad till idag" style={{ gridColumn: 'span 2' }}>
        {funnel.error ? <RxError error={funnel.error} onRetry={funnel.reload} />
          : funnel.loading || !stages ? <RxLoading />
          : stage('booked').count === 0 ? <RxEmpty title="Inga ordrar ännu">Inga ordrar med leveransdatum den här månaden hittills.</RxEmpty>
          : (() => {
            const b = stage('booked'), dl = stage('delivered'), inv = stage('invoiced'), pd = stage('paid');
            const unreconciled = invoicedZero && gapCount > 0;
            const cell = { border: '1px solid var(--rx-border)', borderRadius: 12, padding: 16, display: 'flex', flexDirection: 'column', gap: 6, minWidth: 0 };
            const big = { fontSize: 20, fontWeight: 650, letterSpacing: '-0.01em', fontVariantNumeric: 'tabular-nums' };
            const lab = { fontSize: 13, color: 'var(--rx-ink-3)' };
            const meta = { fontSize: 12.5, color: 'var(--rx-ink-2)', fontVariantNumeric: 'tabular-nums' };
            const dPct = rxOvwPct(dl.sumSek, b.sumSek);
            return <>
              <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(150px, 1fr))', gap: 12 }}>
                <div style={cell}>
                  <div style={lab}>Orderingång</div>
                  <div style={big}>{fmtSek(b.sumSek)}</div>
                  <div style={meta}>{rxPlural(b.count, 'order', 'ordrar')}</div>
                  <RxBar pct={100} />
                </div>
                <div style={cell}>
                  <div style={lab}>Levererat</div>
                  <div style={big}>{fmtSek(dl.sumSek)}</div>
                  <div style={meta}>{rxPlural(dl.count, 'order', 'ordrar')}</div>
                  <RxBar pct={dPct} tone="ok" />
                  <div style={meta}>{dPct} % av orderingång{b.count - dl.count > 0 ? ` · ${fmtInt(b.count - dl.count)} ej levererade` : ''}</div>
                </div>
                {unreconciled
                  ? <div style={{ ...cell, borderColor: 'var(--rx-warn-border)', background: 'var(--rx-warn-soft)' }}>
                      <div style={lab}>Fakturerat</div>
                      <div style={{ ...big, color: 'var(--rx-warn)' }}>Ej avstämt</div>
                      <div style={meta}>{gapLabel} levererade ordrar saknar avstämd kundfaktura i Fortnox</div>
                      <div><button type="button" className="rx-link" onClick={() => nav('finance/invoices')}>Visa avvikelser →</button></div>
                    </div>
                  : <div style={cell}>
                      <div style={lab}>Fakturerat</div>
                      <div style={big}>{fmtSek(inv.sumSek)}</div>
                      <div style={meta}>{rxPlural(inv.count, 'order', 'ordrar')}</div>
                      <RxBar pct={rxOvwPct(inv.sumSek, b.sumSek)} />
                      <div style={meta}>{rxOvwPct(inv.sumSek, dl.sumSek)} % av levererat</div>
                    </div>}
                {unreconciled || inv.count === 0
                  ? <div style={cell}>
                      <div style={lab}>Betalt</div>
                      <div style={{ ...big, color: 'var(--rx-ink-3)' }}>Väntar</div>
                      <div style={meta}>Väntar på fakturering</div>
                    </div>
                  : <div style={cell}>
                      <div style={lab}>Betalt</div>
                      <div style={big}>{fmtSek(pd.sumSek)}</div>
                      <div style={meta}>{rxPlural(pd.count, 'order', 'ordrar')}</div>
                      <RxBar pct={rxOvwPct(pd.sumSek, b.sumSek)} tone="ok" />
                      <div style={meta}>{rxOvwPct(pd.sumSek, inv.sumSek)} % av fakturerat</div>
                    </div>}
              </div>
              <p className="rx-muted" style={{ margin: 0, fontSize: 12.5 }}>
                Belopp exkl. moms (Shopify). Fakturerat och betalt avser ordrar som är kopplade till en kundfaktura i Fortnox.
                {unreconciled && ' Ett steg som inte är avstämt visas som avvikelse, inte som ett tapp.'}
              </p>
            </>;
          })()}
      </RxPanel>

      <RxPanel title="Kundkoncentration" sub="Topp 5, senaste 90 dagarna" actions={panelLink('Kundanalys →', 'customers/analysis')}>
        {dash.error ? <RxError error={dash.error} onRetry={dash.reload} />
          : !d ? <RxLoading />
          : !d.topCustomers || d.topCustomers.length === 0 ? <RxEmpty>Inga ordrar de senaste 90 dagarna.</RxEmpty>
          : <>
            <div aria-hidden="true" style={{ display: 'flex', height: 12, borderRadius: 999, overflow: 'hidden', gap: 2 }}>
              {d.topCustomers.map((c, i) => <div key={c.emailDomain} style={{ width: `${c.sharePct}%`, background: rxOvwPalette[i % rxOvwPalette.length] }} />)}
            </div>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
              {d.topCustomers.map((c, i) => <div key={c.emailDomain} style={{ display: 'flex', alignItems: 'center', gap: 10, fontSize: 13.5 }}>
                <RxOvwSwatch color={rxOvwPalette[i % rxOvwPalette.length]} />
                <span style={{ flex: 1, minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', fontWeight: 500 }}>{c.clientName || c.emailDomain}</span>
                <span className="rx-num rx-muted">{rxShortSek(c.revenueSek)}</span>
                <span className="rx-num" style={{ width: 56, fontWeight: 600 }}>{rxOvwDec(c.sharePct)} %</span>
              </div>)}
            </div>
            <p className="rx-muted" style={{ margin: 0, fontSize: 12.5 }}>Andel av de fem största kundernas omsättning. Exkl. moms.</p>
          </>}
      </RxPanel>
    </section>

    {/* 6. Reskontra och kassaflöde */}
    <section className="rx-grid rx-grid--3">
      <RxPanel title="Reskontra" sub="Fortnox · inkl. moms" actions={panelLink('Reskontra →', 'finance/reskontra')}>
        {fx.error ? <RxError error={fx.error} onRetry={fx.reload} />
          : !fx.data ? <RxLoading />
          : (() => {
            const ar = fx.data.ar || {}, ap = fx.data.ap || {};
            const row = (label, x, tone) => <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
              <div style={{ display: 'flex', justifyContent: 'space-between', gap: 8, fontSize: 13.5 }}>
                <span className="rx-muted">{label}</span>
                <span className="rx-num" style={{ fontWeight: 600, fontSize: 16 }}>{fmtSek(x.openBalance)}</span>
              </div>
              <RxBar pct={rxOvwPct(x.overdueBalance, x.openBalance)} tone={tone} />
              <div style={{ display: 'flex', justifyContent: 'space-between', gap: 8, fontSize: 12.5, fontVariantNumeric: 'tabular-nums' }}>
                <span className="rx-muted">{fmtInt(x.openCount)} öppna</span>
                <span style={{ color: x.overdueBalance > 0 ? 'var(--rx-warn)' : 'var(--rx-ink-3)' }}>{fmtSek(x.overdueBalance)} förfallet ({fmtInt(x.overdueCount)})</span>
              </div>
            </div>;
            const noDso = intel.data && intel.data.collectionRate && intel.data.collectionRate.totalPaid === 0;
            return <>
              {row('Kundfordringar', ar, 'warn')}
              {row('Leverantörsskulder', ap, 'warn')}
              <div style={{ display: 'flex', justifyContent: 'space-between', borderTop: '1px solid var(--rx-divider)', paddingTop: 10, fontSize: 13.5 }}>
                <span className="rx-muted">Netto reskontra</span>
                <span className="rx-num" style={{ fontWeight: 600 }}>{fmtSek(fx.data.netPosition)}</span>
              </div>
              {intel.error ? <div style={{ fontSize: 12.5, color: 'var(--rx-warn)' }}>Kredittider kunde inte hämtas.</div>
                : !intel.data ? <RxLoading label="Hämtar kredittider…" />
                : <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, minmax(0, 1fr))', gap: 8 }}>
                    <RxOvwStat label="Kredittid kunder" value={noDso ? 'Ingen data' : `${rxOvwDec(intel.data.dso)} d`} />
                    <RxOvwStat label="Kredittid leverantörer" value={`${rxOvwDec(intel.data.dpo)} d`} />
                    <RxOvwStat label="Kassacykel" value={`${rxOvwDec(intel.data.ccc)} d`} />
                  </div>}
              {fx.data.lastSync && <div className="rx-faint" style={{ fontSize: 12 }}>Uppdaterad {rxOvwStamp(fx.data.lastSync)}</div>}
            </>;
          })()}
      </RxPanel>

      <RxPanel title="Kassaflöde · kommande veckor" sub="Förfallande kundfakturor mot leverantörsfakturor. Fortnox, inkl. moms."
        style={{ gridColumn: 'span 2' }} actions={panelLink('Kassaflöde och analys →', 'reports/fortnox')}>
        {intel.error ? <RxError error={intel.error} onRetry={intel.reload} />
          : !intel.data ? <RxLoading />
          : cashShown.length === 0 ? <RxEmpty>Inga öppna fakturor förfaller de kommande veckorna.</RxEmpty>
          : <>
            <div style={{ display: 'flex', gap: 16, fontSize: 12.5, color: 'var(--rx-ink-3)' }}>
              <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}><RxOvwSwatch color="var(--rx-accent)" />Inbetalningar</span>
              <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}><RxOvwSwatch color="#C9C4BA" />Utbetalningar</span>
              <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}><RxOvwSwatch color="var(--rx-warn-dot)" />Förfallet</span>
            </div>
            <div role="list" style={{ display: 'grid', gridTemplateColumns: `repeat(${cashShown.length}, minmax(0, 1fr))`, gap: 8, alignItems: 'end' }}>
              {cashShown.map(r => <div role="listitem" key={r.key} aria-label={`${r.label}: in ${fmtSek(r.inS)}, ut ${fmtSek(r.outS)}, netto ${fmtSek(r.net)}`}
                style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 4, minWidth: 0 }}>
                <div aria-hidden="true" style={{ display: 'flex', alignItems: 'flex-end', justifyContent: 'center', gap: 3, height: 140, width: '100%' }}>
                  <div style={{ width: '38%', height: Math.max(2, Math.round((r.inS || 0) / cashMax * 140)), background: r.overdue ? 'var(--rx-warn-dot)' : 'var(--rx-accent)', borderRadius: '4px 4px 0 0' }} />
                  <div style={{ width: '38%', height: Math.max(2, Math.round((r.outS || 0) / cashMax * 140)), background: '#C9C4BA', borderRadius: '4px 4px 0 0' }} />
                </div>
                <span style={{ fontSize: 12, color: 'var(--rx-ink-3)' }}>{r.label}</span>
                <span className="rx-num" style={{ fontSize: 12, fontWeight: 600, color: r.net < 0 ? 'var(--rx-danger)' : 'var(--rx-ok)' }}>{r.net < 0 ? '−' : '+'}{rxShortSek(Math.abs(r.net))}</span>
              </div>)}
            </div>
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(180px, 1fr))', gap: 12, borderTop: '1px solid var(--rx-divider)', paddingTop: 12 }}>
              <RxOvwStat label={`Inbetalningar, ${rxPlural(cashWeeks.length, 'vecka', 'veckor')}`} value={fmtSek(cashIn)} />
              <RxOvwStat label="Utbetalningar till leverantörer" value={fmtSek(cashOut)} />
              <RxOvwStat label="Netto" value={fmtSek(cashIn - cashOut)} />
            </div>
            {liq.data && liq.data.summary && <div style={{ fontSize: 12.5, color: 'var(--rx-ink-2)', background: 'var(--rx-surface-alt)', border: '1px solid var(--rx-divider)', borderRadius: 10, padding: '10px 12px' }}>
              Likviditetsprognos 8 veckor, inklusive ej fakturerade leveranser, moms och amorteringar:
              {' '}in <strong className="rx-num">{rxShortSek(liq.data.summary.totalInflowGrossSek)}</strong>,
              {' '}ut <strong className="rx-num">{rxShortSek(liq.data.summary.totalOutflowSek)}</strong>,
              {' '}förändring <strong className="rx-num">{rxShortSek(liq.data.summary.netLiquiditySek)}</strong>.
            </div>}
            <p className="rx-faint" style={{ margin: 0, fontSize: 12 }}>Innevarande vecka räknas från måndag och kan överlappa Förfallet.</p>
          </>}
      </RxPanel>
    </section>

    {/* 7. Leveranser kommande 14 dagar */}
    <RxPanel title="Leveranser · kommande 14 dagar" sub="Välj en dag för detaljer. Levererat = skickat enligt Shopify. Exkl. moms."
      actions={panelLink('Leveranser →', 'deliveries')}>
      {dash.error ? <RxError error={dash.error} onRetry={dash.reload} />
        : !d ? <RxLoading />
        : <>
          <div style={{ display: 'flex', gap: 16, fontSize: 12.5, color: 'var(--rx-ink-3)' }}>
            <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}><RxOvwSwatch color="var(--rx-accent)" />Levererat</span>
            <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}><RxOvwSwatch color="#CFCBF5" />Ej levererat</span>
          </div>
          <div style={{ overflowX: 'auto', paddingBottom: 2 }}>
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(14, minmax(66px, 1fr))', gap: 6 }}>
              {stripDays.map(x => {
                const selected = x.iso === sel.iso;
                const closed = x.weekend && x.n === 0;
                const dh = x.deliv ? Math.max(3, Math.round(x.deliv / stripMax * 80)) : 0;
                const uh = x.undeliv ? Math.max(3, Math.round(x.undeliv / stripMax * 80)) : 0;
                return <button key={x.iso} type="button" aria-pressed={selected} disabled={closed}
                  aria-label={closed ? `${rxOvwWeekdayLong[x.day.getDay()]} ${x.day.getDate()}/${x.day.getMonth() + 1}, helg` : `${rxOvwWeekdayLong[x.day.getDay()]} ${x.day.getDate()}/${x.day.getMonth() + 1}, ${rxPlural(x.n, 'order', 'ordrar')}, ${fmtSek(x.deliv + x.undeliv)}`}
                  onClick={() => setSelDate(x.iso)}
                  style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 2, padding: '10px 4px 8px', borderRadius: 12, font: 'inherit', cursor: closed ? 'default' : 'pointer',
                    border: selected ? '1px solid var(--rx-accent)' : closed ? '1px solid transparent' : '1px solid var(--rx-border)',
                    boxShadow: selected ? '0 0 0 1px var(--rx-accent)' : 'none',
                    background: selected ? 'var(--rx-accent-softer)' : closed ? 'var(--rx-ground)' : '#fff' }}>
                  <span style={{ fontSize: 12, color: 'var(--rx-ink-3)' }}>{rxOvwWeekdayShort[x.day.getDay()]}</span>
                  <span className="rx-num" style={{ fontSize: 14, fontWeight: 600, color: 'var(--rx-ink)' }}>{x.day.getDate()}/{x.day.getMonth() + 1}</span>
                  <span aria-hidden="true" style={{ display: 'flex', flexDirection: 'column', justifyContent: 'flex-end', height: 84, width: '100%' }}>
                    {uh > 0 && <span style={{ display: 'block', width: '60%', margin: '0 auto', height: uh, background: '#CFCBF5', borderRadius: dh ? '4px 4px 0 0' : 4 }} />}
                    {dh > 0 && <span style={{ display: 'block', width: '60%', margin: '0 auto', height: dh, background: 'var(--rx-accent)', borderRadius: uh ? '0 0 4px 4px' : 4 }} />}
                  </span>
                  <span className="rx-num" style={{ fontSize: 12, fontWeight: 600, color: 'var(--rx-ink)' }}>{closed ? 'Helg' : rxShortSek(x.deliv + x.undeliv)}</span>
                  <span className="rx-num" style={{ fontSize: 11.5, color: 'var(--rx-ink-3)' }}>{closed ? 'Stängt' : rxPlural(x.n, 'order', 'ordrar')}</span>
                </button>;
              })}
            </div>
          </div>

          <div className="rx-split">
            <div className="rx-split__main" style={{ border: '1px solid var(--rx-border)', borderRadius: 12, padding: 16 }}>
              <div style={{ display: 'flex', alignItems: 'baseline', gap: 10, flexWrap: 'wrap' }}>
                <h3 style={{ margin: 0, fontSize: 15, fontWeight: 600 }}>{rxOvwWeekdayLong[sel.day.getDay()]} {sel.day.getDate()}/{sel.day.getMonth() + 1}</h3>
                {selRel && /^(idag|imorgon)$/.test(selRel) && <RxPill tone="info">{rxOvwCap(selRel)}</RxPill>}
                {selCustomers.length > 0 && <span className="rx-muted" style={{ fontSize: 13 }}>{selCustomers.slice(0, 4).join(' · ')}{selCustomers.length > 4 ? ` och ${selCustomers.length - 4} till` : ''}</span>}
              </div>
              <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(110px, 1fr))', gap: 12 }}>
                <RxOvwStat label="Ordrar" value={fmtInt(sel.n)} />
                <RxOvwStat label="Värde" value={fmtSek(selTotal)} />
                <RxOvwStat label="Levererat" value={`${fmtInt(sel.delivN)} st · ${rxShortSek(sel.deliv)}`} />
                <RxOvwStat label="Andel klar" value={selTotal > 0 ? `${rxOvwPct(sel.deliv, selTotal)} %` : 'Ingen'} />
              </div>
              {selOrders.length === 0
                ? <RxEmpty>Inga ordrar med leveransdatum den här dagen.</RxEmpty>
                : <div style={{ overflowX: 'auto' }}>
                  <table className="rx-table">
                    <thead><tr>
                      <th>Order</th>
                      <th>Kund</th>
                      <th>Kundens ordernr</th>
                      <th className="rx-num">Värde</th>
                      <th>Från lager</th>
                      {showOpter && <th>Status i Opter</th>}
                    </tr></thead>
                    <tbody>
                      {selOrders.map(o => {
                        const ship = rxOvwShip(o, sel.iso <= todayIso);
                        const op = opterMap.get(String(o.shopifyId));
                        return <tr key={o.shopifyId} className={ship.tone === 'warn' ? 'rx-row--warn' : ''}>
                          <td><button type="button" className="rx-link rx-mono" onClick={() => openOrder(o.shopifyId)} aria-label={`Öppna order ${o.shopifyName}`}>{o.shopifyName}</button>
                            {o.isPreliminary && <span className="rx-faint" style={{ fontSize: 12, marginLeft: 6 }}>preliminär</span>}</td>
                          <td>{o.clientName || o.emailDomain}</td>
                          <td className="rx-mono rx-muted">{o.poNumber || ''}</td>
                          <td className="rx-num">{fmtSek(o.totalPrice)}</td>
                          <td><RxPill tone={ship.tone}>{ship.label}</RxPill></td>
                          {showOpter && <td>{opter.loading ? <span className="rx-faint">Hämtar…</span>
                            : op ? <RxDeliveryPill status={op.status} label={op.statusLabel} />
                            : <span className="rx-faint">Ingen status</span>}</td>}
                        </tr>;
                      })}
                    </tbody>
                  </table>
                </div>}
              {opter.error && <div className="rx-faint" style={{ fontSize: 12 }}>Status från Opter kunde inte hämtas och visas inte.</div>}
            </div>

            <div className="rx-split__side" style={{ border: '1px solid var(--rx-border)', borderRadius: 12, padding: 16, gap: 12 }}>
              <div>
                <h3 style={{ margin: 0, fontSize: 15, fontWeight: 600 }}>Faktureringsunderlag</h3>
                <div className="rx-muted" style={{ fontSize: 12.5 }}>Rullande 7 bankdagar · underlag för fakturaköp · exkl. moms</div>
              </div>
              {[['Levererat', 'var(--rx-accent)', fbWin.fulS, fbWin.fulN], ['Ej levererat', '#CFCBF5', fbWin.penS, fbWin.penN], ['Totalt', null, fbWin.totS, fbWin.totN]].map(([label, color, sum, n]) =>
                <div key={label} style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 13.5, fontWeight: color ? 400 : 600, borderTop: color ? 0 : '1px solid var(--rx-divider)', paddingTop: color ? 0 : 8 }}>
                  {color ? <RxOvwSwatch color={color} /> : <span style={{ width: 10 }} />}
                  <span style={{ flex: 1 }}>{label}</span>
                  <span className="rx-num">{fmtSek(sum)}</span>
                  <span className="rx-num rx-muted" style={{ width: 48, textAlign: 'right' }}>{fmtInt(n)} st</span>
                </div>)}
              <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
                <RxBar pct={rxOvwPct(fbWin.fulS, fbWin.totS)} />
                <span className="rx-muted" style={{ fontSize: 12.5 }}>{rxOvwPct(fbWin.fulS, fbWin.totS)} % av värdet faktureringsbart</span>
              </div>
              {mtd && mtd.total.count > 0 && <div style={{ fontSize: 12.5, color: 'var(--rx-ink-2)', borderTop: '1px solid var(--rx-divider)', paddingTop: 10, display: 'flex', flexDirection: 'column', gap: 2 }}>
                <span style={{ fontWeight: 600 }}>{rxOvwCap(monthName)} hittills: {rxOvwPct(mtd.fulfilled.count, mtd.total.count)} % levererat</span>
                <span className="rx-num rx-muted">{fmtSek(mtd.fulfilled.sumSek)} ({fmtInt(mtd.fulfilled.count)}) av {fmtSek(mtd.total.sumSek)} ({fmtInt(mtd.total.count)})</span>
              </div>}
            </div>
          </div>
        </>}
    </RxPanel>

    {/* 8. Leveransrisk, senaste ordrar, datakvalitet */}
    <section className="rx-grid rx-grid--3">
      <RxPanel title="Leveransrisk" sub={atp.data ? `${fmtInt(riskSkus.length)} av ${fmtInt((atp.data.skus || []).length)} artiklar, ${fmtInt(atp.data.days)} dagar` : null}
        actions={panelLink('Visa alla →', 'stock')}>
        {atp.error ? <RxError error={atp.error} onRetry={atp.reload} />
          : !atp.data ? <RxLoading />
          : riskSkus.length === 0 ? <RxEmpty title="Ingen brist">Lager och inleveranser räcker till alla lagda ordrar.</RxEmpty>
          : <>
            <div style={{ overflowX: 'auto' }}>
              <table className="rx-table">
                <thead><tr>
                  <th>Artikel</th>
                  <th className="rx-num">Lager</th>
                  <th className="rx-num">Reserverat</th>
                  <th className="rx-num">Brist</th>
                </tr></thead>
                <tbody>
                  {riskSkus.slice(0, 6).map(x => <tr key={x.sku}>
                    <td style={{ minWidth: 0 }}>
                      <div style={{ fontWeight: 500 }}>{x.title || x.sku}</div>
                      <div className="rx-mono rx-muted">{x.sku}{x.vendor ? ` · ${x.vendor}` : ''}</div>
                    </td>
                    <td className="rx-num">{fmtInt(x.currentStock)}</td>
                    <td className="rx-num">{fmtInt(x.committedOrders)}</td>
                    <td className="rx-num" style={{ color: 'var(--rx-danger)', fontWeight: 600 }}>−{fmtInt(x.shortage)}</td>
                  </tr>)}
                </tbody>
              </table>
            </div>
            {(() => {
              const top = riskSkus.slice(0, 6);
              const withIn = top.filter(x => (Number(x.inboundExpected) || 0) > 0).length;
              return <p className="rx-muted" style={{ margin: 0, fontSize: 12.5 }}>
                {withIn === 0 ? 'Ingen av dem har inleverans registrerad.' : `${fmtInt(withIn)} av dem har inleverans registrerad, men den räcker inte i tid.`}
              </p>;
            })()}
          </>}
      </RxPanel>

      <RxPanel title="Senaste ordrar" actions={panelLink('Alla ordrar →', 'orders')}>
        {dash.error ? <RxError error={dash.error} onRetry={dash.reload} />
          : !d ? <RxLoading />
          : !d.recentOrders || d.recentOrders.length === 0 ? <RxEmpty>Inga ordrar ännu.</RxEmpty>
          : <div style={{ overflowX: 'auto' }}>
            <table className="rx-table">
              <thead><tr><th>Order</th><th>Kund</th><th>Leverans</th></tr></thead>
              <tbody>
                {d.recentOrders.map(o => <tr key={o.shopifyId}>
                  <td><button type="button" className="rx-link rx-mono" onClick={() => openOrder(o.shopifyId)} aria-label={`Öppna order ${o.shopifyName}`}>{o.shopifyName}</button></td>
                  <td>{o.clientName || o.emailDomain}</td>
                  <td className="rx-num" style={{ textAlign: 'left' }}>
                    {o.cancelledAt ? <RxPill tone="danger">Avbruten</RxPill>
                      : o.deliveryDate ? rxDay(o.deliveryDate, { relative: true })
                      : <span style={{ color: 'var(--rx-warn)', fontWeight: 500 }}>Datum saknas</span>}
                  </td>
                </tr>)}
              </tbody>
            </table>
          </div>}
      </RxPanel>

      <RxPanel title="Datakvalitet och uppdatering" actions={panelLink('Systemstatus →', 'system-status')}>
        {d && d.deliveryDateCoverage && (() => {
          const c = d.deliveryDateCoverage;
          const missing = (c.ordersLast90 || 0) - (c.withDeliveryDate || 0);
          return <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 13.5 }}>
              <span>Leveransdatum satt</span>
              <span className="rx-num" style={{ fontWeight: 600 }}>{fmtInt(c.pct)} %</span>
            </div>
            <RxBar pct={c.pct} tone={c.pct >= 70 ? 'ok' : 'warn'} />
            <span className="rx-muted rx-num" style={{ fontSize: 12.5 }}>
              {fmtInt(c.withDeliveryDate)} av {fmtInt(c.ordersLast90)} ordrar, senaste 90 dagarna.{missing > 0 ? ` ${fmtInt(missing)} saknar datum.` : ''}
            </span>
          </div>;
        })()}
        {dash.error && <div style={{ fontSize: 12.5, color: 'var(--rx-warn)' }}>Täckning för leveransdatum kunde inte hämtas.</div>}
        <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
          {(() => {
            const rows = [];
            const jobRow = (key, label) => {
              if (health.error) { rows.push({ key, label, dot: 'warn', text: 'Kunde inte hämtas' }); return; }
              if (!health.data) { rows.push({ key, label, dot: 'neutral', text: 'Hämtar…' }); return; }
              const j = health.data.jobs && health.data.jobs[key];
              if (!j || j.status === 'never') { rows.push({ key, label, dot: 'neutral', text: 'Aldrig körd' }); return; }
              if (j.status === 'error') { rows.push({ key, label, dot: 'danger', text: 'Uppdatering misslyckades', title: j.errorMessage || '' }); return; }
              if (j.status === 'running' || !j.finishedAt) { rows.push({ key, label, dot: 'neutral', text: 'Pågår' }); return; }
              if (j.isStale) { rows.push({ key, label, dot: 'warn', text: `Försenad, senast ${rxOvwStamp(j.finishedAt)}` }); return; }
              rows.push({ key, label, dot: 'ok', text: `Uppdaterad ${rxOvwStamp(j.finishedAt)}` });
            };
            jobRow('shopify_orders', 'Shopify-ordrar');
            jobRow('fortnox_ar_ap', 'Fortnox reskontra');
            if (gaps.error) rows.push({ key: 'gaps', label: 'Avstämning order mot faktura', dot: 'warn', text: 'Kunde inte hämtas' });
            else if (gaps.data) rows.push({ key: 'gaps', label: 'Avstämning order mot faktura', dot: gapCount > 0 ? 'warn' : 'ok', text: gapCount > 0 ? `${gapLabel} avvikelser` : 'Inga avvikelser', route: gapCount > 0 ? 'finance/reskontra' : null });
            if (unmatched.error) rows.push({ key: 'opter', label: 'Okopplade Opter-händelser', dot: 'warn', text: 'Kunde inte hämtas' });
            else if (unmatched.data) {
              const n = unmatched.data.count || 0;
              rows.push({ key: 'opter', label: 'Okopplade Opter-händelser', dot: n > 0 ? 'warn' : 'ok', text: n >= 500 ? 'Minst 500' : n > 0 ? fmtInt(n) : 'Inga', route: n > 0 ? 'deliveries' : null });
            }
            return rows.map(r => <div key={r.key} style={{ display: 'flex', alignItems: 'center', gap: 10, fontSize: 13.5 }} title={r.title}>
              <span className={`rx-dot${r.dot === 'ok' ? '' : ` rx-dot--${r.dot}`}`} />
              <span style={{ flex: 1, minWidth: 0 }}>{r.label}</span>
              {r.route
                ? <button type="button" className="rx-link rx-num" onClick={() => nav(r.route)}>{r.text}</button>
                : <span className="rx-num rx-muted" style={{ fontSize: 12.5 }}>{r.text}</span>}
            </div>);
          })()}
        </div>
      </RxPanel>
    </section>
  </div>;
}
