/* ============================================================
   pulse. — shared color system
   Light + dark, auto-switched by Fiji sunrise/sunset.
   Per-page identity: dashboard=violet · news=sky · review=teal
   ============================================================ */

:root{
  /* brand constants — identical in both themes */
  --coral:#FF6B5E; --violet:#7C5CFF; --teal:#00C2A8;
  --amber:#FFB020; --sky:#38BDF8; --pink:#FF7AC6;

  /* light theme */
  --bg:#FFFFFF; --surface:#F8F8FB; --raised:#FFFFFF;
  --text:#1A1D29; --muted:#737885; --line:#ECEDF2;
  --t-violet:#F2EEFF; --t-coral:#FFEFEC; --t-teal:#E3F8F4;
  --t-amber:#FFF6E3; --t-sky:#E8F6FE;
  --a-violet:#5B45C7; --a-coral:#C2372B; --a-teal:#008C7A;
  --a-amber:#C98A00; --a-sky:#1A6E96;
  --neg:#E5533D; --pos:#00A88E;
  --shadow:0 1px 2px rgba(26,29,41,.04),0 8px 24px rgba(26,29,41,.06);
  --grad-hero:linear-gradient(118deg,#7C5CFF 0%,#A855F7 38%,#FF6B5E 100%);
  --grad-news:linear-gradient(118deg,#38BDF8 0%,#7C5CFF 100%);
  --grad-review:linear-gradient(118deg,#00C2A8 0%,#38BDF8 62%,#7C5CFF 100%);
}

:root[data-theme="dark"]{
  --bg:#0E1017; --surface:#171A24; --raised:#12151F;
  --text:#F3F5FA; --muted:#8B92A5; --line:#262A38;
  --t-violet:#1B1733; --t-coral:#2A1712; --t-teal:#0D2622;
  --t-amber:#2A2110; --t-sky:#0F2231;
  --a-violet:#A48FFF; --a-coral:#FF8B7E; --a-teal:#2FD9C0;
  --a-amber:#FFC44D; --a-sky:#6BCEFF;
  --neg:#FF7A66; --pos:#2FD9C0;
  --shadow:0 1px 2px rgba(0,0,0,.3),0 8px 24px rgba(0,0,0,.35);
}

*{box-sizing:border-box}
html,body{margin:0;padding:0}
body{
  font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Inter,Roboto,Helvetica,Arial,sans-serif;
  background:var(--bg); color:var(--text); line-height:1.45;
  -webkit-font-smoothing:antialiased;
  transition:background .4s ease,color .4s ease;
}
a{color:inherit;text-decoration:none}
.wrap{max-width:1080px;margin:0 auto;padding:0 20px 60px}

/* Shown when the source document doesn't exist yet. Deliberately replaces the
   whole page rather than rendering placeholder content, which would look like
   real financial data. */
#empty{display:none;min-height:70vh;align-items:center;justify-content:center;padding:24px}
#empty.show{display:flex}
.empty-card{text-align:center;max-width:420px}
.empty-card .logo{justify-content:center;font-size:26px;margin-bottom:14px}
.empty-msg{color:var(--muted);font-size:14.5px;line-height:1.6;margin:0}

/* ---------- banners ---------- */
.topnote{display:none;font-weight:600;font-size:13px;padding:10px 20px;text-align:center}
.topnote.show{display:block}
#preview{background:var(--t-violet);color:var(--a-violet);border-bottom:1px solid var(--line)}
#stale{background:var(--t-coral);color:var(--a-coral);border-bottom:1px solid var(--line)}

/* ---------- header ---------- */
header{position:sticky;top:0;z-index:30;background:color-mix(in srgb,var(--bg) 86%,transparent);
  backdrop-filter:saturate(1.4) blur(12px);border-bottom:1px solid var(--line)}
.bar{max-width:1080px;margin:0 auto;padding:12px 20px;display:flex;align-items:center;gap:12px}
.logo{display:flex;align-items:center;gap:8px;font-weight:800;font-size:19px;letter-spacing:-.02em}
.logo .dot{width:13px;height:13px;border-radius:50%;background:var(--coral)}
nav{display:flex;gap:6px;margin-left:6px}
nav a{padding:7px 13px;border-radius:999px;font-size:13.5px;font-weight:600;color:var(--muted);
  transition:background .2s,color .2s}
nav a:hover{background:var(--surface)}
nav a.on-violet{background:var(--violet);color:#fff}
nav a.on-sky{background:var(--sky);color:#08131C}
nav a.on-teal{background:var(--teal);color:#062621}
.bar .grow{flex:1}
.sunpill{display:flex;align-items:center;gap:7px;background:var(--surface);border:1px solid var(--line);
  border-radius:999px;padding:6px 12px;font-size:11.5px;font-weight:600;color:var(--muted);cursor:pointer;
  transition:background .2s}
.sunpill:hover{background:var(--t-violet);color:var(--a-violet)}
.sunpill .ic{font-size:13px}
/* Last-updated stamp. Since the daily email stopped, this is the only signal that
   the pipeline is still running — so it turns amber once a run has been missed. */
.lastup{font-size:11px;color:var(--muted);white-space:nowrap;cursor:default}
.lastup.stale{color:var(--a-amber);font-weight:700}
@media (max-width:720px){.lastup{display:none}}
.signout{font-size:11px;color:var(--muted);cursor:pointer;padding:6px 4px}
.signout:hover{color:var(--coral)}

/* Manual re-read. The page loads its data once, so after the 7am VM run lands
   this is what pulls it in without a full reload. */
.refreshbtn{display:flex;align-items:center;gap:7px;background:var(--surface);border:1px solid var(--line);
  border-radius:999px;padding:6px 12px;font:600 11.5px/1 inherit;color:var(--muted);cursor:pointer;
  transition:background .2s,color .2s}
.refreshbtn:hover:not(:disabled){background:var(--t-teal);color:var(--a-teal)}
.refreshbtn:disabled{opacity:.6;cursor:default}
.refreshbtn .ic{font-size:13px;display:inline-block}
.refreshbtn.spinning .ic{animation:spin .8s linear infinite}
.refreshmsg{font-size:11px;color:var(--muted);white-space:nowrap;opacity:0;transition:opacity .25s}
.refreshmsg.show{opacity:1}
.refreshmsg.err{color:var(--a-coral)}
@media (max-width:640px){.refreshmsg{display:none}}

/* ---------- cards ---------- */
.grid{display:grid;gap:16px;margin-top:18px}
.cols2{grid-template-columns:1.4fr 1fr}
.card{background:var(--surface);border-radius:18px;padding:22px;box-shadow:var(--shadow)}
.card.violet{background:var(--t-violet)} .card.coral{background:var(--t-coral)}
.card.teal{background:var(--t-teal)}     .card.amber{background:var(--t-amber)}
.card.sky{background:var(--t-sky)}
.kick{font-size:9.5px;font-weight:800;letter-spacing:.15em;text-transform:uppercase;margin:0 0 13px;color:var(--muted)}
.kick.violet{color:var(--a-violet)} .kick.coral{color:var(--a-coral)}
.kick.teal{color:var(--a-teal)}     .kick.amber{color:var(--a-amber)}
.kick.sky{color:var(--a-sky)}

/* ---------- gradient heroes ---------- */
.hero{border-radius:22px;padding:26px 28px;color:#fff;position:relative;overflow:hidden}
.hero.dash{background:var(--grad-hero)}
.hero.news{background:var(--grad-news)}
.hero.review{background:var(--grad-review)}
.hero .kick{color:rgba(255,255,255,.82)}
.hero h1{margin:2px 0 6px;font-size:38px;font-weight:800;letter-spacing:-.02em;line-height:1.08}
.nw{font-size:54px;font-weight:800;letter-spacing:-.025em;line-height:1;margin:2px 0 13px}
/* Currency switch inside the gradient hero — USD is the default, the rest
   restate the same net worth. Hidden entirely when no fx rates are available. */
.curswitch{display:flex;flex-wrap:wrap;gap:6px;margin:2px 0 12px}
.curswitch .cur{
  font:700 11px/1 inherit;letter-spacing:.05em;padding:6px 10px;border-radius:999px;
  cursor:pointer;color:#fff;background:rgba(255,255,255,.16);
  border:1px solid rgba(255,255,255,.3);
  transition:background .15s ease,color .15s ease,border-color .15s ease;
}
.curswitch .cur:hover{background:rgba(255,255,255,.28)}
.curswitch .cur.on{background:#fff;color:var(--violet);border-color:#fff}
.curswitch .cur:focus-visible{outline:2px solid #fff;outline-offset:2px}

/* Shown when manual balances have moved since the last VM sync, so the headline
   figure and the breakdown below it can legitimately disagree for a while. */
.drift{margin-top:10px;font-size:11.5px;line-height:1.5;color:rgba(255,255,255,.9);
  background:rgba(255,255,255,.16);border-radius:9px;padding:8px 11px;max-width:52ch}

.chips{display:flex;flex-wrap:wrap;gap:8px}
.chip{font-size:11.5px;font-weight:700;padding:5px 12px;border-radius:999px}
.chip.solid{background:#fff;color:var(--violet)}
.chip.ghost{background:rgba(255,255,255,.22);color:#fff}
.hero .sub{font-size:14px;color:rgba(255,255,255,.9);margin:0}
.trend{margin-top:16px}
.trend svg{width:100%;height:88px;display:block;overflow:visible}

/* Recent transactions under the spending bars. Sign carries the meaning:
   money out is neutral (it's the norm), money in is highlighted green. */
.tx-head{font-size:10px;font-weight:800;letter-spacing:.12em;text-transform:uppercase;
  color:var(--a-coral);margin:18px 0 4px;padding-top:14px;border-top:1px solid var(--line)}
/* No max-height and no overflow: the card simply grows to fit the rows. An
   earlier version measured the leftover space and hid rows that didn't fit, but
   hiding rows shrank the card, which shrank the space, which hid more rows — a
   ratchet that never recovered. Letting it grow is both simpler and correct. */
.tx-list{}
.tx{display:flex;align-items:flex-start;gap:12px;padding:9px 0;border-bottom:1px solid var(--line)}
.tx:last-child{border-bottom:none}
.tx-main{flex:1;min-width:0}
.tx-payee{font-size:13.5px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.tx-meta{font-size:11px;color:var(--muted);margin-top:2px;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.tx-right{text-align:right;flex:none}
.tx-amt{font-size:13.5px;font-weight:800;letter-spacing:-.01em;white-space:nowrap}
.tx-amt.in{color:var(--pos)}
.tx-amt.out{color:var(--text)}
.tx-date{font-size:10.5px;color:var(--muted);margin-top:2px;white-space:nowrap}

/* Holdings. The signal chip is a rules-derived verdict, deliberately understated
   so it never reads like a recommendation from an analyst. */
.hold{padding:13px 0;border-bottom:1px solid var(--line)}
.hold:last-child{border-bottom:none}
.hold-top{display:flex;align-items:baseline;gap:9px;flex-wrap:wrap}
.hold-top .grow{flex:1}
.hold-tk{font-size:15px;font-weight:800;letter-spacing:-.01em}
.hold-nm{font-size:12.5px;color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.hold-val{font-size:15px;font-weight:800}
.hold-sig{font-size:10.5px;font-weight:800;text-transform:uppercase;letter-spacing:.07em;
  border:1.5px solid;border-radius:999px;padding:3px 9px}
.hold-sub{font-size:11.5px;color:var(--muted);margin-top:3px}
.hold-pl{margin-left:8px;font-weight:700}
.hold-pl.up{color:var(--pos)} .hold-pl.down{color:var(--neg)}
.hold-stats{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px}
.hold-stat{font-size:11px;background:var(--surface);border-radius:7px;padding:4px 8px;color:var(--muted)}
.hold-stat b{color:var(--text);font-weight:700;margin-right:3px}
.hold-why{font-size:12px;color:var(--muted);margin-top:7px;line-height:1.5;max-width:70ch}

/* ---------- stat strip ---------- */
.stat-row{display:grid;grid-template-columns:repeat(3,1fr);gap:14px;margin-top:16px}
.stat{border-radius:16px;padding:16px 18px;box-shadow:var(--shadow)}
.stat .l{font-size:11px;color:var(--muted);font-weight:600}
.stat .v{font-size:21px;font-weight:800;margin-top:3px;letter-spacing:-.01em}
.stat .s{font-size:10.5px;font-weight:700;margin-top:2px}

/* ---------- allocation ---------- */
.donut-wrap{display:flex;align-items:center;gap:18px}
.legend{flex:1;min-width:0}
.legrow{display:flex;align-items:center;gap:9px;font-size:12.5px;padding:4px 0}
.legrow .sw{width:9px;height:9px;border-radius:3px;flex:0 0 9px}
.legrow .pc{margin-left:auto;font-weight:700;color:var(--muted)}

/* ---------- accounts ---------- */
/* Three distinct levels, so a group header can't be mistaken for an account's
   description. Both used to be var(--muted) grey, which read as one flat list:
     group header  -> violet accent (the Accounts card's own colour)
     account name  -> full-strength text
     description   -> muted grey
*/
.acct-group{margin-bottom:13px;padding-top:12px;border-top:1.5px solid var(--line)}
.acct-group:first-child{padding-top:0;border-top:none}
.acct-group:last-child{margin-bottom:0}
.acct-head{display:flex;gap:8px;font-size:11px;font-weight:800;letter-spacing:.09em;
  text-transform:uppercase;color:var(--a-violet);margin-bottom:6px}
.acct-head .tot{margin-left:auto;color:var(--text);font-size:12.5px;letter-spacing:0;text-transform:none}
.acct{display:flex;align-items:center;gap:10px;padding:8px 0;border-top:1px solid var(--line);font-size:13.5px}
.acct:first-of-type{border-top:none}
.acct .nm{color:var(--text)}
.acct .sub{display:block;color:var(--muted);font-size:11.5px;font-weight:400;margin-top:1px}
.acct .val{margin-left:auto;font-weight:700;font-variant-numeric:tabular-nums}
.acct .val.neg{color:var(--neg)}

/* ---------- spending bars ---------- */
.sp-row{display:flex;align-items:center;gap:10px;margin:9px 0;font-size:12.5px}
.sp-row .lab{width:104px;flex:0 0 104px}
.sp-row .track{flex:1;height:15px;background:color-mix(in srgb,var(--text) 8%,transparent);
  border-radius:8px;overflow:hidden}
.sp-row .fill{height:100%;border-radius:8px}
.sp-row .amt{width:56px;text-align:right;font-weight:700;color:var(--muted);font-variant-numeric:tabular-nums}
.note{font-size:11.5px;color:var(--muted);margin-top:10px}

/* ---------- trade ideas ---------- */
.ideas{display:grid;grid-template-columns:repeat(3,1fr);gap:12px}
.idea{background:var(--raised);border:2px solid;border-radius:14px;padding:15px}
.idea .tk{font-size:18px;font-weight:800}
.idea .nm{font-size:10.5px;color:var(--muted);font-weight:600;margin-top:1px}
.idea .why{font-size:11.5px;margin:9px 0}
.idea .tik{font-size:10.5px;font-weight:700;color:#fff;border-radius:8px;padding:5px 10px;display:inline-block}

/* ---------- manual entry ---------- */
.man-group{font-size:10.5px;font-weight:800;text-transform:uppercase;letter-spacing:.08em;
  color:var(--muted);margin:16px 0 2px}
.man-group:first-child{margin-top:0}
.man-note{display:block;font-size:11.5px;font-weight:400;color:var(--muted);
  margin-top:3px;line-height:1.5;max-width:46ch}
.man-row{display:flex;align-items:center;gap:10px;padding:10px 0;border-top:1px solid var(--line)}
.man-group + .man-row{border-top:none}
.man-row .nm{flex:1;font-size:13.5px}
.man-row .cur{font-size:11px;color:var(--muted)}
.man-row input{width:120px;font:inherit;font-size:14px;font-weight:700;text-align:right;
  border:1.5px solid var(--line);border-radius:10px;padding:7px 10px;color:var(--text);
  background:var(--raised);font-variant-numeric:tabular-nums}
.man-row input:focus{outline:none;border-color:var(--violet)}
.man-actions{display:flex;align-items:center;gap:12px;margin-top:14px}
.btn{font:inherit;font-size:13px;font-weight:700;border:none;border-radius:10px;padding:9px 17px;cursor:pointer}
.btn.primary{background:var(--violet);color:#fff}
.btn.primary:disabled{opacity:.4;cursor:default}
.saved{font-size:12px;color:var(--a-teal);font-weight:700;opacity:0;transition:opacity .3s}
.saved.show{opacity:1}

/* ---------- news ---------- */
.sec{margin-top:22px}
.sec-head{display:flex;align-items:center;gap:10px;margin-bottom:12px}
.sec-badge{font-size:10px;font-weight:800;letter-spacing:.12em;text-transform:uppercase;
  padding:5px 11px;border-radius:999px;color:#fff}
.sec-count{font-size:11.5px;color:var(--muted);font-weight:600;margin-left:auto}
.lead{background:var(--surface);border-radius:16px;padding:20px 22px;margin-bottom:12px;box-shadow:var(--shadow)}
.lead h3{margin:0 0 4px;font-size:17px;font-weight:800;letter-spacing:-.01em;line-height:1.25}
.lead h3 a:hover{text-decoration:underline}
.lead .src{font-size:10px;font-weight:800;letter-spacing:.1em;text-transform:uppercase;color:var(--muted);margin-bottom:9px}
.lead p{margin:0;font-size:13.5px;line-height:1.6}
.digest{display:grid;gap:2px}
.dig{display:block;padding:11px 14px;border-radius:11px;transition:background .18s}
.dig:hover{background:var(--surface)}
.dig .h{font-size:13.5px;font-weight:700;line-height:1.35}
.dig .s{font-size:12px;color:var(--muted);margin-top:2px;line-height:1.45}

/* ---------- weekly review ---------- */
.rev-nav{display:flex;flex-wrap:wrap;gap:7px;margin:16px 0 4px}
.rev-nav a{font-size:12px;font-weight:700;padding:7px 13px;border-radius:999px;
  background:var(--surface);color:var(--muted);border:1px solid var(--line)}
.rev-nav a:hover{background:var(--t-teal);color:var(--a-teal);border-color:transparent}
.rev-sec{scroll-margin-top:78px}
.rev-sec h2{font-size:24px;font-weight:800;letter-spacing:-.02em;margin:26px 0 8px}
.rev-sec h3{font-size:15px;font-weight:800;margin:18px 0 6px}
.rev-sec p{font-size:14px;line-height:1.65;margin:0 0 12px}
.pill-row{display:flex;flex-wrap:wrap;gap:10px;margin:14px 0}
.pill{background:var(--surface);border-radius:13px;padding:12px 16px;box-shadow:var(--shadow);min-width:120px}
.pill .l{font-size:10.5px;color:var(--muted);font-weight:700;text-transform:uppercase;letter-spacing:.08em}
.pill .v{font-size:19px;font-weight:800;margin-top:2px}
.callout{border-left:4px solid;border-radius:0 13px 13px 0;padding:15px 18px;margin:16px 0;font-size:13.5px;line-height:1.6}
.tick{display:inline-block;font-size:11px;font-weight:800;color:#fff;border-radius:8px;padding:5px 11px;margin-top:8px}

/* ---------- footer ---------- */
footer{max-width:1080px;margin:34px auto 0;padding:20px;border-top:1px solid var(--line);
  display:flex;align-items:center;gap:14px;font-size:12px;color:var(--muted);flex-wrap:wrap}
footer .switch{margin-left:auto;font-weight:700;color:var(--violet)}
footer .switch:hover{text-decoration:underline}

/* ---------- auth gate ---------- */
/* Shown by DEFAULT, with no JavaScript required. Firebase auth cannot resolve
   until the SDK downloads from gstatic — several seconds from Fiji — and until
   then #gate and #app are both hidden, which used to render a blank white page.
   This is also the only thing the user sees if JS fails outright. */
#splash{position:fixed;inset:0;z-index:110;background:var(--grad-hero);display:flex;
  align-items:center;justify-content:center;padding:24px}
#splash.hide{display:none}
#splash .gate-card p{margin-bottom:0}
.spin{width:26px;height:26px;margin:20px auto 0;border-radius:50%;
  border:3px solid rgba(255,255,255,.35);border-top-color:#fff;
  animation:spin .8s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion:reduce){.spin{animation-duration:2.4s}}

#gate{position:fixed;inset:0;z-index:100;background:var(--grad-hero);display:none;
  align-items:center;justify-content:center;padding:24px}
#gate.show{display:flex}
.gate-card{text-align:center;color:#fff;max-width:340px}
.gate-card .logo{font-size:32px;font-weight:800;justify-content:center;margin-bottom:8px}
.gate-card .logo .dot{background:#fff}
.gate-card p{color:rgba(255,255,255,.85);font-size:15px;margin:0 0 26px}
.gbtn{display:inline-flex;align-items:center;gap:10px;background:#fff;color:#1A1D29;font:inherit;
  font-size:15px;font-weight:600;border:none;border-radius:12px;padding:13px 22px;cursor:pointer}
.gerr{color:#FFE0DC;font-size:13px;margin-top:16px;min-height:18px}
#app{display:none}#app.show{display:block}

/* Refresh confirm + live status */
.modal{position:fixed;inset:0;z-index:120;background:rgba(12,14,22,.62);
  display:none;align-items:center;justify-content:center;padding:20px}
.modal.show{display:flex}
.modal-card{background:var(--surface);border:1px solid var(--line);border-radius:18px;
  padding:24px;max-width:460px;width:100%;box-shadow:0 24px 60px rgba(0,0,0,.32)}
.modal-card h3{margin:0 0 12px;font-size:19px;font-weight:800;color:var(--ink)}
.modal-card p{margin:0 0 12px;font-size:14px;line-height:1.55;color:var(--muted)}
.modal-warn{background:var(--t-amber);border-left:3px solid var(--amber);
  border-radius:8px;padding:11px 13px;color:var(--ink)!important;font-size:13px!important}
.modal-actions{display:flex;gap:10px;justify-content:flex-end;margin-top:18px}
.btn.ghost{background:transparent;border:1px solid var(--line);color:var(--muted)}
.btn.ghost:hover{color:var(--ink)}
.rf-foot{font-size:11.5px!important;color:var(--muted);margin:14px 0 0!important;text-align:center}

.rf-steps{display:flex;flex-direction:column;gap:2px;margin:4px 0 14px}
.rf-step{display:flex;align-items:flex-start;gap:11px;padding:9px 2px;font-size:14px}
.rf-step .dot{flex:none;width:18px;height:18px;border-radius:50%;margin-top:1px;
  border:2px solid var(--line);display:flex;align-items:center;justify-content:center;
  font-size:11px;font-weight:800;color:#fff}
.rf-step.pending{color:var(--muted);opacity:.65}
.rf-step.active{color:var(--ink);font-weight:600}
.rf-step.active .dot{border-color:var(--violet);border-top-color:transparent;
  animation:spin .8s linear infinite}
.rf-step.done{color:var(--ink)}
.rf-step.done .dot{background:var(--teal);border-color:var(--teal)}
.rf-step.failed{color:var(--a-coral)}
.rf-step.failed .dot{background:var(--coral);border-color:var(--coral)}
.rf-step .detail{display:block;font-size:12px;font-weight:400;color:var(--muted);margin-top:2px}
.rf-msg{font-size:13px;color:var(--muted);min-height:18px}
.rf-msg.err{color:var(--a-coral)}
.rf-msg.ok{color:var(--a-teal)}

/* ---------- responsive + forced mobile ---------- */
@media (max-width:760px){
  .cols2{grid-template-columns:1fr}
  .ideas,.stat-row{grid-template-columns:1fr}
  nav{display:none}
  .nw{font-size:44px} .hero h1{font-size:30px}
  .sp-row .lab{width:84px;flex:0 0 84px}
}
body.force-mobile .cols2,body.force-mobile .ideas,body.force-mobile .stat-row{grid-template-columns:1fr}
body.force-mobile nav{display:none}
body.force-mobile .wrap,body.force-mobile .bar,body.force-mobile footer{max-width:460px}
body.force-mobile .nw{font-size:44px}
body.force-desktop nav{display:flex}
