/* =====================================================================
   TOKENS — neutral surface ramp + one primary ramp. No hardcoded colour
   below this block.
   ===================================================================== */
:root{
  --surface-0:#FFFFFF; --surface-50:#FAFAFA; --surface-100:#F4F4F5; --surface-200:#E4E4E7;
  --surface-300:#D4D4D8; --surface-400:#A1A1AA; --surface-500:#71717A; --surface-600:#52525B;
  --surface-700:#3F3F46; --surface-800:#27272A; --surface-900:#18181B; --surface-950:#09090B;

  --primary-50:#ECFDF5; --primary-100:#D1FAE5; --primary-200:#A7F3D0; --primary-300:#6EE7B7;
  --primary-400:#34D399; --primary-500:#10B981; --primary-600:#059669; --primary-700:#047857;
  --primary-800:#065F46; --primary-900:#064E3B; --primary-950:#022C22;

  --radius-sm:6px; --radius:8px; --radius-lg:12px;
  --s1:4px; --s2:8px; --s3:12px; --s4:16px; --s5:20px; --s6:24px; --gap:12px;
  --serif:ui-serif,"Playfair Display",Georgia,"Iowan Old Style","Times New Roman",serif;
  --sans:"Inter",ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
  --nav-h:0px;
}
html[data-resolved="light"]{
  --bg:#F4F4F8; --card:#FFFFFF; --card-hover:#ECECF3;
  --input-bg:#ECECF3; --border:#E2E2EC; --border-strong:#CACAD8;
  --overlay:rgba(10,10,20,.40);
  --shadow-sm:0 1px 2px rgba(10,10,30,.06), 0 4px 16px -8px rgba(10,10,30,.10);
  --text:#18181F; --text-muted:#5C5C7A; --text-subtle:#9090AE;
  --primary:#5B5BD6; --primary-hover:#4A4ABF; --primary-contrast:#FFFFFF;
  --primary-soft:color-mix(in srgb,var(--primary) 10%,#FFFFFF);
  --primary-soft-border:color-mix(in srgb,var(--primary) 32%,#FFFFFF);
  --ring:color-mix(in srgb,var(--primary) 22%,transparent);
  --m1:#C2261B; --m2:#F07C1E; --m3:#E0B300; --m4:#4FA83D; --m5:#0E7A5F;

  /* Badge art palette. Every colour in badge-art.js is one of these names.
     The DARK values are the Figma export verbatim; do not retune them there,
     retune them here. Light re-points the same names so the hierarchy survives
     the flip: a whisper stays faint, gold stays the loudest thing on the shelf. */
  --ba-14100e:#FFFFFF;--ba-142f1c:#B3DBBF;--ba-15803d:#15803D;--ba-1b3b2b:#B3DBC7;
  --ba-1c1715:#FAF7F4;--ba-1e1b4b:#B6B3DB;--ba-1e2d3d:#B4C6DA;--ba-22c55e:#23C85F;
  --ba-2b4c6f:#2B4C6F;--ba-2d6a4f:#2D6A4F;--ba-34d399:#2AC18A;--ba-38bdf8:#1D97CD;
  --ba-4338ca:#4338CA;--ba-451a03:#DBC1B3;--ba-475569:#475569;--ba-4c1d24:#DBB3B9;
  --ba-5c3e35:#D6BFB8;--ba-5f261b:#DBBAB3;--ba-60a5fa:#1D6CCD;--ba-6366f1:#1D21CD;
  --ba-64748b:#64748B;--ba-8c5846:#8C5846;--ba-8c6a5c:#8C6A5C;--ba-9f1239:#9F1239;
  --ba-a1422e:#A1422E;--ba-a855f7:#771DCD;--ba-b45309:#B45309;--ba-e7b10a:#CDA21D;
  --ba-f43f5e:#CD1D3B;--ba-f59e0b:#CD8C1D;--ba-f5c453:#CD981D;--ba-fbbf24:#CD9C1D;
}
html[data-resolved="dark"]{
  --bg:#111113; --card:#18181C; --card-hover:#212127;
  --input-bg:#1C1C21; --border:rgba(255,255,255,.08); --border-strong:rgba(255,255,255,.15);
  --overlay:rgba(0,0,0,.65);
  --shadow-sm:0 1px 2px rgba(0,0,0,.5), 0 8px 24px -12px rgba(0,0,0,.8);
  --text:#FFFFFF; --text-muted:#94A3B8; --text-subtle:#7A889C;
  --primary:#3B82F6; --primary-hover:#60A5FA; --primary-contrast:#08111F;
  --primary-soft:color-mix(in srgb,var(--primary) 20%,var(--card));
  --primary-soft-border:color-mix(in srgb,var(--primary) 46%,var(--card));
  --ring:color-mix(in srgb,var(--primary) 28%,transparent);
  --m1:#F2503F; --m2:#FF9F45; --m3:#FFD93D; --m4:#7BD25C; --m5:#22B892;

  /* Badge art palette. Every colour in badge-art.js is one of these names.
     The DARK values are the Figma export verbatim; do not retune them there,
     retune them here. Dark is the export. */
  --ba-14100e:#14100e;--ba-142f1c:#142f1c;--ba-15803d:#15803d;--ba-1b3b2b:#1b3b2b;
  --ba-1c1715:#1c1715;--ba-1e1b4b:#1e1b4b;--ba-1e2d3d:#1e2d3d;--ba-22c55e:#22c55e;
  --ba-2b4c6f:#2b4c6f;--ba-2d6a4f:#2d6a4f;--ba-34d399:#34d399;--ba-38bdf8:#38bdf8;
  --ba-4338ca:#4338ca;--ba-451a03:#451a03;--ba-475569:#475569;--ba-4c1d24:#4c1d24;
  --ba-5c3e35:#5c3e35;--ba-5f261b:#5f261b;--ba-60a5fa:#60a5fa;--ba-6366f1:#6366f1;
  --ba-64748b:#64748b;--ba-8c5846:#8c5846;--ba-8c6a5c:#8c6a5c;--ba-9f1239:#9f1239;
  --ba-a1422e:#a1422e;--ba-a855f7:#a855f7;--ba-b45309:#b45309;--ba-e7b10a:#e7b10a;
  --ba-f43f5e:#f43f5e;--ba-f59e0b:#f59e0b;--ba-f5c453:#f5c453;--ba-fbbf24:#fbbf24;
}
*{box-sizing:border-box}
html,body{margin:0;padding:0}
body{
  background:var(--bg); color:var(--text); font:400 15px/1.55 var(--sans);
  -webkit-text-size-adjust:100%; letter-spacing:-.006em;
  padding-bottom:calc(var(--nav-h) + env(safe-area-inset-bottom));
}
h1,h2,h3{font-weight:600;letter-spacing:-.02em;margin:0}
button,input,textarea,select{font:inherit;color:inherit}
button{cursor:pointer}
:focus-visible{outline:2px solid var(--primary);outline-offset:2px;border-radius:var(--radius-sm)}
mark{background:color-mix(in srgb,var(--m3) 45%,transparent);color:inherit;border-radius:3px;padding:0 2px}
@media (prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important}}

/* ===================== buttons ===================== */
.btn{height:40px;display:inline-flex;align-items:center;gap:var(--s2);padding:0 var(--s4);
  border-radius:10px;font-size:14px;font-weight:500;border:1px solid transparent;background:transparent;transition:background .12s,border-color .12s}
.btn-primary{background:var(--primary);color:var(--primary-contrast);border-color:var(--primary)}
.btn-primary:hover{background:var(--primary-hover);border-color:var(--primary-hover)}
.btn-outlined{border-color:var(--border);background:var(--card);color:var(--text)}
.btn-outlined:hover{background:var(--card-hover);border-color:var(--border-strong)}
.btn-text{color:var(--primary)}
.btn-text:hover{background:var(--primary-soft)}
.btn-icon{width:40px;padding:0;justify-content:center}
.btn-block{width:100%;justify-content:flex-start;margin-top:var(--s2)}
.btn-danger{color:var(--m1);border-color:var(--border)}
.btn-danger:hover{background:color-mix(in srgb,var(--m1) 12%,transparent);border-color:var(--m1)}

/* ===================== segmented ===================== */
.seg{display:inline-flex;background:var(--input-bg);border:1px solid transparent;border-radius:12px;padding:4px;gap:3px}
.seg button{height:36px;padding:0 16px;border:1px solid transparent;background:transparent;border-radius:9px;
  font-size:13px;font-weight:500;color:var(--text-muted);white-space:nowrap;transition:background .12s,color .12s}
.seg button:hover{color:var(--text)}
.seg button[aria-pressed="true"],.seg button[aria-selected="true"]{
  background:var(--card);color:var(--text);font-weight:600;
  border-color:var(--border-strong);box-shadow:var(--shadow-sm)}
.seg.scroll{max-width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}
.segwrap{margin-bottom:var(--s3)}

/* ===================== chrome ===================== */
/* --card, not --bg. The segmented control is a white pill on a grey
   track: on the white drawer that chain reads, on the near-grey page
   background it collapsed and the tabs looked flat. The bottom nav was
   already --card, so the two navs disagreed with each other as well. */
.topbar{position:sticky;top:0;z-index:20;background:var(--card);border-bottom:1px solid var(--border);
  padding:var(--s3) var(--s4);padding-top:calc(var(--s3) + env(safe-area-inset-top))}
.topbar-row{display:flex;align-items:center;gap:var(--s3);max-width:1080px;margin:0 auto}
.brand{font-size:15px;font-weight:600;letter-spacing:-.02em;margin-inline-end:auto;white-space:nowrap}
.bottomnav{position:fixed;inset-inline:0;bottom:0;z-index:25;display:none;background:var(--card);
  border-top:1px solid var(--border);padding-bottom:env(safe-area-inset-bottom)}
/* Counted from the tabs there are, not typed in. It said repeat(5,1fr)
   and adding Badges put a sixth button on a second row — which is the
   kind of break that waits for the next view to be added, so this is
   fixed for good rather than changed to six. */
.bottomnav div{display:grid;grid-auto-flow:column;grid-auto-columns:minmax(0,1fr)}
.bottomnav button{background:transparent;border:0;padding:8px 2px 7px;font-size:9.5px;font-weight:500;
  color:var(--text-subtle);display:flex;flex-direction:column;align-items:center;gap:3px;min-height:54px}
.bottomnav button b{font-size:16px;font-weight:400;line-height:1}
.bottomnav button[aria-selected="true"]{color:var(--primary)}
@media (max-width:820px){ .topbar .seg{display:none} .bottomnav{display:block} body{--nav-h:60px} }
@media (max-width:360px){ .bottomnav button{font-size:8.5px;letter-spacing:-.02em} }
main{max-width:1080px;margin:0 auto;padding:var(--s4) var(--s4) var(--s6)}
.view[hidden]{display:none}

/* ===================== cards ===================== */
.card{background:var(--card);border:1px solid var(--border);border-radius:16px;
  box-shadow:var(--shadow-sm);margin-bottom:var(--gap);overflow:hidden}
.card > .head{display:flex;align-items:center;gap:var(--s2);padding:14px var(--s4) 12px;
  border-bottom:1px solid var(--border);font-size:11px;font-weight:700;
  letter-spacing:.09em;text-transform:uppercase;color:var(--text-muted)}
.card > .head::before{content:"";width:6px;height:6px;border-radius:99px;background:var(--dot,var(--primary));flex:none}
.card > .body{padding:var(--s4)}
.head .hint{margin-inline-start:auto;font-weight:500;color:var(--text-subtle);font-size:11px;text-transform:none;letter-spacing:0}
.card.a-1{--dot:#3E9E77}.card.a-2{--dot:#4E8FCB}.card.a-3{--dot:#C4657F}
.card.a-4{--dot:#C99A3C}.card.a-5{--dot:#7B6CC4}
/* section colour lives in the header dot only; borders stay neutral */
.grid2{display:grid;grid-template-columns:1fr 1fr;gap:var(--gap)}
@media (max-width:820px){.grid2{grid-template-columns:1fr}}

/* ===================== inputs ===================== */
.field{width:100%;background:var(--input-bg);border:1px solid transparent;border-radius:10px;
  padding:10px 13px;min-height:44px;transition:border-color .12s,box-shadow .12s,background .12s}
.field::placeholder{color:var(--text-subtle)}
.field:focus{outline:none;border-color:var(--primary);box-shadow:0 0 0 3px var(--ring)}
textarea.field{resize:vertical;min-height:92px;line-height:1.6}
input[type=date].field{max-width:180px}

/* ===================== today ===================== */
/* Every tile carries its own mood's tint at rest instead of one grey for
   all five, so the row reads as a scale before a word of it is read.
   --mt / --mb / --md are mixed per tile in js/view-today.js, which is why
   this works on any of the twelve accents without naming a colour. */
.moods{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:var(--s3);margin-top:var(--s2)}
.mood{min-height:118px;border:1px solid transparent;background:var(--mt);
  border-radius:14px;font-size:30px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;
  transition:transform .12s,background .12s,border-color .12s}
.mood small{font-size:11.5px;font-weight:600;color:var(--text-muted);letter-spacing:.01em}
.mood:hover{border-color:var(--border-strong)}
.mood:active{transform:scale(.97)}
.mood[aria-pressed="true"]{background:var(--mb);border-color:var(--md);box-shadow:0 0 0 1px var(--md)}
.mood[aria-pressed="true"] small{color:var(--text)}
/* Present on every tile and coloured on one. Adding the dot only to the
   selected tile would make that tile taller than its four neighbours and
   the whole row would step every time the mood changed. */
.mood::after{content:"";width:5px;height:5px;border-radius:99px;background:transparent;flex:none}
.mood[aria-pressed="true"]::after{background:var(--md)}
@media (max-width:560px){
  .moods{gap:var(--s2)}
  .mood{min-height:92px;font-size:25px;gap:6px}
  .mood small{font-size:10px}
}

/* ---- the twelve-day row ----
   Today and the signed-out demo both draw it, from one builder in
   js/helpers.js — the gate is a picture of the app, so the two have to be
   the same row or the picture is of an app that no longer exists.

   The joining line is two flex hairlines either side of each chip rather
   than one absolutely-positioned rule. That way it meets the chip's
   centre because it is a sibling of the chip, not because a magic offset
   was measured once against one font and one line-height. */
.moodline{display:flex;gap:2px}
.mlday{flex:1;min-width:0;background:none;border:0;padding:0;
  display:flex;flex-direction:column;align-items:center;gap:7px}
.mldow{font-size:10px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;
  color:var(--text-subtle);line-height:1}
.mlband{display:flex;align-items:center;width:100%}
.mlband i{flex:1;height:1px;background:var(--border)}
.mlday:first-child .mlband i:first-child,
.mlday:last-child .mlband i:last-child{background:transparent}
.mlchip{flex:none;width:26px;height:26px;border-radius:99px;
  border:1px solid var(--border);background:var(--input-bg);
  display:grid;place-content:center;font-size:13px;line-height:1}
/* A day written up but left without a mood still happened. */
.mlchip.filled{background:var(--border-strong);border-color:var(--border-strong)}
.mlnum{font-size:11px;line-height:1;color:var(--text-subtle);font-variant-numeric:tabular-nums}
.mlday[data-today="1"] .mlnum{color:var(--text);font-weight:700}
.mlday[data-sel="1"] .mlchip{outline:2px solid var(--primary);outline-offset:2px}
@media (max-width:560px){
  .moodline{gap:0}
  .mlchip{width:22px;height:22px;font-size:11px}
  .mldow{font-size:9px}
  .mlnum{font-size:10px}
}


.rows{display:flex;flex-direction:column;gap:var(--s2)}
.row{display:flex;align-items:center;gap:var(--s2)}
.bullet{color:var(--text-subtle);font-size:12px;width:14px;text-align:center;flex:none}
.check{appearance:none;width:22px;height:22px;flex:none;border:1px solid var(--border-strong);border-radius:var(--radius-sm);
  background:var(--input-bg);display:grid;place-content:center;transition:background .12s,border-color .12s}
.check:checked{background:var(--primary);border-color:var(--primary)}
.check:checked::after{content:"✓";font-size:12px;color:var(--primary-contrast);font-weight:700}
.row.done .field{color:var(--text-subtle);text-decoration:line-through}
.del{border:0;background:transparent;color:var(--text-subtle);font-size:16px;line-height:1;padding:9px;border-radius:var(--radius-sm);flex:none}
.del:hover{background:color-mix(in srgb,var(--m1) 14%,transparent);color:var(--text)}
.chips{display:flex;flex-wrap:wrap;gap:var(--s2)}
.chip{border:1px solid var(--border);background:var(--card);border-radius:999px;padding:0 16px;height:40px;font-size:13.5px;font-weight:500}
.chip:hover{border-color:var(--border-strong)}
.chip[aria-pressed="true"]{background:var(--primary-soft);border-color:var(--primary-soft-border);color:var(--text)}

/* ===================== calendar ===================== */
.cal-head{display:flex;align-items:center;gap:var(--s2);margin-bottom:var(--s4)}
.cal-title{font-family:var(--serif);font-size:30px;font-weight:600;letter-spacing:-.02em;margin-inline-end:auto;line-height:1.1}
.dow{display:grid;grid-template-columns:repeat(7,1fr);gap:6px;color:var(--text-subtle);font-size:10.5px;
  font-weight:500;text-align:center;margin-bottom:6px;letter-spacing:.06em;text-transform:uppercase}
.cal{display:grid;grid-template-columns:repeat(7,1fr);gap:6px}
.cell{aspect-ratio:1/1;border:1px solid var(--border);background:var(--card);border-radius:13px;
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;font-size:13px;font-weight:500}
.cell:hover{border-color:var(--border-strong)}
.cell.blank{visibility:hidden}
.cell.future{opacity:.4}
.cell .dot{width:7px;height:7px;border-radius:99px;background:var(--input-bg)}
.cell.today{border-color:var(--text);box-shadow:0 0 0 1px var(--text)}
.legend{display:flex;flex-wrap:wrap;gap:var(--s3);margin-top:var(--s4);color:var(--text-muted);font-size:12px;align-items:center}
.legend i{display:inline-block;width:10px;height:10px;border-radius:3px;margin-inline-end:5px;vertical-align:-1px}

/* ===================== log ===================== */
.toolbar{display:flex;gap:var(--s2);flex-wrap:wrap;align-items:center;margin-bottom:var(--s3)}
.search{flex:1;min-width:200px;height:44px}
.logrow{display:flex;gap:var(--s3);padding-block:13px;padding-inline:13px 16px;border:1px solid var(--border);border-inline-start-width:4px;
  background:var(--card);border-radius:14px;margin-bottom:8px;align-items:center;width:100%;text-align:start;
  box-shadow:var(--shadow-sm)}
.logrow:hover{background:var(--card-hover);border-color:var(--border-strong);border-inline-start-color:var(--edge,var(--border-strong))}
.logdate{flex:none;width:50px;text-align:center;line-height:1.15}
.logdate i{display:block;font-style:normal;font-size:9.5px;font-weight:700;letter-spacing:.09em;
  text-transform:uppercase;color:var(--text-subtle)}
.logdate b{display:block;font-family:var(--serif);font-size:24px;font-weight:600;color:var(--text);margin:1px 0}
.logdate u{display:block;text-decoration:none;font-size:10px;font-weight:600;color:var(--text-subtle);
  letter-spacing:.05em;text-transform:uppercase}
.logbody{flex:1;font-size:14px;white-space:pre-wrap;word-break:break-word}
.logbody .meta{display:block;color:var(--text-muted);font-size:12px;margin-top:3px}
.logbody b{font-weight:600}
.moodtag{display:flex;align-items:center;gap:var(--s2);font-weight:600}
.moodtag em{font-style:normal;font-size:17px}
.tick{flex:none;width:18px;text-align:center;font-size:13px}
.tick.no{color:var(--text-subtle)}
.tick.yes{color:var(--primary)}
.pray-line{display:flex;gap:4px;flex-wrap:wrap}
.pill{font-size:11px;font-weight:600;border-radius:999px;padding:3px 10px;border:1px solid var(--border);color:var(--text-subtle)}
.pill.on{background:var(--primary-soft);border-color:var(--primary-soft-border);color:var(--text)}
.empty{text-align:center;color:var(--text-muted);padding:28px 14px;font-size:13.5px}
.count{color:var(--text-muted);font-size:12.5px;margin:0 0 var(--s3)}

/* ===================== select ===================== */
select.select{appearance:none;-webkit-appearance:none;cursor:pointer;padding-inline-end:38px;
  background-image:linear-gradient(45deg,transparent 50%,currentColor 50%),
    linear-gradient(135deg,currentColor 50%,transparent 50%);
  background-position:calc(100% - 18px) calc(50% + 1px),calc(100% - 13px) calc(50% + 1px);
  background-size:5px 5px,5px 5px;background-repeat:no-repeat;color:var(--text)}
select.select:invalid,select.select.placeholder{color:var(--text-subtle)}
select.select option{background:var(--card);color:var(--text)}
select.select optgroup{background:var(--card);color:var(--text-subtle);font-size:12px;font-weight:600}

/* ===================== bar lists ===================== */
.barlist{display:flex;flex-direction:column;gap:11px}
/* The label column was a flat 74px, so "Accomplished" and "Overwhelmed"
   ellipsised away on every render. It can now take the width it needs
   up to a cap, and the bar keeps whatever is left. */
.barrow{display:grid;grid-template-columns:minmax(72px,132px) 1fr 34px;align-items:center;gap:12px}
.barrow .lbl{font-size:13px;font-weight:500;color:var(--text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.barrow .track{height:9px;border-radius:99px;background:var(--input-bg);overflow:hidden}
.barrow .fill{display:block;height:100%;border-radius:99px;background:var(--c);
  box-shadow:0 0 10px -1px var(--c);transition:width .45s ease}
.barrow .val{font-size:13px;font-weight:600;color:var(--text-muted);text-align:end;font-variant-numeric:tabular-nums}
html[data-resolved="dark"] .barrow .track{background:var(--card-hover)}
html[data-resolved="dark"] .barrow .fill{box-shadow:0 0 12px 0 var(--c)}

/* ===================== insights ===================== */
.stats{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--gap);margin-bottom:var(--gap)}
@media (max-width:820px){.stats{grid-template-columns:repeat(2,1fr)}}
.stat{background:var(--card);border:1px solid var(--border);border-radius:16px;padding:var(--s4) var(--s4) 18px;box-shadow:var(--shadow-sm)}
.stat b{display:block;font-family:var(--serif);font-size:32px;font-weight:600;letter-spacing:-.02em;line-height:1.1}
.stat span{color:var(--text-muted);font-size:12px;font-weight:500}
.bar{display:block;height:6px;background:var(--input-bg);border-radius:99px;margin-top:var(--s2);overflow:hidden}
.bar.warn i{background:var(--m2)}
.bar.crit i{background:var(--m1)}
.bar i{display:block;height:100%;background:var(--primary);border-radius:99px}
.chartbox{height:250px;position:relative}
.note{color:var(--text-muted);font-size:12.5px}
.review{display:grid;grid-template-columns:repeat(auto-fit,minmax(104px,1fr));gap:var(--s4)}
.review div span{display:block;color:var(--text-muted);font-size:11.5px}
.review div b{font-size:18px;font-weight:600;letter-spacing:-.02em}
.wins{margin:var(--s4) 0 0;padding-inline-start:18px;font-size:13.5px}
.wins li{margin-bottom:4px}
.heatwrap{overflow-x:auto;padding-bottom:6px}
/* One block per month with real space between them. A single continuous
   grid had to begin on whatever weekday Jan 1 fell on, so the year
   started part-way down the first column and the months ran together
   with nothing marking where one ended. */
.heatyear{display:flex;gap:16px;align-items:flex-start;width:max-content}
.heatmonth{display:flex;flex-direction:column;gap:5px}
.heatmonth .hm{font:500 10px/1 var(--sans);color:var(--text-subtle);letter-spacing:.04em}
.hmgrid{display:grid;grid-auto-flow:column;grid-template-rows:repeat(7,12px);gap:3px;justify-content:start}
.heatcell{width:12px;height:12px;border-radius:3px;border:1px solid var(--border);
  background:var(--input-bg);padding:0;display:block}
.heatcell.on{border-color:transparent}
.streaks{width:100%;border-collapse:collapse;font-size:13px;margin-top:var(--s3)}
.streaks th{color:var(--text-muted);font-weight:500;font-size:11px;text-transform:uppercase;letter-spacing:.06em;
  text-align:start;padding:8px 8px;border-bottom:1px solid var(--border)}
.streaks td{padding:9px 8px;border-bottom:1px solid var(--border)}
.streaks tbody tr:hover{background:var(--card-hover)}
.streaks td:not(:first-child),.streaks th:not(:first-child){text-align:end}

/* ===================== drawer ===================== */
.scrim{position:fixed;inset:0;background:var(--overlay);z-index:30}
.scrim[hidden],.drawer[hidden]{display:none}
.drawer{position:fixed;inset-inline-end:0;top:0;bottom:0;width:min(400px,92vw);background:var(--card);z-index:31;
  border-inline-start:1px solid var(--border);overflow:auto;padding:var(--s5) var(--s4) 44px}
.drawer h2{font-size:18px;margin-bottom:2px}
.drawer h3{font-size:11px;text-transform:uppercase;letter-spacing:.07em;color:var(--text-subtle);margin:var(--s6) 0 var(--s2);font-weight:600}
.toggle{display:flex;align-items:center;gap:var(--s3);padding:11px 0;border-bottom:1px solid var(--border);font-size:14px}
.toggle input{margin-inline-start:auto;width:18px;height:18px;accent-color:var(--primary)}
.seg.full{display:flex;width:100%}
.seg.full button{flex:1}

/* ===================== misc ===================== */
#toast{position:fixed;left:50%;transform:translateX(-50%) translateY(10px);bottom:calc(20px + var(--nav-h));
  background:var(--card);color:var(--text);border:1px solid var(--border);box-shadow:0 8px 24px -12px rgba(0,0,0,.4);
  padding:9px 16px;border-radius:var(--radius);font-size:13px;font-weight:500;opacity:0;pointer-events:none;
  transition:opacity .18s,transform .18s;z-index:40}
#toast.show{opacity:1;transform:translateX(-50%) translateY(0)}
#toast{display:flex;align-items:center;gap:var(--s3);max-width:calc(100vw - 32px)}
#toast.has-action{pointer-events:auto;padding-inline-end:8px}
#toast .undo{border:0;background:transparent;color:var(--primary);font-weight:600;font-size:13px;
  padding:6px 8px;border-radius:var(--radius-sm);white-space:nowrap}
#toast .undo:hover{background:var(--primary-soft)}
.banner{max-width:1080px;margin:var(--s3) auto 0;padding:11px var(--s4);border:1px solid var(--border);
  background:var(--card);border-radius:var(--radius);font-size:13px;display:flex;gap:var(--s2);align-items:center;flex-wrap:wrap}
.banner.warn{border-color:color-mix(in srgb,var(--m2) 45%,var(--border));background:color-mix(in srgb,var(--m2) 9%,var(--card))}
.banner .btn{height:32px;font-size:12.5px;padding:0 12px}
/* ---- the daily line ----
   Nothing that says "object". The first attempt was a tinted panel with
   a big quotation glyph, which is the grammar of an alert — the wrong
   thing to say about a sentence of encouragement — and it wore fill,
   border and radius all at once without being a card, which cost the
   real cards below it their weight.

   Two changes carry the rest. The rule replaces the panel: a marginal
   mark, drawn in the accent so it belongs to whatever primary is chosen,
   and mixed down because at full strength a 2px accent rule reads as a
   status stripe. And the type comes off the serif, which was fighting
   the serif date twenty pixels above it for the same eye; sans italic at
   14.5 is quiet enough to be read on the way past and quiet enough to be
   passed. The reduced-motion rule at the top of this file already
   removes the fade, so there is none here. */
/* An actual .card, head row and all, rather than a lookalike built out
   of the same four properties. Two earlier attempts went wrong here in
   opposite directions: a coloured panel, which in this app is the grammar
   of an alert, and then a bare rule, which was quiet enough to skip. A
   card with the app's own header is neither, and it needs no bespoke
   geometry to hold together — the .card rules already do that.

   Only two things are local to it. The body is roomier than a form card
   needs, because one line of text in a standard 16px body sits marooned.
   And the type is italic, muted, and pointedly not serif: the serif date
   is directly above this, and two display voices that close together
   fight for the same eye. */
.dailyLine > .body{padding:var(--s5) var(--s4)}
/* Serif here, which it could not be while it sat directly under the
   serif date — the twelve-day card is between them now, so there is no
   longer one eye choosing between two display faces. The marks are drawn
   by CSS so the text node stays exactly what the script writes to it. */
.quote{margin:0;max-width:58ch;font-family:var(--serif);font-size:22px;font-style:italic;
  line-height:1.4;letter-spacing:-.01em;color:var(--text);transition:opacity .45s}
.quote::before{content:"\201C"}
.quote::after{content:"\201D"}
.quote.fade{opacity:0}
/* The rule does the work an em dash would, without the em dash's habit
   of hanging at a different height in every serif. */
.quoteBy{display:flex;align-items:center;gap:var(--s2);margin:var(--s4) 0 0;
  font-size:12px;color:var(--text-subtle)}
.quoteBy::before{content:"";flex:none;width:18px;height:1px;background:var(--border-strong)}
@media (max-width:560px){
  .dailyLine > .body{padding:var(--s4)}
  .quote{font-size:19px}
}

/* ---- footer ----
   A footer on a signed-in app is not the footer of a website. There is
   nobody here to persuade and nothing to claim copyright over, so the
   wordmark, the tagline and the year are all gone — every one of them was
   addressed to a stranger, and this page only ever has one reader.

   What is left is the two things that reader might actually want at the
   bottom of a long page: what is in the account and whether it is safe,
   and a way back to the top without scrolling the whole way. The dot
   carries the state as colour as well as words, so it answers before it
   is read. */
footer{max-width:1080px;margin:var(--s6) auto 0;padding:var(--s5) var(--s4) var(--s6);
  border-top:1px solid var(--border);
  display:flex;align-items:flex-end;justify-content:space-between;gap:var(--s4);flex-wrap:wrap}
.footId{min-width:0}
.footName{font-family:var(--serif);font-size:16px;font-weight:600;
  letter-spacing:-.01em;color:var(--text)}
.footBrief{margin:5px 0 0;max-width:52ch;font-size:12.5px;line-height:1.55;color:var(--text-subtle)}
/* Reads as one line with the brief on a wide screen; on a narrow one it
   drops under and keeps its own baseline rather than crowding it. */
.footState{display:flex;align-items:center;gap:var(--s2);margin:0;
  font-size:12px;color:var(--text-subtle);white-space:nowrap}
.footDot{width:6px;height:6px;border-radius:99px;background:var(--m5);flex:none}
@media (max-width:560px){
  footer{flex-direction:column;align-items:flex-start;gap:var(--s3)}
}
.print-only{display:none}

/* ===================== dark colour lift ===================== */
html[data-resolved="dark"] .mood{background:var(--mt);border-color:color-mix(in srgb,var(--md) 24%,transparent)}
html[data-resolved="dark"] .mood:hover{border-color:color-mix(in srgb,var(--md) 55%,transparent)}
html[data-resolved="dark"] .mood small{color:color-mix(in srgb,var(--md) 62%,var(--text))}
html[data-resolved="dark"] .mood[aria-pressed="true"]{
  background:color-mix(in srgb,var(--md) 24%,var(--card));
  box-shadow:0 0 0 1px var(--md), 0 8px 22px -12px var(--md)}
html[data-resolved="dark"] .chip[aria-pressed="true"]{box-shadow:0 0 0 1px var(--primary-soft-border)}
html[data-resolved="dark"] .seg{background:var(--card);border-color:var(--border)}
html[data-resolved="dark"] .seg button[aria-pressed="true"],
html[data-resolved="dark"] .seg button[aria-selected="true"]{
  background:var(--card-hover);border-color:var(--border-strong);color:var(--text)}
html[data-resolved="dark"] .cell.today{box-shadow:0 0 0 1px var(--primary)}
html[data-resolved="dark"] .logrow{box-shadow:none}
html[data-resolved="dark"] .logrow:hover{box-shadow:0 0 0 1px var(--edge,var(--border-strong))}
html[data-resolved="dark"] .tag{background:color-mix(in srgb,var(--primary) 16%,transparent)}
html[data-resolved="dark"] .stat b{color:var(--text)}
html[data-resolved="dark"] .topbar{background:var(--bg)}

/* ===================== habit toggles ===================== */
.habit{display:flex;align-items:center;gap:13px;width:100%;text-align:start;
  background:var(--input-bg);border:1px solid transparent;border-radius:12px;padding:14px 15px;min-height:60px}
.habit:hover{border-color:var(--border-strong)}
.habit[aria-pressed="true"]{background:var(--primary-soft);border-color:var(--primary-soft-border)}
.habit .mark{width:26px;height:26px;flex:none;border-radius:8px;border:1.5px solid var(--border-strong);
  display:grid;place-content:center;font-size:14px;color:transparent}
.habit[aria-pressed="true"] .mark{background:var(--primary);border-color:var(--primary);color:var(--primary-contrast)}
.habit .txt{flex:1;min-width:0}
.habit .txt b{display:block;font-size:14.5px;font-weight:600}
.habit .txt span{display:block;font-size:12.5px;color:var(--text-muted);margin-top:2px}
.habit .glyph{font-size:20px;flex:none;opacity:.55}
.habit[aria-pressed="true"] .glyph{opacity:1}

/* ===================== to-do ===================== */
.tiles{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:12px}
.tile{position:relative;text-align:start;background:var(--card);border:1px solid var(--border);
  border-radius:16px;padding:16px 16px 14px;box-shadow:var(--shadow-sm);min-height:96px;
  display:flex;flex-direction:column;justify-content:flex-end}
/* Reordering. touch-action:none is what lets a finger drag a tile instead
   of scrolling the page with it — but claiming that from the first touch
   is what made the grid unscrollable on phones/tablets. pan-y is the
   default so a vertical swipe scrolls normally; view-todo.js only adds
   .drag-armed (which switches to touch-action:none) once a touch has been
   held in place long enough to count as a drag rather than a scroll.
   Mouse/pen pointers skip the hold and arm immediately, same as before. */
#todoLists .tile{touch-action:pan-y;-webkit-touch-callout:none;
  -webkit-user-select:none;user-select:none}
#todoLists .tile.drag-armed{touch-action:none}
#todoLists .tile.dragging{opacity:.9;z-index:5;position:relative;cursor:grabbing;
  box-shadow:0 12px 28px -8px rgba(0,0,0,.55);transition:none}
