// entries.jsx — Daily/weekly/monthly metric entry form backed by Firestore.
//
// Cadence model (matches Olie's spreadsheet):
//   - Metrics with `frequency: 'daily'` are entered each day (period = 'day').
//   - Metrics with `frequency: 'weekly'` are entered weekly (period = 'week').
//   - Metrics with `frequency: 'monthly'` are entered monthly (period = 'month').
// Doc ID is `{metricId}__{periodKey}` so re-entering a period overwrites
// the same doc (idempotent set with merge).
//
// Weekly cycle is Fri → Thu (payroll-aligned). The weekly period key is the
// cycle-ending Thursday in YYYY-MM-DD-thu form. Monthly metrics close on the
// Thursday nearest month-end (ties resolve to the Thursday before month-end).
//
// Auth context: `getCurrentAuthContext()` prefers Firebase Auth, falls back
// to the device-local registry while auth.jsx is still the placeholder.
// When auth.jsx swaps to Firebase Auth, nothing in this file needs to change.
// In device-local mode, entries are persisted to localStorage so the form
// still works end-to-end for demos.

// ───────────────────────────── auth context ──────────────────────────────
async function getCurrentAuthContext() {
  let real = null;
  try {
    if (typeof firebase !== 'undefined' && firebase.auth) {
      const fbUser = firebase.auth().currentUser;
      if (fbUser) {
        let data = {};
        try {
          const snap = await firebase.firestore().doc('users/' + fbUser.uid).get();
          if (snap.exists) data = snap.data();
        } catch (readErr) {
          console.warn('[norwill] /users/{uid} read failed', readErr);
        }
        real = {
          uid: fbUser.uid,
          personaId: data.personaId ?? null,
          role: data.role || 'member',
          name: data.name || fbUser.displayName || fbUser.email,
          email: fbUser.email,
          backend: 'firebase',
        };
      }
    }
  } catch (e) {
    console.warn('[norwill] firebase auth check failed', e);
  }
  if (!real) {
    const shim = (typeof getAuthUser === 'function') ? getAuthUser() : null;
    if (!shim) return null;
    real = {
      uid: 'local:' + shim.username,
      personaId: shim.personaId,
      role: shim.role,
      name: shim.name,
      email: null,
      backend: 'local',
    };
  }
  // Apply admin "View as" override — keeps real uid (writes still attribute
  // to the actual user) but swaps personaId/role/name so visible metrics and
  // filtering match the impersonated teammate.
  const apply = window.norwillImpersonation?.apply;
  return apply ? apply(real) : real;
}

// ───────────────────────────── period helpers ────────────────────────────
function toYMD(d) {
  const y = d.getFullYear();
  const m = String(d.getMonth() + 1).padStart(2, '0');
  const day = String(d.getDate()).padStart(2, '0');
  return y + '-' + m + '-' + day;
}

function todayYMD() { return toYMD(new Date()); }
function yesterdayYMD() {
  const d = new Date();
  d.setDate(d.getDate() - 1);
  return toYMD(d);
}

// Cycle-ending Thursday for the Fri→Thu week that contains `d`.
function weekEndingThursday(d) {
  const date = new Date(d);
  const dow = date.getDay();        // 0 Sun … 4 Thu … 6 Sat
  const fwd = (4 - dow + 7) % 7;    // days forward to next Thu (0 if today is Thu)
  date.setDate(date.getDate() + fwd);
  return date;
}

function weekKeyFor(d) {
  return toYMD(weekEndingThursday(d)) + '-thu';
}

function monthKeyFor(d) {
  return toYMD(window.NORWILL_DATA.monthCloseThursday(d)) + '-thu-month';
}

function entryDocId(metricId, periodKey) {
  return metricId + '__' + periodKey;
}

// ───────────────────────────── Firestore I/O ─────────────────────────────
const LOCAL_ENTRIES_KEY = 'norwill-entries-local';

function readLocalEntries() {
  try { return JSON.parse(localStorage.getItem(LOCAL_ENTRIES_KEY) || '{}'); }
  catch { return {}; }
}
function writeLocalEntries(all) {
  localStorage.setItem(LOCAL_ENTRIES_KEY, JSON.stringify(all));
}

async function saveEntry({ metric, value, period, periodKey, auth }) {
  if (!auth) throw new Error('Not signed in.');

  if (auth.backend === 'local') {
    const all = readLocalEntries();
    all[entryDocId(metric.id, periodKey)] = {
      metricId: metric.id,
      ownerId: metric.ownerId,
      value,
      date: periodKey,
      period,
      submittedBy: auth.uid,
      submittedAt: Date.now(),
    };
    writeLocalEntries(all);
    return;
  }

  const db = firebase.firestore();
  const ref = db.collection('metricEntries').doc(entryDocId(metric.id, periodKey));
  await ref.set({
    metricId: metric.id,
    ownerId: metric.ownerId,
    value,
    date: periodKey,
    period,
    submittedBy: auth.uid,
    submittedAt: firebase.firestore.FieldValue.serverTimestamp(),
  }, { merge: true });
}

async function loadEntriesForPeriod({ metricIds, periodKey, auth }) {
  if (!metricIds.length) return {};

  if (auth && auth.backend === 'local') {
    const all = readLocalEntries();
    const out = {};
    for (const id of metricIds) {
      const doc = all[entryDocId(id, periodKey)];
      if (doc) out[id] = doc;
    }
    return out;
  }

  const db = firebase.firestore();
  const refs = metricIds.map(id => db.collection('metricEntries').doc(entryDocId(id, periodKey)));
  const snaps = await Promise.all(refs.map(r => r.get()));
  const out = {};
  snaps.forEach((snap, i) => {
    if (snap.exists) out[metricIds[i]] = snap.data();
  });
  return out;
}

// ───────────────────────────── React UI ──────────────────────────────────
// EntryForm = modal version (overlay). EntryFormBody = inline version that
// renders directly into a page (used by the Metric Entry view). They share
// every line of state + save logic — the only difference is the wrapper.
function EntryForm({ onClose }) {
  return (
    <Overlay onClose={onClose}>
      <EntryFormBody onClose={onClose} mode="modal"/>
    </Overlay>
  );
}