body.dragging-list{cursor:grabbing;user-select:none;-webkit-user-select:none}
#todoLists .tile{transition:transform .16s ease}
#todoLists .tile.dragging{transition:none}
.tile:hover{background:var(--card-hover);border-color:var(--border-strong)}
.tile .tglyph{position:absolute;top:14px;inset-inline-start:16px;font-size:19px;color:var(--tint);line-height:1}
.tile .tnum{position:absolute;top:12px;inset-inline-end:16px;font-family:var(--serif);font-size:27px;
  font-weight:600;letter-spacing:-.02em;line-height:1}
.tile .tlabel{font-size:14px;font-weight:500;color:var(--text-muted);margin-top:18px}
.listhead{display:flex;align-items:center;gap:10px;margin:26px 0 12px}
.listhead span{font-size:11px;font-weight:700;letter-spacing:.09em;text-transform:uppercase;color:var(--text-subtle)}
.listhead .btn{margin-inline-start:auto}

.todorow{display:flex;align-items:center;gap:12px;padding:11px 14px 11px 12px;margin-bottom:8px;
  background:var(--card);border:1px solid var(--border);border-inline-start:4px solid var(--tint);
  border-radius:13px;box-shadow:var(--shadow-sm)}
.todorow:hover{border-color:var(--border-strong)}
.tcheck{width:24px;height:24px;flex:none;border-radius:99px;border:1.5px solid var(--tint);
  background:transparent;display:grid;place-content:center;font-size:12px;color:var(--tint);padding:0}