function EntryFormBody({ onClose, mode = 'modal' }) {
  const [auth, setAuth] = React.useState(null);
  const [tab, setTab] = React.useState('today');   // 'today' | 'week' | 'month'
  // Default to yesterday — that's the day reported on each morning during huddle.
  const [date, setDate] = React.useState(yesterdayYMD());
  const [scopeAll, setScopeAll] = React.useState(false); // admin/owner can enter for anyone
  const [values, setValues] = React.useState({});
  const [saved, setSaved] = React.useState({});   // metricId -> 'saving' | 'saved' | error string
  const [loading, setLoading] = React.useState(true);

  React.useEffect(() => {
    getCurrentAuthContext().then(ctx => setAuth(ctx));
  }, []);

  // Force "All" scope on for admins (Dave has no personaId).
  React.useEffect(() => {
    if (auth && !auth.personaId) setScopeAll(true);
  }, [auth]);

  // If the user has no daily metrics, auto-switch to the first cadence with
  // assigned metrics so the form doesn't open on an empty tab.
  const autoTabPicked = React.useRef(false);
  React.useEffect(() => {
    if (!auth || autoTabPicked.current) return;
    const METRICS_ALL = window.NORWILL_DATA.METRICS;
    const mine = (scopeAll || !auth.personaId)
      ? METRICS_ALL
      : METRICS_ALL.filter(m => m.ownerId === auth.personaId);
    const dailyCount = mine.filter(m => window.metricCadence(m) === 'daily').length;
    const weeklyCount = mine.filter(m => window.metricCadence(m) === 'weekly').length;
    const monthlyCount = mine.filter(m => window.metricCadence(m) === 'monthly').length;
    if (dailyCount === 0) {
      if (weeklyCount > 0) setTab('week');
      else if (monthlyCount > 0) setTab('month');
    }
    autoTabPicked.current = true;
  }, [auth, scopeAll]);

  const isPrivileged = auth && (auth.role === 'admin' || auth.role === 'owner');

  const scope = React.useMemo(() => {
    if (!auth) return { daily: [], weekly: [], monthly: [] };
    const METRICS_ALL = window.NORWILL_DATA.METRICS;
    const mine = (scopeAll || !auth.personaId)
      ? METRICS_ALL
      : METRICS_ALL.filter(m => m.ownerId === auth.personaId);
    return {
      daily: mine.filter(m => window.metricCadence(m) === 'daily'),
      weekly: mine.filter(m => window.metricCadence(m) === 'weekly'),
      monthly: mine.filter(m => window.metricCadence(m) === 'monthly'),
    };
  }, [auth, scopeAll]);

  const visible = tab === 'today' ? scope.daily : tab === 'week' ? scope.weekly : scope.monthly;
  const period = tab === 'today' ? 'day' : tab === 'week' ? 'week' : 'month';
  const periodKey = tab === 'today'
    ? date
    : tab === 'week'
      ? weekKeyFor(new Date(date + 'T12:00:00'))
      : monthKeyFor(new Date(date + 'T12:00:00'));

  // Load existing values whenever period changes.
  React.useEffect(() => {
    if (!auth) return;
    setLoading(true);
    loadEntriesForPeriod({ metricIds: visible.map(m => m.id), periodKey, auth })
      .then(existing => {
        const next = {};
        for (const m of visible) {
          const prior = existing[m.id];
          next[m.id] = (prior && prior.value != null) ? String(prior.value) : '';
        }
        setValues(next);
        setSaved({});
        setLoading(false);
      })
      .catch(err => {
        console.error('loadEntriesForPeriod failed', err);
        setLoading(false);
      });
  }, [auth, tab, periodKey, scopeAll]);

  const commit = async (metric) => {
    const raw = values[metric.id];
    if (raw === '' || raw == null) return;
    const num = Number(raw);
    if (Number.isNaN(num)) {
      setSaved(s => ({ ...s, [metric.id]: 'Must be a number' }));
      return;
    }
    setSaved(s => ({ ...s, [metric.id]: 'saving' }));
    try {
      await saveEntry({ metric, value: num, period, periodKey, auth });
      setSaved(s => ({ ...s, [metric.id]: 'saved' }));
      // Notify the recent-entries panel (and any future listeners) so the
      // verification view refreshes immediately. Saves continue working
      // even if the dispatch fails — we don't await it.
      try { window.dispatchEvent(new CustomEvent('norwill:entry-saved', { detail: { metricId: metric.id, periodKey, value: num } })); } catch {}
      setTimeout(() => {
        setSaved(s => {
          if (s[metric.id] !== 'saved') return s;
          const n = { ...s };
          delete n[metric.id];
          return n;
        });
      }, 1800);
    } catch (err) {
      console.error('saveEntry failed', err);
      setSaved(s => ({ ...s, [metric.id]: err.message || 'Save failed' }));
    }
  };

  const isWeeklyDay = new Date().getDay() === 4; // Thursday

  if (!auth) {
    return <div style={emptyStyle}>Loading your account…</div>;
  }

  return (
    <React.Fragment>
      {/* Header ---------------------------------------------------------- */}
      <div style={headerStyle}>
        <div>
          <div style={eyebrowStyle}>Enter numbers</div>
          <div style={titleStyle}>
            {tab === 'today' ? 'Daily metrics' : tab === 'week' ? 'Weekly metrics' : 'Monthly metrics'}
          </div>
          <div style={subStyle}>
            {tab === 'today'
              ? `For ${date}`
              : tab === 'week'
                ? `Week ending ${toYMD(weekEndingThursday(new Date(date + 'T12:00:00')))} (Thu)`
                : `Month closing ${toYMD(window.NORWILL_DATA.monthCloseThursday(new Date(date + 'T12:00:00')))} (Thu)`}
          </div>
        </div>
        {mode === 'modal' && (
          <button onClick={onClose} aria-label="Close" style={closeBtnStyle}>×</button>
        )}
      </div>

      {/* Tabs ------------------------------------------------------------ */}
      <div style={tabsRowStyle}>
        <div style={tabsWrap}>
          {[
            ['today', 'Today (' + scope.daily.length + ')'],
            ['week',  'This week (' + scope.weekly.length + ')'],
            ['month', 'This month (' + scope.monthly.length + ')'],
          ].map(([k, label]) => (
            <button key={k} onClick={() => setTab(k)} style={tabBtn(tab === k)}>
              {label}
            </button>
          ))}
        </div>
        {auth.personaId && (
          <label style={scopeToggleStyle} title="Turn on to enter numbers for a teammate you're covering. Each entry is stamped with your name in the audit log.">
            <input
              type="checkbox"
              checked={scopeAll}
              onChange={e => setScopeAll(e.target.checked)}
              style={{ accentColor: 'var(--brand-500)' }}
            />
            <span>{isPrivileged ? 'All metrics' : 'Covering for a teammate'}</span>
          </label>
        )}
      </div>

      {/* Date picker (so you can backfill) ------------------------------- */}
      <div style={dateRowStyle}>
        <label style={{ fontSize: 11.5, color: 'var(--ink-500)', fontWeight: 600 }}>
          {tab === 'today' ? 'Entry date' : tab === 'week' ? 'Any date in the target week' : 'Any date in the target month'}
        </label>
        <input
          type="date"
          value={date}
          onChange={e => setDate(e.target.value || todayYMD())}
          max={todayYMD()}
          style={dateInputStyle}
        />
        {tab === 'today' && (
          <div style={{ fontSize: 11, color: 'var(--ink-500)', lineHeight: 1.4 }}>
            Defaulted to yesterday — that's the day we report on each morning. Pick a different date to backfill.
          </div>
        )}
        {tab === 'week' && !isWeeklyDay && (
          <div style={{ fontSize: 11, color: 'var(--ink-500)', lineHeight: 1.4 }}>
            Weekly numbers are typically entered on Thursday (cycle end).
          </div>
        )}
        {tab === 'month' && (
          <div style={{ fontSize: 11, color: 'var(--ink-500)', lineHeight: 1.4 }}>
            Monthly metrics close on the Thursday nearest month-end.
          </div>
        )}
      </div>

      {/* Backend banner -------------------------------------------------- */}
      {auth.backend === 'local' && (
        <div style={bannerStyle}>
          Dev mode — entries saved to this device only until Firebase Auth goes live.
        </div>
      )}

      {/* Metric list ----------------------------------------------------- */}
      <div style={listWrapStyle}>
        {loading ? (
          <div style={emptyStyle}>Loading…</div>
        ) : visible.length === 0 ? (
          <div style={emptyStyle}>
            <div style={{ marginBottom: 10 }}>
              {tab === 'today'
                ? 'You don\'t have any daily metrics — your numbers are entered weekly.'
                : tab === 'week'
                  ? 'You don\'t have any weekly metrics — your numbers may be entered daily or monthly.'
                  : 'You don\'t have any monthly metrics assigned right now.'}
            </div>
            {(tab === 'today' ? (scope.weekly.length + scope.monthly.length) : (scope.daily.length + scope.weekly.length + scope.monthly.length)) > 0 && (
              <button
                type="button"
                onClick={() => {
                  if (tab === 'today') {
                    if (scope.weekly.length > 0) setTab('week');
                    else if (scope.monthly.length > 0) setTab('month');
                  } else if (scope.daily.length > 0) {
                    setTab('today');
                  } else if (tab === 'week' && scope.monthly.length > 0) {
                    setTab('month');
                  } else if (tab === 'month' && scope.weekly.length > 0) {
                    setTab('week');
                  }
                }}
                style={{
                  padding: '8px 14px', borderRadius: 8,
                  border: '1px solid var(--brand-700)',
                  background: 'var(--brand-700)', color: '#fff',
                  fontSize: 13, fontWeight: 600, cursor: 'pointer',
                }}
              >
                {tab === 'today'
                  ? (scope.weekly.length > 0 ? 'Switch to weekly metrics →' : 'Switch to monthly metrics →')
                  : scope.daily.length > 0
                    ? 'Switch to daily metrics →'
                    : tab === 'week'
                      ? 'Switch to monthly metrics →'
                      : 'Switch to weekly metrics →'}
              </button>
            )}
          </div>
        ) : (
          visible.map(metric => (
            <MetricRow
              key={metric.id}
              metric={metric}
              value={values[metric.id] ?? ''}
              onChange={v => setValues(s => ({ ...s, [metric.id]: v }))}
              onCommit={() => commit(metric)}
              status={saved[metric.id]}
              period={period}
            />
          ))
        )}
      </div>

      <div style={footerNoteStyle}>
        Numbers save automatically when you tap out of the field. Look for the green check next to each row to confirm.
      </div>
    </React.Fragment>
  );
}

function MetricRow({ metric, value, onChange, onCommit, status, period }) {
  const target  = window.entryTarget(metric, period);
  const unit    = inferUnit(metric);
  const owner   = window.NORWILL_DATA.PEOPLE.find(p => p.id === metric.ownerId);
  const leadAdornStyle  = { ...inputAdornStyle, left: 9 };
  const trailAdornStyle = { ...inputAdornStyle, right: 9 };

  let indicator;
  if (status === 'saving') indicator = <span style={statusDotStyle('saving')}>saving…</span>;
  else if (status === 'saved') indicator = <span style={statusDotStyle('saved')}>saved ✓</span>;
  else if (typeof status === 'string' && status.length) indicator = <span style={statusDotStyle('error')}>{status}</span>;
  else indicator = null;

  return (
    <div style={rowStyle}>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={rowLabelStyle}>{metric.label}</div>
        <div style={rowMetaStyle}>
          <span style={{ color: owner ? owner.color : 'var(--ink-500)', fontWeight: 600 }}>
            {owner ? owner.name.split(' ')[0] : 'Unassigned'}
          </span>
          <span>· {metric.fn}</span>
          <span>· {metric.dir === '>=' ? 'Target' : 'Cap'} {formatTarget(target, unit)} {period === 'day' ? '/day' : period === 'week' ? '/wk' : '/mo'}</span>
        </div>
      </div>
      <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-end', gap: 4 }}>
        <div style={inputWrapStyle}>
          {unit === '$' && <span style={leadAdornStyle}>$</span>}
          <input
            type="text"
            inputMode="decimal"
            value={value}
            onChange={e => onChange(e.target.value)}
            onBlur={onCommit}
            onKeyDown={e => { if (e.key === 'Enter') e.currentTarget.blur(); }}
            placeholder="—"
            style={inputStyle(unit === '$')}
          />
          {unit === '%' && <span style={trailAdornStyle}>%</span>}
        </div>
        <div style={{ fontSize: 10.5, height: 12 }}>{indicator}</div>
      </div>
    </div>
  );
}