.todorow.done .tcheck{background:var(--tint);color:var(--card);border-color:var(--tint)}
.todorow.done .ttext{color:var(--text-subtle);text-decoration:line-through}
/* The text has always been a live input — click it and type. Nothing
   said so, though: no border, no caret hint, and focus removed the only
   outline there was. So it read as a label, and editing a task looked
   like something the app could not do. The pencil is the affordance and
   the underline is the confirmation. */
.ttext{flex:1;min-width:0;background:transparent;border:0;padding:6px 0;font-size:15px;
  border-bottom:1px solid transparent}
.ttext:hover{border-bottom-color:var(--border)}
.ttext:focus{outline:none;border-bottom-color:var(--primary)}
.todorow.done .ttext:hover,.todorow.done .ttext:focus{border-bottom-color:transparent}
.tmeta{display:flex;align-items:center;gap:8px;flex:none}
.tchip{font-size:11px;font-weight:600;white-space:nowrap}
.tedit{border:0;background:transparent;color:var(--text-subtle);line-height:0;
  padding:9px;border-radius:var(--radius-sm);flex:none;display:grid;place-content:center}
.tedit:hover{background:var(--card-hover);color:var(--text)}
/* Hidden until the row is pointed at, so a long list stays a list of
   tasks rather than a wall of buttons. Only where hovering is a thing
   a pointer can do — on a touch screen there is no hover to reveal it
   with, so it is simply always there. Focus reveals it too, or it would
   be unreachable by keyboard. */