function inferUnit(m) {
  if (/\$/.test(m.label)) return '$';
  if (/%/.test(m.label))  return '%';
  return '';
}

function formatTarget(n, unit) {
  if (n == null) return '—';
  if (unit === '$') return '$' + Number(n).toLocaleString();
  if (unit === '%') return n + '%';
  return Number(n).toLocaleString();
}

// ───────────────────────────── Overlay shell ─────────────────────────────
function Overlay({ children, onClose }) {
  React.useEffect(() => {
    const onKey = (e) => { if (e.key === 'Escape') onClose(); };
    document.addEventListener('keydown', onKey);
    const prev = document.body.style.overflow;
    document.body.style.overflow = 'hidden';
    return () => {
      document.removeEventListener('keydown', onKey);
      document.body.style.overflow = prev;
    };
  }, [onClose]);

  return (
    <div
      onClick={(e) => { if (e.target === e.currentTarget) onClose(); }}
      style={{
        position: 'fixed', inset: 0, zIndex: 90,
        background: 'rgba(12, 20, 28, 0.42)',
        display: 'flex', alignItems: 'flex-end', justifyContent: 'center',
        padding: 0,
      }}
    >
      <div
        className="slide-up"
        style={{
          width: '100%', maxWidth: 560,
          maxHeight: '92vh',
          display: 'flex', flexDirection: 'column',
          background: 'var(--card)',
          borderTopLeftRadius: 18, borderTopRightRadius: 18,
          borderLeft: '1px solid var(--line)',
          borderRight: '1px solid var(--line)',
          borderTop: '1px solid var(--line)',
          boxShadow: '0 -10px 40px rgba(0,0,0,0.18)',
          overflow: 'hidden',
        }}
      >
        {children}
      </div>
    </div>
  );
}