@media (hover:hover){
  .tedit{opacity:0;transition:opacity .12s}
  .todorow:hover .tedit,.tedit:focus-visible,.todorow:focus-within .tedit{opacity:1}
}
.tdue{background:transparent;border:1px solid transparent;border-radius:8px;padding:4px 6px;
  font-size:12px;color:var(--text-subtle);min-height:32px;max-width:132px}
.tdue:hover{border-color:var(--border)}
.tdue:focus{outline:none;border-color:var(--primary)}
.tdue.over{color:var(--m1)}
@media (max-width:560px){
  .tdue{max-width:38px;padding:4px 2px}
  .tchip{display:none}
  /* always on a touch screen, so it has to earn its width */
  .tedit{padding:9px 4px}
  .tmeta{gap:4px}
}

.grouphead{display:flex;align-items:center;gap:10px;width:100%;text-align:start;background:transparent;
  border:0;padding:22px 2px 10px;color:var(--tint)}
.grouphead .gglyph{font-size:15px;line-height:1}
.grouphead .gname{font-size:13px;font-weight:700;letter-spacing:.07em;text-transform:uppercase}
.grouphead .gcount{margin-inline-start:auto;font-size:12px;font-weight:600;color:var(--text-subtle)}
.grouphead:hover .gname{text-decoration:underline}
.fab{position:fixed;inset-inline-end:20px;bottom:calc(20px + var(--nav-h) + env(safe-area-inset-bottom));
  width:56px;height:56px;border-radius:999px;border:0;background:var(--primary);color:var(--primary-contrast);
  font-size:28px;line-height:1;box-shadow:0 8px 24px -8px rgba(0,0,0,.5);z-index:22}
.fab:hover{background:var(--primary-hover)}

/* ===================== theme pickers ===================== */
.swatches{display:flex;flex-wrap:wrap;gap:8px}
.swatch{width:32px;height:32px;min-height:32px;border-radius:9px;border:1px solid var(--border);
  padding:0;position:relative;cursor:pointer;transition:transform .12s}
.swatch:hover{transform:scale(1.08)}
.swatch[aria-pressed="true"]{box-shadow:0 0 0 2px var(--card),0 0 0 4px var(--text)}
.swatch[aria-pressed="true"]::after{content:"✓";position:absolute;inset:0;display:grid;place-content:center;
  font-size:14px;font-weight:700;color:var(--sw-tick,#fff);text-shadow:0 1px 2px rgba(0,0,0,.4)}
.swrow{margin-bottom:6px;font-size:11px;color:var(--text-subtle);font-weight:600;
  text-transform:uppercase;letter-spacing:.07em}

/* ===================== sleep ===================== */
.sleepwrap{display:grid;grid-template-columns:auto 1fr;gap:22px;align-items:center}
@media (max-width:560px){.sleepwrap{grid-template-columns:1fr;gap:16px;justify-items:center}}
.ring{position:relative;width:118px;height:118px;flex:none}
.ring svg{transform:rotate(-90deg);width:118px;height:118px;display:block}
.ring .trk{fill:none;stroke:var(--input-bg);stroke-width:9}
.ring .arc{fill:none;stroke:var(--sleep-c,var(--primary));stroke-width:9;stroke-linecap:round;
  transition:stroke-dashoffset .5s ease,stroke .3s ease}
.ring .mid{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:1px}
.ring .num{font-family:var(--serif);font-size:33px;font-weight:600;line-height:1;letter-spacing:-.02em}
.ring .num.none{color:var(--text-subtle);font-size:26px}
.ring .band{font-size:10px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--text-subtle)}
.sleepfields{width:100%;display:flex;flex-direction:column;gap:14px}
.sleepfield label{display:block;font-size:11px;font-weight:700;letter-spacing:.08em;
  text-transform:uppercase;color:var(--text-subtle);margin-bottom:7px}
.sleeprow{display:flex;align-items:center;gap:10px}
.sleeprow .field{max-width:110px}
.sleeprow input[type=range]{flex:1;accent-color:var(--primary);height:26px;min-width:80px;
  transition:opacity .15s}
.sleeprow input[type=range]:disabled{opacity:.35;accent-color:var(--border-strong);cursor:not-allowed}
.sleepnote{color:var(--text-subtle);font-size:12px}
.rsleep{display:flex;align-items:center;gap:18px;flex-wrap:wrap}
.rsleep .big{font-family:var(--serif);font-size:30px;font-weight:600;line-height:1}
.rsleep .cap{font-size:11px;font-weight:700;letter-spacing:.09em;text-transform:uppercase;color:var(--text-subtle)}
.rsleep .sband{display:inline-block;margin-inline-start:9px;font-size:13.5px;font-weight:600;vertical-align:3px}

/* ===================== review ===================== */
.daycard{display:flex;gap:14px;align-items:flex-start;width:100%;text-align:start;
  padding-block:15px;padding-inline:13px 16px;background:var(--card);border:1px solid var(--border);border-inline-start-width:4px;
  border-radius:14px;margin-bottom:9px;box-shadow:var(--shadow-sm)}
.daycard:hover{background:var(--card-hover);border-color:var(--border-strong)}
.dstamp{flex:none;width:52px;text-align:center;line-height:1.15}
.dstamp i{display:block;font-style:normal;font-size:9.5px;font-weight:700;letter-spacing:.09em;
  text-transform:uppercase;color:var(--text-subtle)}
.dstamp b{display:block;font-family:var(--serif);font-size:25px;font-weight:600;color:var(--text);margin:1px 0}
.dstamp u{display:block;text-decoration:none;font-size:10px;font-weight:600;color:var(--text-subtle);
  letter-spacing:.05em;text-transform:uppercase}
.daycard .dmain{flex:1;min-width:0}
.daycard .dmood{display:flex;align-items:center;gap:8px;font-weight:600;font-size:14.5px}
.daycard .dmood em{font-style:normal;font-size:19px}
.daycard .dsum{color:var(--text-muted);font-size:13.5px;margin-top:4px;overflow:hidden;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}
.daycard .dbits{display:flex;flex-wrap:wrap;gap:5px;margin-top:9px}
.bit{font-size:11px;font-weight:500;color:var(--text-subtle);background:var(--input-bg);
  border-radius:999px;padding:3px 9px;white-space:nowrap}

/* The Review toolbar. The dropdown picks what is listed, the segmented
   control picks over what period — one row, one job each. They are
   deliberately different shapes: two segmented bars side by side read as
   two halves of one broken control, which is exactly how this looked
   before the dropdown. On a narrow screen the row wraps and the seg
   keeps the full width it already wanted. */
.rbar{display:flex;align-items:center;justify-content:space-between;
  gap:var(--s3);flex-wrap:wrap;margin-bottom:var(--s3)}
.rbar .segwrap{margin-bottom:0}
.rpick{display:flex;align-items:center;gap:9px;min-width:0}
.rpick>span{font-size:12.5px;font-weight:600;color:var(--text-muted);white-space:nowrap}
.rpick select{width:auto;min-width:170px;max-width:100%;min-height:40px;padding-top:7px;padding-bottom:7px}
@media (max-width:560px){
  .rbar{gap:var(--s2)}
  .rpick{width:100%}
  .rpick select{flex:1}
  .rbar .segwrap{width:100%}
}

/* A browse row: same stamp and same left-edge mood colour as a day card,
   but it is not itself a button — the card holds one written line and the
   only clickable thing in it is the button that opens the day. */
.bcard{display:flex;gap:14px;align-items:flex-start;
  padding:13px 12px 13px 13px;background:var(--card);border:1px solid var(--border);
  border-inline-start-width:4px;border-radius:14px;margin-bottom:9px;box-shadow:var(--shadow-sm)}
.bcard .bmain{flex:1;min-width:0}
.bcard .btext{margin:0;display:flex;gap:9px;font-size:14px;line-height:1.55;color:var(--text)}
.bcard .btext .mk{flex:none;width:14px;text-align:center;color:var(--text-subtle);font-weight:700}
.bcard .btext .tx{min-width:0;overflow-wrap:anywhere}
/* a journal entry is not a list item — clamp it rather than let one day
   push the rest of the list off the screen */
.bcard .btext.prose .tx{display:-webkit-box;-webkit-line-clamp:4;-webkit-box-orient:vertical;overflow:hidden}
.bcard .btext.done .tx{color:var(--text-muted);text-decoration:line-through}
.bcard .dbits{display:flex;flex-wrap:wrap;gap:5px;margin-top:8px}
.bcard .bgo{flex:none;align-self:center;white-space:nowrap;padding-inline-start:8px;padding-inline-end:8px}
/* On a phone the button drops under the text instead of standing beside
   it. Wrapping alone is not enough — .bmain would rather shrink than
   wrap — so it is given the whole width left by the stamp, which leaves
   the button nowhere to go but the next line. 66px is the stamp plus the
   gap, so the button lines up under the text it belongs to. */
@media (max-width:560px){
  .bcard{flex-wrap:wrap;padding-bottom:9px}
  .bcard .bmain{min-width:calc(100% - 66px)}
  .bcard .bgo{align-self:flex-start;margin-inline-start:66px;margin-top:2px;padding-inline-start:0}
}

.dayhead{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:16px}
.dayhead .spacer{flex:1}
.rday{background:var(--card);border:1px solid var(--border);border-radius:16px;
  box-shadow:var(--shadow-sm);padding:24px 22px 26px;margin-bottom:14px}
.rtop{border-bottom:1px solid var(--border);padding-bottom:18px;margin-bottom:20px}
.rdate{font-size:11px;font-weight:700;letter-spacing:.11em;text-transform:uppercase;color:var(--text-subtle)}
.rtitle{font-family:var(--serif);font-size:31px;font-weight:600;letter-spacing:-.02em;line-height:1.14;margin:3px 0 0}
.rmood{display:flex;align-items:center;gap:11px;margin-top:14px}
.rmood em{font-style:normal;font-size:34px;line-height:1}
.rmood .lab{font-size:17px;font-weight:600}
.rmood .felt{color:var(--text-muted);font-size:14px}
.rmood.solo{margin-top:18px;flex-direction:column;align-items:flex-start;gap:6px}
.rmood.solo em{font-size:54px}
.rsec{margin-top:26px}
.rsec h3{font-size:11px;font-weight:700;letter-spacing:.09em;text-transform:uppercase;
  color:var(--text-subtle);margin:0 0 10px}
.rsec h3::before{content:"";display:inline-block;width:6px;height:6px;border-radius:99px;
  background:var(--dot,var(--primary));margin-inline-end:8px;vertical-align:1px}
.rprose{font-size:15.5px;line-height:1.72;white-space:pre-wrap;word-break:break-word;max-width:68ch;color:var(--text)}
.rlist{list-style:none;margin:0;padding:0;font-size:15px}
.rlist li{display:flex;gap:10px;padding:5px 0;line-height:1.5}
.rlist li .mk{flex:none;color:var(--text-subtle);width:16px;text-align:center;font-size:13px}
.rlist li.done .tx{color:var(--text-muted);text-decoration:line-through}
.rlist li.done .mk{color:var(--primary)}
.rpair{display:grid;grid-template-columns:1fr 1fr;gap:26px}
@media (max-width:640px){.rpair{grid-template-columns:1fr}}
.rprayers{display:flex;flex-wrap:wrap;gap:7px}
.rpray{font-size:12.5px;font-weight:500;border-radius:999px;padding:5px 12px;
  border:1px solid var(--border);color:var(--text-subtle)}
.rpray.on{background:var(--primary-soft);border-color:var(--primary-soft-border);color:var(--text)}
.rtags{display:flex;flex-wrap:wrap;gap:7px;margin-top:24px;padding-top:18px;border-top:1px solid var(--border)}
.rtags button{border:0;background:transparent;padding:0;cursor:pointer}
.rfoot{color:var(--text-subtle);font-size:12.5px;margin-top:18px;padding-top:16px;border-top:1px solid var(--border)}

/* ===================== hero + view titles ===================== */
.viewtitle{font-family:var(--serif);font-size:34px;font-weight:600;letter-spacing:-.02em;margin:2px 0 18px}
.viewtitle span{color:var(--text-subtle)}
/* The day, on the page rather than in a card — see index.html. */
.todayHead{display:flex;align-items:flex-end;justify-content:space-between;gap:var(--s3);
  flex-wrap:wrap;margin:var(--s2) 0 var(--s5)}
.todayHead .field{max-width:190px}
/* Serif italic and muted, not sans in the accent colour: it is an aside
   to the date, and an accent-coloured line above a heading competes with
   it for the first read. */
.heroHi{font-family:var(--serif);font-style:italic;font-size:14px;color:var(--text-muted);
  margin-bottom:6px;letter-spacing:0}
.heroHi[hidden]{display:none}
.acctHi{font-size:15px;font-weight:600;margin:0 0 2px;letter-spacing:-.02em}
.acctHi[hidden]{display:none}
.heroDate{font-family:var(--serif);font-size:clamp(28px,4.2vw,38px);font-weight:600;
  letter-spacing:-.025em;line-height:1.1}
/* The year is context, not headline: same face, dropped to a whisper. */
.heroYear{font-weight:400;font-size:.62em;color:var(--text-subtle);letter-spacing:-.01em}

/* On a phone the date was on screen twice — spelled out in full, then
   again as 09/04/2026 in a control wide enough to take its own row. The
   control has to stay: it is the only way back to another day. So it
   keeps its whole hit area and loses its text, and the row it was taking
   goes back to the date.

   The glyph is drawn here rather than left to the browser because iOS
   renders a date input with no picker indicator at all, which would have
   left an empty grey box. The native indicator is kept at zero opacity
   and stretched over the control, so the thing being tapped is still the
   real one. Neutral grey on purpose: this sits on --input-bg, and that
   token has twenty different values across the surface tones and themes. */
@media (max-width:560px){
  .todayHead{flex-wrap:nowrap;align-items:flex-start}
  .todayHead > div{min-width:0}
  .todayHead .field{flex:none;width:44px;max-width:44px;min-height:44px;padding:0;
    color:transparent;
    background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%238A8A98' stroke-width='1.7' stroke-linecap='round'><rect x='3.5' y='5' width='17' height='15.5' rx='2.5'/><path d='M3.5 9.6h17M8.4 3v4M15.6 3v4'/></svg>");
    background-repeat:no-repeat;background-position:center;background-size:20px 20px}
  .todayHead .field::-webkit-datetime-edit{display:none}
  .todayHead .field::-webkit-calendar-picker-indicator{opacity:0;width:100%;height:100%;
    margin:0;padding:0;cursor:pointer}
}

/* ===================== modal / lock ===================== */
.modal{position:fixed;inset:0;z-index:50;display:flex;align-items:center;justify-content:center;padding:var(--s4);background:var(--overlay)}
.modal[hidden]{display:none}
.sheet{background:var(--card);border:1px solid var(--border);border-radius:var(--radius-lg);
  width:100%;max-width:470px;max-height:88vh;overflow:auto;padding:var(--s5)}
.sheet h2{font-size:17px;margin-bottom:4px}
.sheet .field{margin-top:var(--s3)}
.sheet .row2{display:grid;grid-template-columns:1fr 1fr;gap:var(--s3);margin-top:var(--s3);align-items:end}
/* The label text sat inline before its input. Date fields are capped at
   180px, so "To" fitted beside its field and the longer "From" wrapped
   below its own — two controls the same shape, sitting at two different
   heights. Stacked, both read the same way. */
.sheet .row2 label{display:flex;flex-direction:column;gap:5px}
.sheet .row2 label .field{max-width:none;width:100%}
.sheet .actions{display:flex;gap:var(--s2);justify-content:flex-end;margin-top:var(--s5);flex-wrap:wrap}
.sheet label.toggle{padding:9px 0}
.err{color:var(--m1);font-size:12.5px;margin-top:var(--s2);min-height:17px}
.callout{background:var(--input-bg);border:1px solid var(--border);border-radius:var(--radius);
  padding:11px 13px;font-size:12.5px;color:var(--text-muted);margin-top:var(--s3)}

/* ===================== tags ===================== */
.tag{color:var(--primary);font-weight:600;background:var(--primary-soft);border-radius:999px;padding:1px 8px;font-size:12px;white-space:nowrap}
.tagbar{display:flex;align-items:center;gap:var(--s2);margin-bottom:var(--s3)}

/* The written half of a summary. Days break rather than split across a
   page, which matters more here than anywhere else in the app. */
.rtDay{padding:var(--s4) 0;border-top:1px solid var(--border);break-inside:avoid;page-break-inside:avoid}
.rtDay:first-child{border-top:0;padding-top:0}
.rtDay h4{margin:0 0 var(--s3);font-size:14px;font-weight:600}
.rtDay h4 span{color:var(--text-subtle);font-weight:400;font-size:12.5px;margin-inline-start:6px}
.rtDay .swrow{margin:var(--s3) 0 4px}
.rtDay p{margin:0;line-height:1.6}
.rtList{margin:0;padding-inline-start:18px;line-height:1.6}

/* ===================== badges =====================
   A shelf. Locked badges are drawn, not hidden — the shape of what is
   still out there is half of why a collection is worth having — but they
   are drawn at a third of the weight, so the earned ones are what the
   eye lands on first.

   Locked ones show their name and withhold their number. Showing both
   turns the page into a to-do list; showing neither makes it a wall of
   grey. You learn there is something called Centurion out there without
   being handed a target to grind at. */
.bGrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(122px,1fr));gap:var(--s3)}
.badge{display:flex;flex-direction:column;align-items:center;text-align:center;gap:8px;
  padding:var(--s4) var(--s2) var(--s3);border:1px solid var(--border);border-radius:14px;
  background:var(--input-bg);opacity:.42;transition:opacity .14s,border-color .14s}