// ───────────────────────────── styles ────────────────────────────────────
const headerStyle = {
  display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between',
  padding: '18px 20px 12px',
  borderBottom: '1px solid var(--line)',
};
const eyebrowStyle = {
  fontSize: 10.5, letterSpacing: 0.1, textTransform: 'uppercase',
  color: 'var(--brand-700)', fontWeight: 700,
};
const titleStyle = {
  margin: '4px 0 2px', fontSize: 19, fontWeight: 700, letterSpacing: -0.3,
  color: 'var(--ink-900)',
};
const subStyle = { fontSize: 12.5, color: 'var(--ink-600)' };
const closeBtnStyle = {
  background: 'transparent', border: 'none',
  fontSize: 26, lineHeight: 1, color: 'var(--ink-500)',
  cursor: 'pointer', padding: 4, marginTop: -2,
};
const tabsRowStyle = {
  display: 'flex', alignItems: 'center', gap: 10,
  padding: '12px 20px 8px', flexWrap: 'wrap',
};
const tabsWrap = {
  display: 'inline-flex', background: 'var(--ink-50)',
  padding: 3, borderRadius: 9, border: '1px solid var(--line)', gap: 2,
};
const tabBtn = (active) => ({
  padding: '7px 14px', borderRadius: 7, border: 'none',
  background: active ? 'var(--card)' : 'transparent',
  color: active ? 'var(--ink-900)' : 'var(--ink-600)',
  fontSize: 12.5, fontWeight: 600,
  boxShadow: active ? 'var(--shadow-sm)' : 'none',
  cursor: 'pointer',
});
const scopeToggleStyle = {
  display: 'inline-flex', alignItems: 'center', gap: 6,
  fontSize: 12, color: 'var(--ink-600)', fontWeight: 600,
  marginLeft: 'auto', cursor: 'pointer',
};
const dateRowStyle = {
  padding: '4px 20px 12px',
  display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap',
};
const dateInputStyle = {
  padding: '7px 10px',
  borderRadius: 8, border: '1px solid var(--line-strong)',
  background: 'var(--card-alt)', color: 'var(--ink-900)',
  fontSize: 13,
};
const bannerStyle = {
  margin: '0 20px 8px',
  padding: '8px 12px',
  background: 'var(--warn-bg)', color: 'var(--warn)',
  borderRadius: 8, fontSize: 11.5, fontWeight: 500,
};
const listWrapStyle = {
  flex: 1, overflowY: 'auto',
  padding: '4px 12px 12px',
};
const emptyStyle = {
  padding: '40px 20px', textAlign: 'center',
  color: 'var(--ink-500)', fontSize: 13.5,
};
const rowStyle = {
  display: 'flex', alignItems: 'center', gap: 12,
  padding: '12px 12px', margin: '4px 0',
  background: 'var(--card-alt)', border: '1px solid var(--line)',
  borderRadius: 11,
};
const rowLabelStyle = {
  fontSize: 13.5, fontWeight: 600, color: 'var(--ink-900)',
  lineHeight: 1.3,
};
const rowMetaStyle = {
  marginTop: 3,
  fontSize: 11, color: 'var(--ink-500)',
  display: 'flex', gap: 4, flexWrap: 'wrap',
};
const inputWrapStyle = {
  position: 'relative',
  display: 'inline-flex', alignItems: 'center',
};
const inputAdornStyle = {
  position: 'absolute',
  pointerEvents: 'none',
  color: 'var(--ink-500)', fontSize: 13, fontWeight: 600,
};
const inputStyle = (hasLeadingDollar) => ({
  width: 96,
  padding: '11px 12px',
  paddingLeft:  hasLeadingDollar ? 22 : 12,
  paddingRight: hasLeadingDollar ? 12 : 22, // room for trailing %
  border: '1px solid var(--line-strong)',
  borderRadius: 9,
  background: 'var(--card)',
  color: 'var(--ink-900)',
  fontSize: 16, // 16px avoids iOS zoom on focus
  fontWeight: 600,
  textAlign: 'right',
  fontFamily: "'JetBrains Mono', monospace",
});
const footerNoteStyle = {
  padding: '10px 20px 14px',
  borderTop: '1px solid var(--line)',
  fontSize: 11.5, color: 'var(--ink-500)',
  background: 'var(--ink-50)',
};