.badge.got{opacity:1;background:var(--card);border-color:var(--border-strong)}

/* The face carries its own ring, its own colour and its own ornament, so
   the card underneath stops competing: no tinted border, no recolouring
   from here. The only thing added back is the drop shadow Figma drew,
   which lives here rather than in the art so it can differ by theme.
   --ba-glow is set per badge by badgeArtSvg(). */
.bMark{display:flex;align-items:center;justify-content:center}
.bArt{width:60px;height:60px;display:block}
.badge.got .bArt{filter:drop-shadow(0 3px 10px color-mix(in srgb,var(--ba-glow) 30%,transparent))}

.bName{font-size:12.5px;font-weight:600;line-height:1.3;color:var(--text)}
.bCond{font-size:10.5px;line-height:1.4;color:var(--text-subtle)}
@media (max-width:560px){
  .bGrid{grid-template-columns:repeat(auto-fill,minmax(104px,1fr));gap:var(--s2)}
  .bArt{width:52px;height:52px}
  .bName{font-size:11.5px}
}

@media print{
  .topbar,.bottomnav,.segwrap,footer,#toast,.banner,.btn,.dayhead,.dailyLine{display:none!important}
  .rday{box-shadow:none;border:0;padding:0}
  .print-only{display:block;margin-bottom:var(--s4)}
  .print-only h1{font-size:18px}
  .card,.stat{box-shadow:none;break-inside:avoid;page-break-inside:avoid}
  main{padding:0}

  /* Browsers drop CSS background colours when printing. The charts are
     canvases and came through fine, but everything drawn with a
     background did not: the bar lists printed as labels with no bars,
     and Year at a glance printed as an empty grid. */
  *{-webkit-print-color-adjust:exact!important;print-color-adjust:exact!important}

  /* And a light palette whatever theme the app is in, so a page printed
     from dark mode is not white text on white paper. The mood ramp and
     the primary are left alone — they are the meaning. */
  html,body{
    --bg:#fff; --card:#fff; --card-hover:#fff; --input-bg:#EFEFF3;
    --border:#DDDDE3; --border-strong:#B8B8C2; --shadow-sm:none;
    --text:#141418; --text-muted:#4A4A57; --text-subtle:#70707E;
    background:#fff; color:#141418;
  }
}

/* =====================================================================
   BOOT + GATE — the signed-out screen. `gated` on <body> keeps the app
   chrome out of the way rather than unmounting it, so nothing has to be
   rebuilt when the user signs in.
   ===================================================================== */
/* The fallback matters. --bg comes from html[data-resolved], set by the
   inline script in <head>; if that ever cannot run, an overlay with no
   background is worse than one in the wrong shade — the app shows
   straight through it, which is the bug this whole block exists for. */
#boot{position:fixed;inset:0;z-index:60;display:flex;align-items:center;justify-content:center;
  background:var(--bg,#F4F4F8)}
body.booted #boot{display:none}
.spinner{width:26px;height:26px;border-radius:50%;border:2.5px solid var(--border);
  border-top-color:var(--primary);animation:spin .7s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion:reduce){.spinner{animation-duration:2s}}
/* The spinner alone says "wait" but never "how long" or "what if it
   doesn't". The message carries the stage, and #bootOut is the way out. */
.bootBox{display:flex;flex-direction:column;align-items:center;gap:14px;
  padding:0 var(--s5);max-width:340px;text-align:center}
.bootMsg{margin:0;font-size:13.5px;color:var(--text-subtle);line-height:1.5}
.bootMsg.bad{color:var(--m2)}
.bootOut{display:flex;flex-direction:column;align-items:stretch;gap:8px;width:100%}
.bootOut[hidden]{display:none}
.bootOut .btn{justify-content:center}

/* Nothing of the app until the boot resolves. This used to lean entirely
   on the overlay being opaque, which meant one frame of a half-styled
   Today page on every cold load. Not rendering it is the honest version. */
/* :not(.gateFoot) because the gate carries a <footer> of its own and this
   rule would otherwise hide the one disclaimer that has to be readable
   before anybody signs up. */
body.gated .topbar,
body.gated main,
body.gated footer:not(.gateFoot),
body.gated .bottomnav,
body:not(.booted) .topbar,
body:not(.booted) main,
body:not(.booted) footer:not(.gateFoot),
body:not(.booted) .bottomnav{display:none}
body.gated{padding-bottom:0}

/* A visitor who was signed out last time is signed out now, near enough.
   The hint set in <head> puts them on the gate at the first paint rather
   than a spinner they have to sit through first — the hero and the
   sign-in form are static markup, and welcome.js fills the demo in below
   the fold. js/cloud.js drops the attribute as it boots and owns the gate
   from there, so a stale hint costs one overlay and nothing else. */
html[data-boot="gate"] #boot{display:none}
html[data-boot="gate"] #gate[hidden]{display:block}
html[data-boot="gate"] .topbar,
html[data-boot="gate"] main,
html[data-boot="gate"] footer,
html[data-boot="gate"] .bottomnav{display:none}

#gate{position:fixed;inset:0;z-index:50;overflow-y:auto;background:var(--bg);
  -webkit-overflow-scrolling:touch}
.gateWrap{max-width:1080px;margin:0 auto;padding:48px var(--s5) 64px}

/* The hero and the form are one centred column. They used to be
   left-aligned blocks inside a 1080px wrapper, which put a 420px card
   against the left edge of a wide screen and read as a mistake. */
.gateHero{max-width:660px;margin:0 auto;text-align:center}
.gateBrand{font:600 13px/1 var(--sans);letter-spacing:.14em;text-transform:uppercase;
  color:var(--primary);margin-bottom:var(--s4)}
.gateHero h1{font:400 clamp(28px,4.6vw,44px)/1.15 var(--serif);letter-spacing:-.015em;color:var(--text)}
.gateLede{margin:var(--s4) auto 0;font-size:16px;line-height:1.6;color:var(--text-muted);max-width:56ch}


.gateAuth{margin:32px auto 0;max-width:420px}

/* The prose sections between the form and the charts. Heading centred,
   body left — a three-sentence paragraph set centred is a chore to read,
   and centring only the heading still reads as deliberate rather than as
   two competing alignments. 62ch is the column, not the section. */
.gateSay{max-width:62ch;margin:56px auto 0}
.gateSay h2{font:400 clamp(21px,2.8vw,27px)/1.25 var(--serif);letter-spacing:-.01em;
  text-align:center;margin:0 0 var(--s4)}
.gateSay p{margin:0 0 var(--s3);font-size:15.5px;line-height:1.65;color:var(--text-muted)}
.gateSay p:last-child{margin-bottom:0}
/* Two classes, because `.gateSay p` and `.gateDemoHead p` are each one
   class plus one type and would otherwise repaint the kicker as body text */
.gateSay .gateKicker,.gateDemoHead .gateKicker{font:600 11px/1 var(--sans);
  letter-spacing:.12em;text-transform:uppercase;color:var(--primary);
  text-align:center;margin:0 0 var(--s3)}
.gateLog{list-style:none;margin:0 0 var(--s4);padding:0}
.gateLog li{padding:9px 0;border-bottom:1px solid var(--border);
  font-size:15.5px;line-height:1.55;color:var(--text-muted)}
.gateLog li:last-child{border-bottom:0}
.gateLog b{color:var(--text);font-weight:600}

.gateDemo{margin-top:56px;border-top:1px solid var(--border);padding-top:40px}
.gateDemoHead{max-width:62ch;margin:0 auto var(--s6)}
.gateDemoHead h2{font:400 clamp(21px,2.8vw,27px)/1.25 var(--serif);
  text-align:center;margin:0 0 var(--s4)}