// Note: `inputAdornStyle` is absolutely-positioned, so we fudge horizontal
// placement with these static offsets from the edge of the input.
// Leading "$": sits at the left inside the input.
// Trailing "%": sits at the right inside the input.
// Keep this in a pseudo-wrapper via inline adjustments below.
const statusDotStyle = (kind) => ({
  display: 'inline-block',
  padding: '1px 6px',
  borderRadius: 5,
  fontSize: 10, fontWeight: 700, letterSpacing: 0.04, textTransform: 'uppercase',
  background: kind === 'saved' ? 'var(--good-bg)'
            : kind === 'saving' ? 'var(--ink-100)'
            : 'var(--bad-bg)',
  color:      kind === 'saved' ? 'var(--good)'
            : kind === 'saving' ? 'var(--ink-600)'
            : 'var(--bad)',
});

// Read recent entries for a persona (or all, if personaId is null) over the
// last `days` days. Returns an array sorted newest-first. Used by the
// "Recent saved entries" panel on the Metric Entry page so users can verify
// what actually persisted.
async function loadRecentEntries({ personaId, days = 14, auth }) {
  if (!auth) return [];

  const cutoff = new Date();
  cutoff.setDate(cutoff.getDate() - days);
  const cutoffYMD = toYMD(cutoff);

  if (auth.backend === 'local') {
    const all = readLocalEntries();
    return Object.values(all)
      .filter(d => (!personaId || d.ownerId === personaId) && d.date >= cutoffYMD)
      .sort((a, b) => (b.submittedAt || 0) - (a.submittedAt || 0));
  }

  const db = firebase.firestore();
  let q = db.collection('metricEntries').where('date', '>=', cutoffYMD);
  if (personaId) q = q.where('ownerId', '==', personaId);
  const snap = await q.get();
  const rows = [];
  snap.forEach(doc => rows.push(doc.data()));
  rows.sort((a, b) => {
    const at = a.submittedAt?.toMillis ? a.submittedAt.toMillis() : (a.submittedAt || 0);
    const bt = b.submittedAt?.toMillis ? b.submittedAt.toMillis() : (b.submittedAt || 0);
    return bt - at;
  });
  return rows;
}

Object.assign(window, {
  EntryForm,
  EntryFormBody,
  getCurrentAuthContext,
  entryDocId,
  weekEndingThursday,
  weekKeyFor,
  monthKeyFor,
  todayYMD,
  loadEntriesForPeriod,
  loadRecentEntries,
  saveEntry,
});