.gateDemoHead p{margin:0 0 var(--s3);font-size:15.5px;line-height:1.65;color:var(--text-muted)}
.gateDemoHead p:last-child{text-align:center;margin-bottom:0}
.gateTag{display:inline-block;font:600 11px/1 var(--sans);letter-spacing:.1em;text-transform:uppercase;
  color:var(--primary);background:var(--primary-soft);border:1px solid var(--primary-soft-border);
  border-radius:99px;padding:5px 10px}

.gateGrid{display:grid;grid-template-columns:1fr 1fr;gap:var(--gap);margin-bottom:var(--gap);align-items:start}
@media (max-width:820px){.gateGrid{grid-template-columns:1fr}}
/* Timelines read across, not down: a year of days squeezed into half the
   page is a smear. These get the whole width and more height with it. */
.gateGrid.wide{grid-template-columns:1fr}
.gateGrid.wide .demoBox{height:320px}
@media (max-width:820px){.gateGrid.wide .demoBox{height:240px}}
.demoBox{height:230px}

.demoDay{display:flex;align-items:center;gap:var(--s2);flex-wrap:wrap}
.demoDayName{font-weight:600}
.demoDayDate{color:var(--text-subtle);font-size:13px}
.demoMood{width:10px;height:10px;border-radius:50%;display:inline-block}
.demoList{margin:var(--s2) 0 0;padding:0;list-style:none;color:var(--text-muted)}
/* A single day now gets the whole width, so its sections run across it
   instead of down one edge. auto-fit collapses them back to a column on
   a phone without a second breakpoint. */
.demoSecs{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));
  gap:var(--s5) var(--s6);margin-top:var(--s5);align-items:start}
.demoSec p{margin:0}
.demoSec .swrow{margin-bottom:6px}
.demoList li{padding:2px 0}
#demoEntry p{margin:var(--s1) 0 0}

.gateCta{margin-top:var(--s6);display:flex;justify-content:center}
.gateCta .btn{min-width:220px}
.gateHero .gateCta{margin-top:var(--s5)}

/* The disclaimer. Deliberately the quietest thing on the page — it has to
   be here and it has to be findable, but it is not the pitch. */
/* display and padding are restated, not inherited: the bare footer{} rule
   above dresses the app's footer as a space-between flex row, and this one
   is a single centred paragraph. */
.gateFoot{display:block;max-width:62ch;margin:56px auto 0;
  padding:var(--s5) 0 0;border-top:1px solid var(--border)}
.gateFoot p{margin:0;font-size:12.5px;line-height:1.6;color:var(--text-subtle);text-align:center}

@media (max-width:560px){
  .gateWrap{padding:32px var(--s4) 48px}
  .gateDemo{margin-top:40px}
  .demoBox{height:200px}
}

/* =====================================================================
   ARABIC + RTL

   Direction is handled by logical properties throughout the sheet above
   — margin-inline-start rather than margin-left, inset-inline-end rather
   than right — so mirroring the layout costs nothing here. What is left
   is the small set of things logical properties cannot reach, and the
   typography, which is not a mirroring problem at all.

   Two selectors, and they are not interchangeable:
     [dir="rtl"]   geometry. Would also apply to any other RTL language.
     [lang="ar"]   type. Arabic script, whichever way the page runs.
   ===================================================================== */

/* ---- geometry that logical properties don't cover ---- */

/* The select's chevron is two gradients placed from the right edge, and
   background-position has no logical form. Mirrored: what sat 18px from
   the right sits 18px from the left, and the two halves of the "v" swap
   with it — which is why the offsets are 13/18 here and 18/13 there. */
[dir="rtl"] select.select{
  background-position:13px calc(50% + 1px),18px calc(50% + 1px);
}

/* The month arrows point at a direction, not at a side. Flipping the
   glyph is right where translating it would not be: "‹" means earlier,
   and earlier is to the right in Arabic. */
[dir="rtl"] #prevMonth,[dir="rtl"] #nextMonth{transform:scaleX(-1)}

/* ---- Arabic type ----
   Arabic letters join. Tracking them apart breaks the joins and leaves a
   word looking misspelt, so every letter-spacing in the sheet above —
   all of it written for uppercase Latin labels — has to go. Same for the
   uppercasing itself: it does nothing to Arabic, and the rules that ask
   for it are exactly the ones that also ask for the tracking.

   Synthesised italic is the other one. There is no italic Arabic; a
   browser asked for one shears the glyphs, which reads as a rendering
   fault rather than as emphasis. */
/* ---- the Arabic faces ----
   Self-hosted, Arabic subset only, and pointedly not in the service
   worker's SHELL — see the note beside the fonts rule in sw.js.

   IBM Plex Sans Arabic carries the UI, because the Latin Plex is the
   same design and the two sit together without either looking borrowed.
   Amiri takes the --serif role: that variable is doing display work in
   this app (the date, the view titles, the daily line), and a Naskh with
   real stroke contrast is the closest Arabic gets to what Playfair
   Display is doing in English. A second sans there would flatten every
   heading in the app.

   swap, not block: Arabic in a fallback face for one frame is a worse
   look than a reflow, but it is a great deal better than invisible text
   on a slow connection. */
@font-face{font-family:"IBM Plex Sans Arabic";font-style:normal;font-weight:400;font-display:swap;
  src:url(fonts/plex-arabic-400.woff2) format("woff2")}
@font-face{font-family:"IBM Plex Sans Arabic";font-style:normal;font-weight:500;font-display:swap;
  src:url(fonts/plex-arabic-500.woff2) format("woff2")}
@font-face{font-family:"IBM Plex Sans Arabic";font-style:normal;font-weight:600;font-display:swap;
  src:url(fonts/plex-arabic-600.woff2) format("woff2")}
@font-face{font-family:"IBM Plex Sans Arabic";font-style:normal;font-weight:700;font-display:swap;
  src:url(fonts/plex-arabic-700.woff2) format("woff2")}
@font-face{font-family:"Amiri";font-style:normal;font-weight:400;font-display:swap;
  src:url(fonts/amiri-400.woff2) format("woff2")}
@font-face{font-family:"Amiri";font-style:normal;font-weight:700;font-display:swap;
  src:url(fonts/amiri-700.woff2) format("woff2")}

/* Two things about the order, and both matter.

   The named Latin families stay in front. Every file above is an Arabic
   subset with no Latin glyphs in it, so a product name, a hashtag or a
   number inside an Arabic sentence falls through to the Latin face and
   goes on looking like the rest of the app, instead of being drawn
   second-best by an Arabic font.

   The generic keyword moves behind the Arabic face. `ui-serif` and
   `ui-sans-serif` always match — they are the system's own font, not a
   named family that can be missing — so anything after one of them is
   unreachable. In the base stacks `ui-serif` sits second, which would
   have meant Amiri never rendering a single glyph. */
[lang="ar"]{
  --sans:"Inter","IBM Plex Sans Arabic",ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
  --serif:"Playfair Display","Amiri",ui-serif,Georgia,"Times New Roman",serif;
}

[lang="ar"] *{letter-spacing:normal}
[lang="ar"] .quote,[lang="ar"] .heroHi{font-style:normal}

/* Naskh sits lower and carries marks above and below the line, so the
   line-heights chosen for Latin crowd it. */
[lang="ar"] body{line-height:1.7}
[lang="ar"] .rtitle,[lang="ar"] .viewtitle,[lang="ar"] .heroDate,
[lang="ar"] .gateHero h1,[lang="ar"] .gateSay h2{line-height:1.35}

/* Numerals stay Western — see LOCALE() in i18n.js — and a run of Latin
   digits inside an Arabic sentence is where direction gets ambiguous.
   Marking the runs that are purely numeric keeps them together and the
   right way round. */
[lang="ar"] .mlnum,[lang="ar"] .barrow .val,[lang="ar"] .stat b,
[lang="ar"] .ring .num,[lang="ar"] .tile .tnum,[lang="ar"] .dstamp b{
  direction:ltr;unicode-bidi:isolate;
}

/* The gate's language toggle. Top of the page and off to the side: it
   has to be findable before the pitch is read, without competing with
   the headline for the first look. */
.gateLang{display:inline-flex;margin-bottom:var(--s5)}
@media (max-width:560px){
  .gateLang{margin-bottom:var(--s4)}
}

/* ---- user-written text, whichever language it is in ----
   The UI language and the journal language are two different questions.
   Someone writing in Arabic with the app in English is as ordinary here
   as the reverse, and a single entry often carries both — an English
   band name inside an Arabic sentence, a hashtag in Latin letters.

   `unicode-bidi:plaintext` is dir="auto" applied per paragraph: each
   line takes its direction from its own first strong character, so a
   mixed entry comes out right line by line without anything having to
   guess up front. It goes on the containers rather than on every render
   site, of which there are dozens.

   This is only for text a user typed. App copy is in the app's language
   by definition and must not be second-guessed. */
.rprose,.rlist,.rlist li,.rtags .tag,.rtList,.rtList li,
.rtDay > p,.logtext,.dtext,.ttext,.tchip{
  unicode-bidi:plaintext;
}
