/* LifePlaner styles. Lifted verbatim out of index.html (Phase 4).
   Two <style> blocks were merged: the main sheet from <head>, and the
   loading-screen rules that sat inline inside #globalLoader. */

/* Line-icon system (icons.js). Every fixed UI emoji is now an <i class="lp-ic">
   holding an inline SVG. It sizes to the surrounding text (1em) and draws in the
   current text colour, so it needs no separate light/dark copy and lines up on the
   baseline exactly where the emoji used to sit. Colour a specific icon by setting
   `color` on it or its parent. */
.lp-ic{
  display:inline-flex;
  align-items:center;
  justify-content:center;
  width:1em;
  height:1em;
  line-height:1;
  vertical-align:-0.125em;
  flex:0 0 auto;
}
.lp-ic > svg{
  width:1em;
  height:1em;
  display:block;
  stroke:currentColor;
  fill:none;
}
/* A couple of glyphs (theme toggle) fill a half — let those keep their fill. */
.lp-ic > svg [fill="currentColor"]{ fill:currentColor; }

/* Which set of system surfaces this page belongs to, and — the part that
   matters on a phone — which colour the OS paints the status bar icons. The
   only declaration was a static <meta content="light dark">, which means
   "follow the system", not "follow this app". With the app forced to one
   theme and the phone on the other, the bar and its icons resolved from
   different sides and the clock disappeared into the bar. This follows the
   app instead, and switches with the same class the tokens do. */
:root{ color-scheme: dark; }

:root{
  --bg:#07090E;
  --card:rgba(255,255,255,.03);
  --card-hover:rgba(255,255,255,.05);
  --card-brd:rgba(255,255,255,.08);
  --card-brd-hover:rgba(0,229,255,.35);
  --text:#F8FAFC;
  --text-dim:#94A3B8;
  --text-faint:#64748B;
  --accent:var(--cyan);
  --accent-rgb:var(--cyan-rgb);

  --cyan:#00E5FF;   --cyan-rgb:0,229,255;
  --magenta:#FF3EA0; --magenta-rgb:255,62,160;
  --amber:#FFB224;  --amber-rgb:255,178,36;
  --violet:#9B6BFF; --violet-rgb:155,107,255;
  --lime:#10B981;   --lime-rgb:16,185,129;
  --coral:#FF5470;  --coral-rgb:255,84,112;
  --blue:#3B82F6;   --blue-rgb:59,130,246;
  --yellow:#F7E016; --yellow-rgb:247,224,22;
  --danger:#FF4D6D;

  --health:#00E5FF; --health-rgb:0,229,255;
  --mind:#9B6BFF;   --mind-rgb:155,107,255;
  --work:#FFB224;   --work-rgb:255,178,36;

  --legend-completed: #00FFA3;
  --legend-missed: #FF3EA0;
  --legend-pending: rgba(255,255,255,0.15);
  /* Circular tracker SVG palette (dark). The SVG references these with var(),
     so a theme switch recolours it via a normal CSS repaint and the 441-node
     SVG never has to be rebuilt. Rebuilding it left an unrastered slice of the
     1400px-tall card, which is what produced the black/white band. */
  --ct-grid: rgba(255,255,255,0.15);
  --ct-text-habit: rgba(255,255,255,0.85);
  --ct-text-day: rgba(255,255,255,0.55);
  --ct-arc: rgba(255,255,255,0.12);
  --ct-seg-border: var(--surface-bg);
  --ct-hub-bg: rgba(9,12,19,0.95);
  --ct-hub-border: rgba(255,255,255,0.12);

  --radius:18px;
  --radius-sm:10px;

  /* NEW: Glass/surface variables for theme-awareness */
  --surface-bg:rgba(255,255,255,.03);
  --surface-hover:rgba(255,255,255,.05);
  --surface-border:rgba(255,255,255,.08);
  --glass-bg:rgba(15,19,28,.8);
  --glass-bg-deep:rgba(10,13,20,.95);
  /* Opaque on purpose. A translucent card forces the compositor to blend the
     card layer with the page behind it; on a very tall card (e.g. the 1400px
     #circularWheelBox) an incomplete raster then shows the page background as a
     horizontal band. Opaque = same colour, no blend, cheaper raster.
     rgba(12,15,22,.96) over --bg #07090E resolves to this. */
  /* RULE: --glass-* and --table-* stay opaque. Every ghost-box and banding
     artifact this project hit came from one pattern — a large translucent
     surface getting its own composited layer, whose raster could still be
     stale after a full-document repaint. Opaque values are the fix, and the
     .98 alphas below are already at the edge of what is safe. Do not lower
     them, and do not reintroduce backdrop-filter on a scroller or a sticky
     child. See IMPROVEMENT_PLAN.md Appendix A. */
  --glass-bg-card:#0C0F16;
  --input-bg:rgba(15,19,28,.8);
  --topbar-bg:rgba(9,11,17,.82);
  --tabs-bg:rgba(9,11,17,.88);
  /* opaque equivalent of rgba(10,13,20,.7) composited over --bg with blur(20px) */
  --table-bg:#090C12;
  --table-sticky-bg:rgba(11,14,21,.98);
  /* The same colour with the last 2% of transparency taken out. A frozen column
     has a whole grid sliding under it, and at .98 a 24px column of digits still
     ghosts through. Separate token rather than a change to the one above,
     because that one is on the desktop table too. */
  /* The frozen habit column follows the page, not a fixed panel colour. Six
     rules paint with this token and #0B0E15 read as a lighter strip down the
     left of the grid on a pure-black theme. var(--bg) is still fully opaque, so
     the reason it was made solid in the first place — day numbers ghosting
     through a 2%% gap — still holds. Light theme keeps its own value below. */
  --table-sticky-bg-solid:var(--bg);
  /* The band between two sections of the Today feed. Grey, not accent — it is a
     gap you look past, not a thing you look at. */
  --feed-sep:#191E29;
  /* The grid rules. Deliberately NOT the accent: the accent is whatever theme
     the user picked, and a line that says "this is today" should not change
     colour when they change their theme. White on dark, near-black on light,
     and the soft one is the same line at half strength for the column and row
     divisions, so today reads as stronger than the grid it sits in. */
  --grid-rule:rgba(255,255,255,.28);
  --grid-rule-soft:rgba(255,255,255,.14);
  --sheet-bg:rgba(10,13,20,0.95);
  --sheet-tab-bg:rgba(18,24,36,0.8);
  --shadow-color:rgba(0,0,0,0.5);
  --shadow-strong:rgba(0,0,0,0.8);
  --scrollbar-track:var(--surface-bg);
  --scrollbar-thumb:rgba(255,255,255,0.16);
  --skeleton-a:var(--inner-box-bg-light);
  --skeleton-b:var(--inner-box-border);
  --glow-blur:blur(140px);
  --body-glow-1:rgba(var(--accent-rgb),.15);
  --body-glow-2:rgba(var(--magenta-rgb),.12);
  --chk-border:rgba(255,255,255,.45);
  --chk-bg:var(--inner-box-bg-light);
  --select-bg:rgba(14,18,26,0.95);
  --select-option-bg:#0b0e14;
  --kanban-bg:rgba(14,18,26,.6);
  --kanban-header-bg:var(--inner-box-bg);

  --inner-box-bg: var(--inner-box-bg);
  --inner-box-border: var(--inner-box-border);
  --inner-box-bg-light: var(--inner-box-bg-light);
  --inner-box-border-light: rgba(255,255,255,0.05);
}

/* ===== LIGHT THEME ===== */
html.light-theme, html.light-theme-preload, body.light-theme { color-scheme: light; }

/* Light mode me accent us theme ka gehra shade hai (app.js THEMES[].lt, jise
   applyTheme <html> par --accent-lt rakhta hai). <html> par --accent neon hi
   rehta hai; body yahan apna --accent deta hai, to body ke andar sab gehra
   rang padhta hai. --cyan bhi, kyunki kai jagah seedha var(--cyan) likha hai.
   Fallback cyan ka shade hai: applyTheme se pehle, aur un pages ke liye jo
   accent lagate hi nahi. Shades ~4.6 : 1 par chune hain taaki safed par text
   aur accent par #000 dono padhe jaayein — dekho app.js THEMES.

   --on-accent: accent ki fill par text. Sirf yahan define hai, :root me nahi —
   har jagah `color:var(--on-accent, #000)` likha hai, to dark mode apna purana
   rang (#000 / #07090E) fallback se leta hai aur ek pixel nahi badalta; light
   me gehre shade par safed. --accent-end / --cyan-end: jo gradient pakke neon
   (#00E5FF, #0099FF, #00bfff) par khatam hote the, light me wahan bhi accent,
   warna safed text us kone me 1.5 : 1 par feeka ho jaata. */
body.light-theme, html.light-theme-preload body {
  --accent: var(--accent-lt, #057BB9);
  --accent-rgb: var(--accent-lt-rgb, 5,123,185);
  --cyan: #057BB9; --cyan-rgb: 5,123,185;
  --on-accent: #fff;
  --accent-end: var(--accent);
  --cyan-end: var(--cyan);
}

body.light-theme {
  --bg:#F5F7FA;
  --card:rgba(0,0,0,.025);
  --card-hover:rgba(0,0,0,.04);
  --card-brd:rgba(0,0,0,.1);
  --card-brd-hover:rgba(var(--accent-rgb),.4);
  --text:#0F172A;
  --text-dim:#475569;
  --text-faint:#94A3B8;

  --inner-box-bg: rgba(0,0,0,0.03);
  --inner-box-border: rgba(0,0,0,0.08);
  --inner-box-bg-light: rgba(0,0,0,0.02);
  --inner-box-border-light: rgba(0,0,0,0.06);
  --danger:#DC2626;

  --surface-bg:rgba(0,0,0,.02);
  --surface-hover:rgba(0,0,0,.04);
  --surface-border:rgba(0,0,0,.08);
  --glass-bg:rgba(255,255,255,.85);
  --glass-bg-deep:rgba(245,247,250,.96);
  /* opaque equivalent of rgba(255,255,255,.92) over the light page bg — see dark note */
  --glass-bg-card:#FEFEFF;
  --input-bg:rgba(255,255,255,.9);
  --topbar-bg:rgba(255,255,255,.88);
  --tabs-bg:rgba(255,255,255,.92);
  /* opaque equivalent of rgba(255,255,255,.85) composited over the light page bg */
  --table-bg:#FEFEFE;
  --table-sticky-bg:rgba(250,250,252,.98);
  --table-sticky-bg-solid:#FFFFFF;
  --feed-sep:#E4E6EB;
  --grid-rule:rgba(11,14,21,.28);
  --grid-rule-soft:rgba(11,14,21,.14);
  --sheet-bg:rgba(255,255,255,.96);
  --sheet-tab-bg:rgba(240,242,248,.9);
  --shadow-color:rgba(0,0,0,0.08);
  --shadow-strong:rgba(0,0,0,0.12);
  --scrollbar-track:rgba(0,0,0,0.06);
  --scrollbar-thumb:var(--inner-box-bg-light);
  --skeleton-a:rgba(0,0,0,0.03);
  --skeleton-b:rgba(0,0,0,0.07);
  --glow-blur:blur(100px);
  --body-glow-1:rgba(var(--accent-rgb),.06);
  --body-glow-2:rgba(var(--magenta-rgb),.04);
  --chk-border:rgba(0,0,0,.3);
  --chk-bg:rgba(255,255,255,0.8);
  --select-bg:rgba(255,255,255,0.95);
  --select-option-bg:#ffffff;
  --kanban-bg:rgba(255,255,255,.7);
  --kanban-header-bg:rgba(0,0,0,0.04);
}

/* Light theme: Body glow orbs toned down */
body.light-theme::before { background:radial-gradient(circle, var(--body-glow-1), transparent 60%) !important; }
body.light-theme::after { background:radial-gradient(circle, var(--body-glow-2), transparent 60%) !important; }

/* Light theme: Topbar */
body.light-theme .topbar2 { background:var(--topbar-bg); border-bottom:1px solid var(--card-brd); box-shadow:0 1px 4px var(--shadow-color); }
body.light-theme .top-tabs { background:var(--tabs-bg); }
body.light-theme .top-tab:hover { border-bottom-color:var(--inner-box-bg-light); }
body.light-theme .logo .word { color:var(--text); }
body.light-theme .logo .mark::after { background:rgba(255,255,255,0.5); }

/* Light theme: XP bar */
body.light-theme .xp-bar-container { background:var(--glass-bg) !important; border-color:var(--card-brd) !important; box-shadow:inset 0 1px 2px rgba(0,0,0,.06) !important; }

/* Light theme: Cards */
body.light-theme .card { background:rgba(255,255,255,.7) !important; border: 1px solid rgba(0,0,0,0.12) !important; }
body.light-theme .card.separate-arena-card { background:var(--glass-bg-card) !important; }
body.light-theme .card h3 { border-bottom-color:rgba(0,0,0,.05); }
body.light-theme .card:hover { background:rgba(255,255,255,.85) !important; box-shadow:0 4px 16px var(--shadow-color), 0 0 0 1px rgba(var(--accent-rgb),.15); border-color:rgba(var(--accent-rgb),.3); }

/* Light theme: Stat card text gradients */
body.light-theme .stat-card .num { background:linear-gradient(135deg, #1E293B, #334155) !important; -webkit-background-clip:text; -webkit-text-fill-color:transparent; }

/* Light theme: page hero */
body.light-theme .page-hero { border-color:var(--card-brd); background:linear-gradient(90deg, rgba(var(--accent-rgb),.06), transparent 60%); }
body.light-theme .page-hero-text h1 { color:var(--text); }

/* Light theme: Inputs */
body.light-theme .onb-input,
body.light-theme .onb-textarea,
body.light-theme .add-row input,
body.light-theme .add-row textarea,
body.light-theme .htable input[type=text],
body.light-theme .form-grid input,
body.light-theme textarea#reflectionInput { background:var(--input-bg) !important; color:var(--text) !important; border-color:var(--card-brd) !important; }

/* Light theme: Selects */
body.light-theme select,
body.light-theme select.onb-input,
body.light-theme .htable select,
body.light-theme .form-grid select { background:var(--select-bg) !important; color:var(--text) !important; border-color:var(--card-brd) !important; }
body.light-theme select option { background-color:var(--select-option-bg) !important; color:var(--text) !important; }

/* Light theme: Buttons */
body.light-theme .onb-btn.secondary { background:rgba(0,0,0,.04) !important; color:var(--text) !important; border-color:var(--card-brd) !important; }
body.light-theme .radio-opt:hover:not(.sel) { background:rgba(0,0,0,.06); }
body.light-theme .radio-row, body.light-theme .segmented-control { background:rgba(0,0,0,.04); border-color:rgba(0,0,0,.1); box-shadow:inset 0 1px 3px rgba(0,0,0,.06); }

/* Light theme: Tables */
body.light-theme .hub-table-wrap { background:var(--table-bg) !important; box-shadow:0 2px 8px var(--shadow-color); }
body.light-theme table.hub-table td.hub-name { background:var(--table-sticky-bg) !important; }
body.light-theme .habit-grid-wrap { background:var(--table-bg) !important; box-shadow:0 2px 8px var(--shadow-color); }
body.light-theme table.habit-grid td.habit-name { background:var(--table-sticky-bg) !important; }
body.light-theme table.habit-grid thead th { background:var(--glass-bg-deep) !important; }
body.light-theme .tx-table td { border-bottom-color:rgba(0,0,0,.06); }
body.light-theme .tx-table tr:hover td { background:rgba(0,0,0,.025); }
body.light-theme .htable td { border-bottom-color:rgba(0,0,0,.06); }

/* Light theme: Mini-task rows */
body.light-theme .mini-task { border-bottom-color:rgba(0,0,0,.06); }
body.light-theme .mini-task:hover { background:rgba(0,0,0,.02); }
body.light-theme .link-more:hover { color:var(--text); }

/* Light theme: Heatmap */
body.light-theme .heat-cell { background:rgba(0,0,0,.05); }

/* Light theme: Checkboxes */
body.light-theme .chk { border-color:var(--chk-border) !important; background:var(--chk-bg) !important; }
body.light-theme .chk:hover { background:rgba(var(--accent-rgb),.15) !important; }
body.light-theme .chk.on { background:#ffffff !important; border-color:#ffffff !important; box-shadow:none !important; }
body.light-theme .chk.on::after { color:#0F172A !important; text-shadow:none !important; font-weight:900 !important; }

/* Light theme: Kanban */
body.light-theme .kanban-col { background:var(--kanban-bg); }
body.light-theme .k-header { background:var(--kanban-header-bg); }
body.light-theme .k-task-card { background:rgba(255,255,255,.8) !important; }
body.light-theme .k-task-card:hover { box-shadow:0 4px 16px var(--shadow-color); border-color:rgba(0,0,0,.12); }

/* Light theme: Calendar */
body.light-theme .cal-day-cell { background:rgba(0,0,0,.02); border-color:rgba(0,0,0,.08); }
body.light-theme .cal-day-cell:hover { background:rgba(0,0,0,.06); }

/* Light theme: Pomodoro */
body.light-theme .pomo-bg { stroke:rgba(0,0,0,.08); }
body.light-theme .pomo-time { color:var(--text) !important; text-shadow:none !important; }
body.light-theme .pomo-tab { background:rgba(0,0,0,.04); border-color:var(--card-brd); color:var(--text-dim); }
body.light-theme .pomo-tab:hover { background:rgba(0,0,0,.08); color:var(--text); }
body.light-theme .pomo-tab.active { background:var(--accent); color:#fff; }

/* Light theme: Leaderboard */
body.light-theme .lb-row { background:rgba(0,0,0,.02); border-color:var(--card-brd); }
body.light-theme .lb-row:hover { background:rgba(0,0,0,.04); }
body.light-theme .leaderboard-tab-bar { background:rgba(255,255,255,.8); border-color:var(--card-brd); box-shadow:inset 0 1px 2px rgba(0,0,0,.04), 0 2px 8px var(--shadow-color); }

/* Light theme: Sheet tabs */
body.light-theme .sheet-tabs-bar { background:var(--sheet-bg); }
body.light-theme .sheet-tab { background:var(--sheet-tab-bg); }
body.light-theme .sheet-tab:hover { background:rgba(0,0,0,.06); }
body.light-theme .sheet-tab.active { background:var(--bg); }

/* Light theme: Page switcher */
body.light-theme .ps-btn { background:var(--glass-bg) !important; box-shadow:0 2px 6px var(--shadow-color); }
body.light-theme .ps-menu { background:var(--glass-bg-card); box-shadow:0 8px 24px rgba(0,0,0,0.12), 0 0 0 1px rgba(0,0,0,0.06); }
body.light-theme .ps-item:hover { background:rgba(0,0,0,.05); }

/* Light theme: Scrollbars */
body.light-theme ::-webkit-scrollbar-track { background:var(--scrollbar-track); }
body.light-theme ::-webkit-scrollbar-thumb { background:var(--scrollbar-thumb); }

/* Light theme: Skeleton */
body.light-theme .skeleton-bg { background:linear-gradient(to right, var(--skeleton-a) 4%, var(--skeleton-b) 25%, var(--skeleton-a) 36%); }
body.light-theme #skeletonScreen { background:var(--bg); }

/* Light theme: Selection */
body.light-theme ::selection { background:rgba(var(--accent-rgb),.2); color:var(--text); }

/* Light theme: Color swatches */
body.light-theme .color-circle.active { border-color:#0F172A !important; }
body.light-theme .color-circle.active::after { color:#fff; }

/* Light theme: Misc overrides */
body.light-theme .tag { background:rgba(0,0,0,.06); color:var(--text-dim); }
body.light-theme .recur-item { border-bottom-color:rgba(0,0,0,.06); }
body.light-theme .ov-stat { border-bottom-color:rgba(0,0,0,.08); }
body.light-theme .wa-item { background:rgba(0,0,0,.03); border-color:rgba(0,0,0,.08); }
body.light-theme .wa-track { background:rgba(0,0,0,.06); }
body.light-theme .wt-bar-track { background:rgba(0,0,0,.08); }
body.light-theme .mini-bar { background:rgba(0,0,0,.08); }
body.light-theme .balance-row .bl-track { background:rgba(0,0,0,.06); }
body.light-theme .empty-state { background:rgba(0,0,0,.015); border-color:var(--card-brd); }
body.light-theme .ring-card-item { background:rgba(255,255,255,.7) !important; backdrop-filter:blur(10px); border: 1px solid rgba(0,0,0,0.12) !important; }
body.light-theme .hub-top-left { background:rgba(255,255,255,.7) !important; backdrop-filter:blur(10px); border: 1px solid rgba(0,0,0,0.12) !important; border-radius:0 !important; padding:12px; box-sizing:border-box; }
.hub-top-left, #homeTaskBox, #circularWheelBox { border-radius: 0 !important; }
body.light-theme .ring-card-item:hover { background:rgba(255,255,255,.9) !important; border-color:var(--accent) !important; transform:none !important; box-shadow:none !important; animation:none !important; }
body.light-theme .rings-flower-sm .ring-item:hover,
body.light-theme .ring-item:hover { transform:none !important; box-shadow:none !important; animation:none !important; }

/* Progress Rings Shadow Removal (Universal Light & Dark Modes) */
.ring-card-item, .rings-flower .ring-item, .rings-flower-sm .ring-item, .ring-item, .ring-item circle, .ring-card-item svg {
  box-shadow: none !important;
  filter: none !important;
  -webkit-filter: none !important;
}

/* Habits Page Light Mode & Border Differentiation Fixes */
.card, .bento .card { border: 1px solid rgba(255,255,255,0.2) !important; }
body.light-theme .card, body.light-theme .bento .card { border: 1px solid rgba(0,0,0,0.16) !important; }
#newHabitInput, #newHabitCue, #newHabitWhy, #newHabitCat { border: 1px solid rgba(255,255,255,0.2) !important; }
body.light-theme #newHabitInput, body.light-theme #newHabitCue, body.light-theme #newHabitWhy, body.light-theme #newHabitCat { border: 1px solid rgba(0,0,0,0.16) !important; }
.cat-add-box { border: 1px solid rgba(255,255,255,0.2) !important; }
body.light-theme .cat-add-box { background: rgba(0,0,0,0.03) !important; border-color: rgba(0,0,0,0.12) !important; }
/* The decorative watermark glyphs in card corners, faded almost out in light
   mode. They are matched by their inline font-size because that is all that
   marks them out — which makes this a substring match on a style attribute, and
   it catches by shape rather than by intent.

   #rcGrade is the one false positive: the audit's grade letter is also 52px, so
   it was being faded to 0.015 and the report opened with an invisible grade in
   light mode on both tiers. Measured on the page, the selector matches 38
   elements — 37 real watermarks and this. Excluding it by id is narrower and
   safer than loosening the font-size match. */
body.light-theme .card div[style*="pointer-events:none"]:not(#rcGrade),
body.light-theme .card div[style*="font-size:48px"]:not(#rcGrade),
body.light-theme .card div[style*="font-size:42px"]:not(#rcGrade),
body.light-theme .card div[style*="font-size:52px"]:not(#rcGrade) {
  opacity: 0.015 !important;
  filter: none !important;
}

/* ...and once it is visible it has to be readable. The grade is painted in its
   own neon inline by generateReportCard — mint, cyan, amber — every one of which
   is near-white on a light card (cyan on white measures 1.54:1). It takes the
   dark text colour in light mode; the box behind it keeps the coloured border
   and tint, so the grade is still signalled by colour, just not by the letter. */
body.light-theme #rcGrade { color: var(--text) !important; }
body.light-theme #newCatNameInput, body.light-theme #newCatEmojiBtn { background: var(--input-bg) !important; color: var(--text) !important; border-color: var(--card-brd) !important; }
body.light-theme .cat-add-box { background: rgba(0,0,0,0.03) !important; border-color: rgba(0,0,0,0.08) !important; }
body.light-theme #newCatNameInput::placeholder { color: var(--text-faint) !important; }
body.light-theme #heatDetail { background: rgba(0,0,0,0.03) !important; border-color: rgba(0,0,0,0.08) !important; color: var(--text) !important; }
body.light-theme .add-btn.habit, .add-btn.habit { color: #ffffff !important; font-weight: 800 !important; text-shadow: 0 1px 2px var(--inner-box-bg) !important; }
body.light-theme .challenge-card { background: rgba(255,255,255,0.85) !important; border: 1px solid rgba(0,0,0,0.14) !important; backdrop-filter: blur(10px); }
body.light-theme .challenge-card:hover { background: rgba(255,255,255,0.98) !important; border-color: var(--accent) !important; box-shadow: 0 4px 16px var(--shadow-color) !important; }

/* Input Focus Line Overrides (No Yellow Shadow) */
#newHabitInput:focus, #newHabitCue:focus, #newHabitWhy:focus, #newHabitCat:focus, #newCatNameInput:focus {
  outline: none !important;
  border-color: var(--accent) !important;
  box-shadow: none !important;
}

/* Deep Colors for Light Mode Grid */
body.light-theme .th-wk-0 { background: linear-gradient(135deg, #0284C7, #0369A1) !important; color: #fff !important; }
body.light-theme .td-checked-0 { background: linear-gradient(135deg, #0284C7, #0369A1) !important; color: #fff !important; }
body.light-theme .td-checked-0::after { color: #ffffff !important; text-shadow: none !important; box-shadow: none !important; }

body.light-theme .th-wk-1 { background: linear-gradient(135deg, #E11D48, #BE123C) !important; color: #fff !important; }
body.light-theme .td-checked-1 { background: linear-gradient(135deg, #E11D48, #BE123C) !important; color: #fff !important; }
body.light-theme .td-checked-1::after { color: #ffffff !important; text-shadow: none !important; box-shadow: none !important; }

body.light-theme .th-wk-2 { background: linear-gradient(135deg, #7E22CE, #6B21A8) !important; color: #fff !important; }
body.light-theme .td-checked-2 { background: linear-gradient(135deg, #7E22CE, #6B21A8) !important; color: #fff !important; }
body.light-theme .td-checked-2::after { color: #ffffff !important; text-shadow: none !important; box-shadow: none !important; }

body.light-theme .th-wk-3 { background: linear-gradient(135deg, #059669, #047857) !important; color: #fff !important; }
body.light-theme .td-checked-3 { background: linear-gradient(135deg, #059669, #047857) !important; color: #fff !important; }
body.light-theme .td-checked-3::after { color: #ffffff !important; text-shadow: none !important; box-shadow: none !important; }

body.light-theme .th-wk-4 { background: linear-gradient(135deg, #D97706, #B45309) !important; color: #fff !important; }
body.light-theme .td-checked-4 { background: linear-gradient(135deg, #D97706, #B45309) !important; color: #fff !important; }
body.light-theme .td-checked-4::after { color: #ffffff !important; text-shadow: none !important; box-shadow: none !important; }

body.light-theme table.hub-table tbody tr:not(:last-child) td { border-bottom-color:rgba(0,0,0,.06); }
body.light-theme table.habit-grid tbody tr:not(:last-child) td { border-bottom-color:rgba(0,0,0,.04); }
body.light-theme .focus-box { border-color:rgba(var(--accent-rgb),.25); background:linear-gradient(160deg, rgba(var(--accent-rgb),.08), rgba(var(--accent-rgb),.02)); }
body.light-theme .onb-input { background:var(--surface-bg); }

body.light-theme {
  --heat-0-bg: rgba(0,0,0,0.04);
  --heat-0-brd: rgba(0,0,0,0.08);
  --heat-1-bg: rgba(var(--accent-rgb), 0.2);
  --heat-1-brd: rgba(var(--accent-rgb), 0.3);
  --heat-2-bg: rgba(var(--accent-rgb), 0.45);
  --heat-2-brd: rgba(var(--accent-rgb), 0.55);
  --heat-3-bg: rgba(var(--accent-rgb), 0.75);
  --heat-3-brd: rgba(var(--accent-rgb), 0.9);
  --heat-4-bg: var(--accent);
  --heat-4-brd: var(--accent);

  /* Circular wheel legend overrides for light mode */
  --legend-completed: #10B981;
  --legend-missed: #EF4444;
  --legend-pending: #CBD5E1;
  /* Circular tracker SVG palette (light) — see the dark block for why these exist */
  --ct-grid: rgba(15, 23, 42, 0.16);
  --ct-text-habit: #0F172A;
  --ct-text-day: #475569;
  --ct-arc: rgba(0,0,0,0.12);
  --ct-seg-border: #FFFFFF;
  --ct-hub-bg: #FFFFFF;
  --ct-hub-border: #94A3B8;
}

.ring-track { stroke: rgba(255, 255, 255, 0.14); }
body.light-theme .ring-track { stroke: rgba(15, 23, 42, 0.16); }

body.light-theme ::placeholder {
  color: var(--text-faint) !important;
  opacity: 1;
}

/* Light theme: Edit Profile Modal Redesign */
body.light-theme #editProfileModal.modal-overlay {
  background: rgba(15, 23, 42, 0.45) !important;
  backdrop-filter: blur(16px) !important;
  -webkit-backdrop-filter: blur(16px) !important;
}

body.light-theme #editProfileModal .card {
  background: #ffffff !important;
  border: 1px solid rgba(0, 0, 0, 0.12) !important;
  box-shadow: 0 25px 50px -12px rgba(15, 23, 42, 0.25), 0 0 0 1px rgba(0, 0, 0, 0.05) !important;
  color: #0f172a !important;
}

body.light-theme #editProfileModal h3 {
  color: var(--accent) !important;
}

body.light-theme #editProfileModal p,
body.light-theme #editProfileModal label {
  color: #475569 !important;
}

body.light-theme #editProfileModal .onb-input,
body.light-theme #editProfileModal select.onb-input {
  background: #f8fafc !important;
  color: #0f172a !important;
  border: 1px solid #cbd5e1 !important;
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.03) !important;
}

body.light-theme #editProfileModal .onb-input:focus,
body.light-theme #editProfileModal select.onb-input:focus {
  background: #ffffff !important;
  border-color: var(--accent) !important;
  box-shadow: 0 0 0 3px rgba(var(--accent-rgb), 0.15) !important;
}

body.light-theme #editProfileModal input[readonly] {
  background: #f1f5f9 !important;
  color: #64748b !important;
  border-color: #e2e8f0 !important;
}

body.light-theme #epTabContainer {
  background: #e2e8f0 !important;
  border: 1px solid #cbd5e1 !important;
  padding: 4px !important;
  border-radius: 30px !important;
}

body.light-theme #epTabPersonaBtn,
body.light-theme #epTabSecurityBtn {
  color: #475569;
  background: transparent;
}

body.light-theme #epTabPersonaBtn[style*="var(--accent)"],
body.light-theme #epTabSecurityBtn[style*="var(--accent)"] {
  background: var(--accent) !important;
  color: var(--on-accent, #000000) !important;
  font-weight: 800 !important;
  box-shadow: 0 2px 8px rgba(var(--accent-rgb), 0.35) !important;
}

body.light-theme #epCancelBtn {
  background: #f1f5f9 !important;
  color: #334155 !important;
  border: 1px solid #cbd5e1 !important;
}

body.light-theme #epCancelBtn:hover {
  background: #e2e8f0 !important;
  color: #0f172a !important;
}

body.light-theme #profSaveBtn:disabled,
body.light-theme #cpSaveBtn:disabled {
  background: #f1f5f9 !important;
  color: #94a3b8 !important;
  border: 1px solid #e2e8f0 !important;
  cursor: not-allowed !important;
  box-shadow: none !important;
}

body.light-theme #epChangePassBox {
  background: #f8fafc !important;
  border: 1px solid #e2e8f0 !important;
}

body.light-theme #phoneEditPanel,
body.light-theme #emailEditPanel {
  background: rgba(var(--accent-rgb), 0.05) !important;
  border: 1px solid rgba(var(--accent-rgb), 0.2) !important;
}

/* ===== LIGHT THEME UNIVERSAL MODALS & DIALOGS REDESIGN ===== */
body.light-theme .modal-overlay {
  background: rgba(15, 23, 42, 0.45) !important;
  backdrop-filter: blur(16px) !important;
  -webkit-backdrop-filter: blur(16px) !important;
}

body.light-theme .modal-overlay .card,
body.light-theme .modal-overlay .card.bento,
body.light-theme .modal-overlay .modal-content,
body.light-theme #fabModal .card.bento,
body.light-theme #pageGuideModal .card.bento,
body.light-theme #defaultHabitsModal .card,
body.light-theme #challengeModal .modal-content {
  background: #ffffff !important;
  border: 1px solid rgba(0, 0, 0, 0.12) !important;
  box-shadow: 0 25px 50px -12px rgba(15, 23, 42, 0.25), 0 0 0 1px rgba(0, 0, 0, 0.05) !important;
  color: #0f172a !important;
}

body.light-theme .modal-overlay h2,
body.light-theme .modal-overlay h3,
body.light-theme .modal-overlay h4,
body.light-theme #pageGuideHeader h3,
body.light-theme #pageGuideHeader h4 {
  color: #0f172a !important;
}

body.light-theme .modal-overlay p,
body.light-theme .modal-overlay label,
body.light-theme #pageGuideBody p,
body.light-theme #pageGuideBody li {
  color: #475569 !important;
}

/* FAB Dialog Modal Buttons */
body.light-theme #fabModal button[id^="fabBtn"] {
  background: #f8fafc !important;
  border: 1px solid #cbd5e1 !important;
  color: #0f172a !important;
  box-shadow: 0 2px 4px rgba(0,0,0,0.03) !important;
}

body.light-theme #fabModal button[id^="fabBtn"]:hover {
  background: #f1f5f9 !important;
  border-color: var(--accent) !important;
  box-shadow: 0 4px 12px rgba(0,0,0,0.08) !important;
}

body.light-theme #fabModal button[id^="fabBtn"] div[style*="font-size:12px"] {
  color: #64748b !important;
}

body.light-theme #fabModal button[id^="fabBtn"] svg {
  stroke: #64748b !important;
}

/* Page Guide / System Guide Body Elements & Scrollbar Removal */
#pageGuideModal .card.bento,
#pageGuideModal .card {
  scrollbar-width: none !important;
  -ms-overflow-style: none !important;
}

#pageGuideModal .card.bento::-webkit-scrollbar,
#pageGuideModal .card::-webkit-scrollbar {
  display: none !important;
  width: 0 !important;
  height: 0 !important;
}

#pageGuideBody {
  display: flex !important;
  flex-direction: column !important;
  gap: 14px !important;
}

body.light-theme #pageGuideModal div[style*="background"],
body.light-theme #pageGuideBody div {
  background: #f8fafc !important;
  border-color: #e2e8f0 !important;
  color: #334155 !important;
}

body.light-theme #pageGuideModal strong {
  color: #0f172a !important;
}

/* Default Habits Picker Modal Buttons */
body.light-theme #defaultHabitsModal button[onclick*="pickDefaultHabit"] {
  background: #f8fafc !important;
  border: 1px solid #cbd5e1 !important;
  color: #0f172a !important;
}

body.light-theme #defaultHabitsModal button[onclick*="pickDefaultHabit"]:hover {
  background: rgba(var(--accent-rgb), 0.1) !important;
  border-color: var(--accent) !important;
  color: #0f172a !important;
}

/* Challenge Details Modal Elements */
body.light-theme #challengeModal #cModalDesc {
  color: #1e293b !important;
}

body.light-theme #challengeModal #cModalRules {
  background: rgba(var(--accent-rgb), 0.08) !important;
  border-color: rgba(var(--accent-rgb), 0.25) !important;
  color: #0f172a !important;
}

body.light-theme #challengeModal button {
  background: #f1f5f9 !important;
  border-color: #cbd5e1 !important;
  color: #0f172a !important;
}

/* The EN/HI toggle inside #challengeModal is not a plain action button — it
   has two states, and the rule above (every button in this modal gets the
   same flat grey) was erasing the difference between them, so neither side
   looked selected. #cModalLangToggle in the selector makes this more
   specific than the rule above, so it wins in light theme too. */
body.light-theme #challengeModal #cModalLangToggle button.lang-active {
  background: var(--accent) !important;
  border-color: var(--accent) !important;
  color: var(--on-accent, #000) !important;
}
body.light-theme #challengeModal #cModalLangToggle button:not(.lang-active) {
  background: transparent !important;
  border-color: transparent !important;
  color: #64748b !important;
}

/* Universal Modal Close Buttons */
body.light-theme .modal-overlay button[onclick*="close"],
body.light-theme .modal-overlay .close,
body.light-theme .modal-overlay span.close {
  color: #64748b !important;
}

body.light-theme .modal-overlay button[onclick*="close"]:hover,
body.light-theme .modal-overlay .close:hover,
body.light-theme .modal-overlay span.close:hover {
  color: #0f172a !important;
}

/* ===== END LIGHT THEME ===== */

*{box-sizing:border-box;margin:0;padding:0;}
html.light-theme-preload, html.light-theme-preload body { --bg:#F5F7FA; background:#F5F7FA !important; min-height:100vh; }
html,body{background:var(--bg); min-height:100vh;}
body{
  min-width:320px;
  color:var(--text);
  font-family:'Inter',-apple-system,BlinkMacSystemFont,sans-serif;
  -webkit-font-smoothing:antialiased;
  position:relative;
  overflow-x:hidden;
}
h1,h2,h3,.disp{font-family:'Space Grotesk',sans-serif;}
::selection{background:rgba(var(--accent-rgb),.28);color:#FFF;}

body::before, body::after{
  content:'';position:fixed;border-radius:50%;/* GPU BUG FIX */;z-index:0;pointer-events:none;
}
body::before{ width:650px;height:650px;top:-220px;left:180px; background:radial-gradient(circle, rgba(var(--accent-rgb),.15), rgba(var(--violet-rgb),.05) 50%, transparent 70%); }
body::after{ width:700px;height:700px;bottom:-260px;right:-180px; background:radial-gradient(circle, rgba(var(--magenta-rgb),.12), rgba(var(--amber-rgb),.04) 50%, transparent 70%); }

/* Phone-tier markup, folded away everywhere else. Both are opened up inside the
   640px block; on desktop they take no space and render nothing. */
#headerPageShortTitle { display: none; }
.mobile-only { display: none; }

/* The header light/dark button carries an emoji glyph and an SVG icon side by
   side. The whole app now runs on line icons, so the SVG shows everywhere and the
   emoji glyph is retired — both tiers stay identical. The runtime keeps the SVG's
   sun/moon in step with the theme (setThemeMode). */
.tm-ico-emoji { display: none; }
.tm-ico-svg { display: inline-flex; align-items: center; justify-content: center; }

/* The Manage/Delete Categories button carries a full desktop label and a short
   phone label ("Delete Category"); the short one is hidden off the phone. */
.cat-short { display: none; }

/* The lettered category circle in a goal card is a phone-only stand-in for the
   text badge; hidden off the phone so the desktop card is unchanged. */
.fg-cat-circle { display: none; }

/* The header page-guide button is phone-only (desktop reaches the guide by the
   ? shortcut and keeps its bar unchanged). */
#headerGuideBtn { display: none; }

/* ---------- topbar ---------- */
.topbar2{
  position:sticky;top:0;z-index:60;display:flex;align-items:center;justify-content:space-between;
  padding:6px 24px;
  background:rgba(9,11,17,.82);backdrop-filter:blur(28px) saturate(180%);-webkit-backdrop-filter:blur(28px) saturate(180%);
  border-bottom:none;
  box-shadow:none;
  transition:transform 0.32s cubic-bezier(.16,1,.3,1), opacity 0.25s ease;
  transform:translateY(0); opacity:1;
}
.topbar2.headbar-hidden{
  transform:translateY(-100%);
  opacity:0;
  pointer-events:none;
}

.logo{display:flex;align-items:center;gap:12px;}
.logo .mark{
  width:34px;height:34px;border-radius:10px;flex-shrink:0;
  background:linear-gradient(135deg, var(--cyan), var(--violet), var(--magenta));
  box-shadow:none;
  position:relative;overflow:hidden;
}
.logo .mark::after{
  content:'';position:absolute;inset:1px;border-radius:9px;background:rgba(7,9,14,0.4);backdrop-filter:blur(2px);
}
.logo .word{font-family:'Space Grotesk';font-weight:700;font-size:18px;letter-spacing:-.02em;color:#FFF;}
.logo .word span{color:var(--accent);font-weight:600;font-size:10px;display:block;margin-top:-1px;letter-spacing:.08em;text-transform:uppercase;opacity:.9;}

.xp-bar-container{
  background:rgba(15,19,28,.8) !important;
  border:1px solid rgba(255,255,255,.08) !important;
  border-radius:12px !important;
  padding:6px 12px !important;
  box-shadow:inset 0 1px 1px rgba(255,255,255,.05);
}
#lvlBadge{
  background:linear-gradient(135deg, rgba(var(--accent-rgb),.25), rgba(var(--violet-rgb),.2)) !important;
  border:1px solid rgba(var(--accent-rgb),.4) !important;
  color:var(--accent) !important;
  box-shadow:0 0 12px rgba(var(--accent-rgb),.2);
}

/* ---------- top tabs navigation ---------- */
.top-tabs{
  position:sticky;top:46px;z-index:59;
  display:flex;align-items:center;gap:4px;
  padding:6px 20px;
  background:rgba(9,11,17,.88);backdrop-filter:blur(24px);-webkit-backdrop-filter:blur(24px);
  border-bottom:1px solid var(--card-brd);
  overflow-x:auto;
  scrollbar-width:none;
  transition:top 0.32s cubic-bezier(.16,1,.3,1);
}
.top-tabs.headbar-hidden-tabs{
  top:0 !important;
}
.top-tabs::-webkit-scrollbar{display:none;}
.top-tab{
  display:flex;align-items:center;gap:8px;padding:10px 14px 8px 14px;
  font-size:13px;font-weight:600;color:var(--text-dim);
  cursor:pointer;white-space:nowrap;border-radius:0;
  background:transparent;
  border-bottom:2px solid transparent;
  transition:all .22s cubic-bezier(.16,1,.3,1);position:relative;
  font-family:'Inter',sans-serif;
}
.top-tab:hover{color:var(--text);background:transparent;border-bottom-color:rgba(255,255,255,0.25);}
.top-tab.active{
  color:var(--accent);
  background:transparent !important;
  border-bottom:2px solid var(--accent) !important;
  box-shadow:none !important;
}
.top-tab .tab-icon{font-size:15px;}
.top-tab .tab-dot{
  width:6px;height:6px;border-radius:50%;background:var(--accent);
  opacity:0;transition:opacity .2s, transform .2s;
}
.top-tab.active .tab-dot{opacity:1;box-shadow:0 0 10px rgba(var(--accent-rgb),.8);transform:scale(1.2);}

/* ---------- rich page header ---------- */
.page-hero{
  display:flex;align-items:center;gap:18px;margin-bottom:28px;padding:24px 20px 20px;
  background:linear-gradient(90deg, rgba(var(--accent-rgb),.05), transparent 60%);
  border-radius:var(--radius);
  border:1px solid rgba(255,255,255,.06);
  position:relative;overflow:hidden;
}
.page-hero::after{
  content:'';position:absolute;bottom:0;left:0;right:0;height:1px;
  background:linear-gradient(90deg, rgba(var(--accent-rgb),.5), rgba(var(--violet-rgb),.3) 50%, transparent);
}
.page-hero-icon{
  width:52px;height:52px;border-radius:16px;display:flex;align-items:center;justify-content:center;
  font-size:26px;flex-shrink:0;
  background:linear-gradient(135deg, rgba(var(--accent-rgb),.22), rgba(var(--accent-rgb),.06));
  border:1px solid rgba(var(--accent-rgb),.3);
  box-shadow:0 0 24px rgba(var(--accent-rgb),.2);
}
.page-hero-text h1{
  font-size:24px;font-weight:700;letter-spacing:-.02em;color:#FFF;
  font-family:'Space Grotesk',sans-serif;
}
.page-hero-text p{
  color:var(--text-dim);font-size:13.5px;margin-top:4px;line-height:1.4;
}
.page-hero .badge{
  margin-left:auto;
  font-size:11.5px;font-weight:700;padding:8px 16px;border-radius:999px;
  background:linear-gradient(135deg, rgba(var(--accent-rgb),.15), rgba(var(--accent-rgb),.05));
  border:1px solid rgba(var(--accent-rgb),.35);
  color:var(--accent);letter-spacing:.04em;
  animation:badgePulse 3s ease-in-out infinite;
  box-shadow:0 0 16px rgba(var(--accent-rgb),.15);
}
@keyframes badgePulse{0%,100%{box-shadow:0 0 0 0 rgba(var(--accent-rgb),.25);}50%{box-shadow:0 0 0 8px rgba(var(--accent-rgb),0);}}

/* ---------- empty state ---------- */
.empty-state{
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  padding:56px 24px;text-align:center;
  background:rgba(255,255,255,.015);border-radius:var(--radius);border:1px dashed var(--card-brd);
}
.empty-state-icon{
  font-size:52px;margin-bottom:18px;opacity:.6;
  animation:emptyFloat 3s ease-in-out infinite;
}
@keyframes emptyFloat{0%,100%{transform:translateY(0);}50%{transform:translateY(-8px);}}
.empty-state-text{
  font-size:15px;color:var(--text);margin-bottom:6px;font-weight:600;
}
.empty-state-sub{
  font-size:13px;color:var(--text-dim);margin-bottom:20px;
}
.empty-state-btn{
  background:linear-gradient(135deg, rgba(var(--accent-rgb),.2), rgba(var(--accent-rgb),.08));
  border:1px solid rgba(var(--accent-rgb),.4);
  color:var(--accent);padding:11px 24px;border-radius:12px;
  font-size:13.5px;font-weight:700;cursor:pointer;transition:all .2s ease;
  box-shadow:0 4px 16px rgba(var(--accent-rgb),.15);
}
.empty-state-btn:hover{background:linear-gradient(135deg, rgba(var(--accent-rgb),.3), rgba(var(--accent-rgb),.15));transform:translateY(-2px);box-shadow:0 6px 20px rgba(var(--accent-rgb),.3);}

.page-switcher{position:relative;}
.ps-btn{
  display:flex;align-items:center;gap:10px;background:rgba(15,19,28,.8);border:1px solid var(--card-brd);
  color:var(--text);font-family:'Inter';font-weight:700;font-size:13.5px;padding:10px 18px;
  border-radius:12px;cursor:pointer;transition:all .2s ease;
  box-shadow:0 4px 14px var(--inner-box-bg);
}
.ps-btn:hover{border-color:rgba(var(--accent-rgb),.4);background:rgba(20,26,38,.9);transform:translateY(-1px);}
.ps-caret{color:var(--text-faint);font-size:11px;transition:.2s;}
.ps-btn.open .ps-caret{transform:rotate(180deg);}

.ps-menu{
  position:absolute;top:calc(100% + 8px);right:0;min-width:210px;
  background:rgba(12,15,22,.96);backdrop-filter:blur(24px);-webkit-backdrop-filter:blur(24px);
  border:1px solid var(--card-brd);
  border-radius:16px;padding:8px;
  display:none;flex-direction:column;gap:3px;z-index:70;
  box-shadow:0 12px 36px rgba(0,0,0,0.7), 0 0 0 1px rgba(255,255,255,0.05);
}
.ps-menu.open{display:flex;}
.ps-item{display:flex;align-items:center;gap:10px;padding:10px 14px;border-radius:10px;cursor:pointer;color:var(--text-dim);font-size:13px;font-weight:600;transition:.18s;}
.ps-item .ic{font-size:16px;width:20px;text-align:center;}
.ps-item:hover{background:rgba(255,255,255,.06);color:var(--text);transform:translateX(2px);}
.ps-item.active{background:rgba(var(--accent-rgb),.15);color:var(--accent);font-weight:700;}

.flame{animation:pulse 2.2s ease-in-out infinite;}
@keyframes pulse{0%,100%{transform:scale(1);filter:drop-shadow(0 0 6px rgba(255,140,40,.55));}50%{transform:scale(1.12);filter:drop-shadow(0 0 14px rgba(255,140,40,.9));}}

/* Micro-interactions */
.add-btn:active, .onb-btn:active, .ps-btn:active, .day-tab:active, input[type="checkbox"]:active {
  transform: scale(0.98) !important;
  transition: transform 0.05s ease !important;
}

@keyframes shimmer {
  0% { background-position: -1000px 0; }
  100% { background-position: 1000px 0; }
}
.skeleton-bg {
  animation: shimmer 2s infinite linear;
  background: linear-gradient(to right, var(--inner-box-bg-light) 4%, var(--inner-box-border) 25%, var(--inner-box-bg-light) 36%);
  background-size: 1000px 100%;
}
#skeletonScreen {
  position: absolute; top: 0; left: 0; width: 100%; height: 100%;
  background: var(--bg); z-index: 50; padding: 20px 16px; pointer-events: none;
  display: flex; flex-direction: column; gap: 16px;
  transition: opacity 0.3s ease;
}
#skeletonScreen.hidden, #skeletonScreen[style*="display: none"], #skeletonScreen[style*="display:none"] {
  display: none !important;
  visibility: hidden !important;
  opacity: 0 !important;
  pointer-events: none !important;
  z-index: -100 !important;
  height: 0 !important;
  overflow: hidden !important;
}

/* ---------- main ---------- */
.main2{flex:1;max-width:100%;margin:0 auto;padding:16px 24px 80px;overflow-x:auto;}
.topline{display:none;}

.page{display:none;animation:pageIn .45s cubic-bezier(.16,1,.3,1);}
.page.active{display:block;}
@keyframes pageIn{from{transform:translateY(12px) scale(.995);}to{transform:none;}}

/* ---------- bento grid ---------- */
.bento{display:grid;grid-template-columns:repeat(12,1fr);gap:18px;margin-bottom:18px;}
.s2{grid-column:span 2;} .s3{grid-column:span 3;} .s4{grid-column:span 4;} .s5{grid-column:span 5;}
.s6{grid-column:span 6;} .s7{grid-column:span 7;} .s8{grid-column:span 8;}
.s9{grid-column:span 9;} .s12{grid-column:span 12;}

.card{
  background:linear-gradient(145deg, rgba(255,255,255,.04), rgba(255,255,255,.015));
  border:1px solid var(--card-brd);border-radius:var(--radius);
  padding:22px 24px;position:relative;
  /* No opacity here, and none in cardIn: a running-but-not-advancing animation holds
     its `from` value, so an opacity keyframe pinned every card at 0 whenever frames
     stopped (background tab, throttling). Verified: playState "running" with
     currentTime stuck at 0 while document.visibilityState === "hidden".
     Animating transform alone keeps the entrance and can never hide a card. */
  animation:cardIn .6s cubic-bezier(.16,1,.3,1) forwards;
  transition:transform .25s cubic-bezier(.16,1,.3,1), box-shadow .25s cubic-bezier(.16,1,.3,1), border-color .25s cubic-bezier(.16,1,.3,1);
  box-shadow:none;
}
.card:hover{
  border-color:rgba(var(--accent-rgb),.3);
  background:linear-gradient(145deg, rgba(255,255,255,.055), rgba(255,255,255,.025));
  box-shadow:0 16px 40px -12px rgba(0,0,0,0.8), 0 0 24px -4px rgba(var(--accent-rgb),.12);
  transform:translateY(-3px);
}
/* `to` deliberately omits `transform`: a filling animation outranks normal
   declarations, so pinning transform here permanently suppressed .card:hover's
   translateY(-3px). Omitting it makes the fill resolve to the underlying
   cascade value, which is `none` at rest and the hover lift while hovered. */
@keyframes cardIn{from{transform:translateY(16px) scale(.98);}}
/* The circular-wheel card is ~1200px tall and holds a ~440-node SVG. Animating its
   opacity promotes it to its own composited layer whose raster is scheduled
   independently, so after a full-document repaint (theme switch) the part below the
   viewport could still be unrastered when scrolled into view — a band of page
   background that only cleared once the pointer forced a repaint.
   Measured over 36 scroll captures each: 8.3% showed the band with this animation,
   0% without it. Every other card keeps its fade-in. */
#circularWheelBox{animation:none !important;opacity:1 !important;}
.card h3.section-title{
  font-size:12px;font-weight:700;color:var(--text-dim);text-transform:uppercase;letter-spacing:.09em;margin-bottom:16px;
  display:flex;align-items:center;justify-content:space-between;
}

.stat-card .num{
  font-family:'Space Grotesk';font-weight:700;font-size:28px;letter-spacing:-.02em;display:flex;align-items:center;gap:8px;
  background:linear-gradient(135deg, #FFFFFF, #CBD5E1);
  -webkit-background-clip:text;-webkit-text-fill-color:transparent;
}
.stat-card .lbl{font-size:11.5px;color:var(--text-dim);margin-top:6px;font-weight:600;text-transform:uppercase;letter-spacing:.07em;}
.stat-card .sub{font-size:11.5px;color:var(--text-faint);margin-top:6px;line-height:1.5;}
.g-cyan .num{color:var(--cyan); -webkit-text-fill-color:var(--cyan);}
.g-violet .num{color:var(--violet); -webkit-text-fill-color:var(--violet);}
.g-accent .num{color:var(--accent); -webkit-text-fill-color:var(--accent);}
.g-amber .num{color:var(--amber); -webkit-text-fill-color:var(--amber);}
.g-lime .num{color:var(--lime); -webkit-text-fill-color:var(--lime);}
.g-coral .num{color:var(--danger); -webkit-text-fill-color:var(--danger);}

/* category tag */
.cat-tag{font-size:10.5px;font-weight:700;padding:4px 10px;border-radius:8px;display:inline-flex;align-items:center;gap:5px;letter-spacing:.02em;}
.cat-health{background:rgba(var(--health-rgb),.15);color:var(--health);border:1px solid rgba(var(--health-rgb),.3);}
.cat-mind{background:rgba(var(--mind-rgb),.15);color:var(--mind);border:1px solid rgba(var(--mind-rgb),.3);}
.cat-work{background:rgba(var(--work-rgb),.15);color:var(--work);border:1px solid rgba(var(--work-rgb),.3);}

/* ---------- mini task rows ---------- */
.mini-task{display:flex;align-items:center;gap:12px;padding:10px 6px;border-bottom:1px solid rgba(255,255,255,.05);font-size:13.5px;transition:background .15s ease;border-radius:8px;}
.mini-task:hover{background:rgba(255,255,255,.02);}
.mini-task:last-child{border-bottom:none;}
.mini-task input{width:17px;height:17px;accent-color:var(--accent); cursor:pointer; transition:transform 0.15s;}
.mini-task input:active{transform:scale(0.85);}
.mini-task.done label{text-decoration:line-through;color:var(--text-faint);}
.link-more{font-size:12px;color:var(--accent);font-weight:700;cursor:pointer;margin-top:12px;display:inline-flex;align-items:center;gap:4px;transition:all .2s ease;}
.link-more:hover{color:#FFF;transform:translateX(3px);}

/* ---------- heatmap ---------- */
.heat-month-label{font-size:13px;color:var(--text);font-weight:700;margin-bottom:14px;font-family:'Space Grotesk';letter-spacing:-.01em;}
.heatmap-wrap{display:flex;gap:16px;align-items:flex-start;}
/* One token drives the whole heatmap. renderHeatmap() writes
   grid-template-columns inline (the week count is only known at runtime), and
   an inline declaration cannot be overridden by a media query — but the value
   inside it can be inherited. So the phone tier retunes --heat-cell and the
   inline rule follows it, whatever the week count is. Desktop resolves to the
   same 22px it always used. */
:root{--heat-cell:22px;}
.heat-grid{display:grid;grid-auto-flow:column;grid-template-rows:repeat(7,var(--heat-cell));gap:7px;}
.heat-cell{width:var(--heat-cell);height:var(--heat-cell);border-radius:6px;background:rgba(255,255,255,.05);transition:all .15s ease;}
.heat-cell.has-data{cursor:pointer;}
.heat-cell.has-data:hover{transform:scale(1.35);z-index:2;position:relative;box-shadow:0 0 12px rgba(var(--accent-rgb),.6);}
.heat-legend{display:flex;align-items:center;gap:6px;font-size:11px;color:var(--text-faint);margin-top:16px;}
.heat-legend .heat-cell{width:13px;height:13px;cursor:default;}
.heat-legend .heat-cell.has-data:hover{transform:none;}
.heat-daylabels{display:grid;grid-template-rows:repeat(7,var(--heat-cell));gap:7px;margin-right:8px;}
.heat-daylabels span{font-size:10px;color:var(--text-faint);line-height:var(--heat-cell);font-weight:600;}

/* ---------- svg charts ---------- */
.svgchart{width:100%;}
/* No opacity entrance on charts: the base value is what a frozen animation leaves
   behind, so `opacity:0` + fade-in meant every chart stayed blank whenever frames
   stopped advancing. The bars' scaleY grow already supplies the entrance. */
.svgchart svg{display:block;width:100%;height:auto;overflow:visible;}
.svgchart .bar-val{fill:#64748B;font-size:10px;font-weight:600;}
.bar-rect{transform-box:fill-box;transform-origin:50% 100%;transform:scaleY(0);transition:transform .8s cubic-bezier(.16,1,.3,1);}
.bar-rect.grown{transform:scaleY(1);}
.bar-rect.future-bar{transform:scaleY(1);}
.line-path{transition:stroke-dashoffset 1.1s cubic-bezier(.16,1,.3,1);}
.line-area{opacity:0;transition:opacity .8s ease .4s;}
.line-area.shown{opacity:1;}
.donut-wrap{display:flex;gap:24px;align-items:center;flex-wrap:wrap;}
.donut-wrap svg{width:256px!important; height:256px!important; flex-shrink:0;}
.donut-legend{display:flex;flex-direction:column;gap:8px;}
.donut-legend-row{display:flex;align-items:center;gap:10px;font-size:12px;color:var(--text-dim);}
.donut-legend-dot{width:10px;height:10px;border-radius:3px;flex-shrink:0;}
.donut-legend-val{margin-left:auto;font-weight:700;color:var(--text);font-family:'Space Grotesk';font-size:12px;padding-left:16px;}

/* ---------- rings ---------- */
.rings-flower{display:flex;justify-content:flex-start;gap:24px;flex-wrap:wrap;}
.ring-item{display:flex;flex-direction:column;align-items:center;gap:10px;width:92px;}
.ring-item svg{transform:rotate(-90deg);}
.ring-fg{transition:stroke-dashoffset 1.1s cubic-bezier(.16,1,.3,1);}
.ring-item .ring-label{font-size:11px;color:var(--text-dim);text-align:center;font-weight:600;line-height:1.3;}
.ring-item .ring-pct{font-family:'Space Grotesk';font-weight:700;font-size:15px;fill:var(--text);}

/* ---------- today dashboard: unified single-panel habit hub ---------- */
.hub-panel{background:none;border:none;border-radius:0;padding:0;backdrop-filter:none;width:100%;min-width:0;}
.hub-top{display:grid;grid-template-columns:180px 28px minmax(0, 1fr) 28px 260px;gap:0;align-items:flex-start;margin-bottom:12px;width:100%;box-sizing:border-box;}
.hub-top-left{width:180px;min-width:180px;max-width:180px;box-sizing:border-box;display:flex;flex-direction:column;align-items:center;text-align:center;gap:4px;overflow:hidden;background:var(--glass-bg-card);border:1px solid var(--card-brd);border-radius:0 !important;padding:12px;}
.hub-top-mid{width:100%;min-width:0;box-sizing:border-box;margin-left:0;padding:0;}
.hub-top-right{width:260px;min-width:260px;max-width:260px;box-sizing:border-box;padding:0;}

.ov-ring-wrap{position:relative;width:80px;height:80px;margin:0 auto;filter:none !important;box-shadow:none !important;background:none !important;overflow:visible !important;}
.ov-ring-wrap svg{position:absolute;inset:0;filter:none !important;box-shadow:none !important;}
.ov-ring-wrap circle{filter:none !important;box-shadow:none !important;}
.ov-ring-wrap svg{position:absolute;inset:0;}
.ov-ring-pct{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-family:'Space Grotesk';font-weight:800;font-size:18px;color:var(--accent);text-shadow:none;}
.ov-stats{display:flex;flex-direction:column;gap:3px;width:100%;text-align:left;}
.ov-stat{font-size:13.5px;color:var(--text);font-weight:600;display:flex;justify-content:space-between;gap:6px;padding:3px 0;border-bottom:1px solid rgba(255,255,255,.07);white-space:nowrap;overflow:hidden;}
.ov-stat:last-child{border-bottom:none;}
.ov-stat > span{display:inline-flex;align-items:center;gap:6px;}
.ov-stat b{color:var(--accent);font-family:'Space Grotesk';font-weight:800;font-size:15px;text-shadow:none;}

.week-avg-row{display:flex;gap:6px;margin-top:4px;width:100%;box-sizing:border-box;}
.wa-item{text-align:center;min-width:0;background:rgba(255,255,255,.03);border-radius:8px;padding:5px 0;display:flex;flex-direction:column;align-items:center;justify-content:center;position:relative;overflow:hidden;border:1px solid rgba(255,255,255,.08);}
.wa-pct{font-family:'Space Grotesk';font-weight:700;font-size:12.5px;z-index:2;}
.wa-track{position:absolute;bottom:0;left:0;right:0;height:4px;width:100%;background:rgba(255,255,255,.06);}
.wa-fill{height:100%;width:0%;transition:width .9s cubic-bezier(.16,1,.3,1);}
.wa-label{font-size:9px;color:var(--text-faint);margin-top:2px;text-transform:uppercase;letter-spacing:.06em;z-index:2;font-weight:700;}

.rings-flower-sm{display:grid;grid-template-columns:repeat(2,1fr);gap:12px 12px;width:100%;}

/* Lifted verbatim out of two inline style attributes on the wheel card. The
   phone tier collapses both by default, and `display:none` in a media query
   cannot beat an inline `display:flex` without !important — moving the
   declarations here keeps that override a plain rule. Desktop is unchanged;
   the pixel baseline is what proves it. */
.wheel-legend{display:flex;align-items:center;gap:14px;background:var(--surface-bg);padding:6px 14px;border-radius:20px;border:1px solid var(--card-brd);}
.wheel-body{display:flex;flex-direction:column;align-items:center;justify-content:center;position:relative;min-height:380px;}
/* Desktop has the room to just show the wheel, so the control does not exist there. */
#wheelToggleBtn{display:none;}
.ring-card-item:hover{background:var(--surface-border) !important; border-color:rgba(var(--accent-rgb),0.3) !important; transform:translateY(-2px); box-shadow:0 8px 20px rgba(0,0,0,0.6);}

/* ---------- unified habit table ---------- */
.hub-table-wrap{
  /* No backdrop-filter here: this is a scroll container with position:sticky
     descendants (td.hub-name), and Chromium fails to invalidate a backdrop-filter
     render surface as those sticky cells move -> stale black/white rectangles that
     clear on any forced repaint. --table-bg is the opaque equivalent of the old
     rgba(10,13,20,.7)+blur(20px), so the panel looks the same. */
  overflow-x:auto; border-radius:0; border:1px solid var(--card-brd);
  /* var(--bg), not var(--table-bg): #090C12 against the page's #07090E read as
     a lighter panel floating on the Today page while every other section sits
     flush on the background. The sticky habit-name column and the sticky header
     row below carry the same colour for the same reason — a scroll container
     whose frozen edges are a different shade is the same artifact, just smaller.
     Light theme overrides all three with !important further up, so it keeps the
     white panel it is designed around. */
  background:var(--bg); width:100%;
  box-shadow:0 12px 32px rgba(0,0,0,0.5);
}
table.hub-table{border-collapse:collapse;width:100%;font-size:13px;table-layout:fixed;}
/* Phone-only cells, off by default. renderHubTable emits a second % column right
   after the habit names and a month label above them, because a table column
   cannot be moved with CSS and the desktop order is not changing. display:none
   on a cell takes no column, so the desktop table is exactly what it was. */
.hub-month-m, .hub-pct-mh, td.hub-pct-m, .feed-head,
.task-date-icon, .today-add-cta, .tk-label-short, .m-title-short, .m-history-head, .hub-name-m,
.grid-month, .hub-h-m, .task-flat,
.hb-add-caret, .hg-h-m, .hb-seg, .hb-log-m, .hb-row-mobile,
.hb-manage-btn, .hb-panel-head, .hb-del-h { display: none; }

/* ---- the three monthly notes (desktop and shared) ---- */
#page-today textarea[id^="monthly"] { display: none; }
#page-today .note-editing > textarea[id^="monthly"] { display: block; }
#page-today .note-editing > .note-read { display: none; }
#page-today .note-read {
  display: block;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--text);
  white-space: pre-wrap;
  word-break: break-word;
  background: var(--card-bg);
  border: 1px solid var(--card-brd);
  border-radius: 10px;
  padding: 10px;
  box-sizing: border-box;
}
#page-today .note-read.is-empty { color: var(--text-faint); font-style: italic; opacity: 0.75; }
#page-today .note-edit {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  margin-left: auto;
  background: var(--surface-bg);
  border: 1px solid var(--card-brd);
  border-radius: 6px;
  padding: 0;
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
  color: var(--text-dim);
  transition: all 0.2s;
}
#page-today .note-edit:hover { color: var(--accent); border-color: var(--accent); background: rgba(var(--accent-rgb), 0.1); }
#page-today .note-save { display: none; }
#page-today .note-editing > .note-save {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  margin-left: auto;
  min-height: 28px;
  padding: 0 12px;
  background: rgba(var(--accent-rgb), 0.14);
  border: 1px solid var(--accent);
  border-radius: 8px;
  color: var(--accent);
  font-family: inherit;
  font-size: 11.5px;
  font-weight: 800;
  cursor: pointer;
}

/* "Join" is the phone's label; the desktop keeps "+ Join Challenge", which is
   what its baseline was recorded with. Same button, same handler. */
.c-join-m, .hb-log-sub-m { display: none; }
/* These wrap a part of a heading so the phone can drop it, and .card
   h3.section-title is a flex container with justify-content:space-between — so
   a plain span became a flex item and space-between pushed it to the far end of
   the heading. display:contents keeps the text in the same run it was in, which
   is what the desktop had before any of this. */
.heat-cat-part, .heat-month-part { display: contents; }
table.hub-table th,table.hub-table td{padding:0;text-align:center; height:28px;}
table.hub-table thead th{padding:6px 4px;color:var(--text-faint);font-weight:700;font-size:11px;border-bottom:1px solid var(--card-brd); border-right:1px solid rgba(255,255,255,0.05); background:var(--bg);}
table.hub-table .hub-name-h{width:190px;min-width:190px;max-width:190px;box-sizing:border-box;font-size:14px;font-weight:800;color:var(--text);text-align:left;padding-left:12px;}
table.hub-table td.hub-name{text-align:left;padding:6px 12px;color:var(--text);font-weight:700;white-space:nowrap;position:sticky;left:0;background:var(--bg);border-right:1px solid var(--card-brd);width:190px;min-width:190px;max-width:190px;box-sizing:border-box;font-size:14px; z-index:5;}
.hub-name-ic{margin-right:8px;font-size:16px;}
table.hub-table tbody tr:not(:last-child) td{border-bottom:1px solid rgba(255,255,255,.06);}
table.hub-table .hub-pct-h,table.hub-table td.hub-pct{border-left:1px solid var(--card-brd);}
td.hub-pct{font-family:'Space Grotesk';font-weight:700;font-size:11px;white-space:nowrap;padding:0 4px;}
td.hub-streak{color:var(--text-faint);font-size:10px;white-space:nowrap;padding:0 4px;border-right:1px solid var(--card-brd);}
.wt-bar-track{width:88%;height:6px;background:rgba(255,255,255,.08);border-radius:4px;overflow:hidden;flex-shrink:0;margin:0 auto;}
.wt-bar-fill{height:100%;width:0%;border-radius:4px;transition:width .8s cubic-bezier(.16,1,.3,1);}

/* Translucent Glowing Header Columns */
.th-wk-0 { background: linear-gradient(135deg, rgba(0, 229, 255, 0.85), rgba(0, 180, 216, 0.9)) !important; color: #ffffff !important; border-right: 1px solid rgba(255,255,255,0.07) !important; font-weight:700 !important; }
.th-wk-1 { background: linear-gradient(135deg, rgba(255, 62, 160, 0.85), rgba(216, 0, 115, 0.9)) !important; color: #ffffff !important; border-right: 1px solid rgba(255,255,255,0.07) !important; font-weight:700 !important; }
.th-wk-2 { background: linear-gradient(135deg, rgba(155, 107, 255, 0.85), rgba(120, 60, 235, 0.9)) !important; color: #ffffff !important; border-right: 1px solid rgba(255,255,255,0.07) !important; font-weight:700 !important; }
.th-wk-3 { background: linear-gradient(135deg, rgba(16, 185, 129, 0.85), rgba(5, 150, 105, 0.9)) !important; color: #ffffff !important; border-right: 1px solid rgba(255,255,255,0.07) !important; font-weight:700 !important; }
.th-wk-4 { background: linear-gradient(135deg, rgba(255, 178, 36, 0.85), rgba(217, 130, 0, 0.9)) !important; color: #ffffff !important; border-right: 1px solid rgba(255,255,255,0.07) !important; font-weight:700 !important; }

.td-wk-0 { background: transparent !important; border-right: 1px solid rgba(255, 255, 255, 0.07) !important; }
.td-wk-1 { background: transparent !important; border-right: 1px solid rgba(255, 255, 255, 0.07) !important; }
.td-wk-2 { background: transparent !important; border-right: 1px solid rgba(255, 255, 255, 0.07) !important; }
.td-wk-3 { background: transparent !important; border-right: 1px solid rgba(255, 255, 255, 0.07) !important; }
.td-wk-4 { background: transparent !important; border-right: 1px solid rgba(255, 255, 255, 0.07) !important; }

.td-checked-0 { background: rgba(0, 229, 255, 0.75) !important; border-right: 1px solid rgba(255,255,255,0.25) !important; }
.td-checked-1 { background: rgba(255, 62, 160, 0.75) !important; border-right: 1px solid rgba(255,255,255,0.25) !important; }
.td-checked-2 { background: rgba(155, 107, 255, 0.75) !important; border-right: 1px solid rgba(255,255,255,0.25) !important; }
.td-checked-3 { background: rgba(16, 185, 129, 0.75) !important; border-right: 1px solid rgba(255,255,255,0.25) !important; }
.td-checked-4 { background: rgba(255, 178, 36, 0.75) !important; border-right: 1px solid rgba(255,255,255,0.25) !important; }

/* Demo Mode Clickable Trigger Styles */
.hub-name-h.demo-clickable {
  cursor: pointer !important;
  transition: all 0.25s ease;
  user-select: none;
}
.hub-name-h.demo-clickable:hover {
  color: var(--accent) !important;
  text-shadow: 0 0 10px rgba(var(--accent-rgb), 0.6);
}
.habit-reveal-highlight {
  box-shadow: inset 0 0 0 1px var(--accent) !important;
  background: rgba(0, 229, 255, 0.05) !important;
  border-radius: 0 !important;
  transition: box-shadow 0.2s, background 0.2s !important;
}

.demo-trigger-badge {
  display: inline-block;
  margin-left: 6px;
  font-size: 9px;
  font-weight: 700;
  background: linear-gradient(135deg, rgba(0, 229, 255, 0.25), rgba(155, 107, 255, 0.25));
  border: 1px solid rgba(0, 229, 255, 0.5);
  color: #00E5FF;
  padding: 2px 6px;
  border-radius: 6px;
  vertical-align: middle;
  animation: pulseBadge 2s infinite ease-in-out;
}
@keyframes pulseBadge {
  0%, 100% { transform: scale(1); opacity: 0.9; }
  50% { transform: scale(1.05); opacity: 1; box-shadow: 0 0 8px rgba(0, 229, 255, 0.6); }
}

/* ---------- category balance bars ---------- */
.balance-row{display:flex;align-items:center;gap:14px;margin-bottom:16px;}
.balance-row:last-child{margin-bottom:0;}
.balance-row .bl-label{width:115px;font-size:13px;font-weight:600;flex-shrink:0;display:flex;align-items:center;gap:6px;}
.balance-row .bl-track{flex:1;height:10px;border-radius:6px;background:rgba(255,255,255,.06);overflow:hidden;}
.balance-row .bl-fill{height:100%;border-radius:6px;width:0;transition:width 1s cubic-bezier(.16,1,.3,1);}
.balance-row .bl-pct{width:42px;text-align:right;font-family:'Space Grotesk';font-weight:700;font-size:13px;flex-shrink:0;}

/* ---------- habit detail table ---------- */
.htable{width:100%;border-collapse:collapse;font-size:13px;}
.htable th{text-align:left;padding:10px 12px;color:var(--text-faint);font-weight:700;font-size:10.5px;text-transform:uppercase;letter-spacing:.05em;border-bottom:1px solid var(--card-brd);}
.htable td{padding:12px 12px;border-bottom:1px solid rgba(255,255,255,.05);vertical-align:middle;}
.htable tr:last-child td{border-bottom:none;}
.htable input[type=text]{background:rgba(15,19,28,.8);border:1px solid var(--card-brd);border-radius:10px;padding:8px 12px;color:var(--text);font-size:12.5px;width:100%;font-family:'Inter';transition:all .2s;}
.htable input[type=text]:focus{outline:none;border-color:var(--accent);box-shadow:0 0 12px rgba(var(--accent-rgb),.25);}
.htable select{background:rgba(15,19,28,.8);border:1px solid var(--card-brd);border-radius:10px;padding:8px 10px;color:var(--text);font-size:12.5px;}
.mini-bar{width:76px;height:8px;border-radius:4px;background:rgba(255,255,255,.08);overflow:hidden;display:inline-block;vertical-align:middle;margin-right:8px;}
.mini-bar-fill{height:100%;border-radius:4px;width:0;transition:width .9s cubic-bezier(.16,1,.3,1);}
.del-x{background:none;border:none;color:var(--text-faint);cursor:pointer;font-size:15px;transition:color .2s;}
.del-x:hover{color:var(--danger);}
.ring-item{position:relative;}
.ring-del-btn{position:absolute;top:-3px;right:-3px;width:20px;height:20px;border-radius:50%;background:rgba(255,62,160,0.9);color:#fff;border:none;font-size:12px;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center;opacity:0;transition:all 0.2s ease;z-index:15;box-shadow:0 2px 8px rgba(0,0,0,0.4);}
.ring-item:hover .ring-del-btn{opacity:1;}
.ring-del-btn:hover{background:#ff0055;transform:scale(1.15);}
.grid-del-btn{background:none;border:none;color:var(--text-faint);font-size:13px;margin-left:8px;cursor:pointer;opacity:0;transition:all 0.2s ease;}
.habit-name-inner:hover .grid-del-btn{opacity:1;}
.grid-del-btn:hover{color:#ff3ea0;}
.del-x-btn{background:rgba(255,62,160,0.14);border:1px solid rgba(255,62,160,0.35);color:#ff4d94;border-radius:8px;padding:5px 12px;font-size:12px;font-weight:700;cursor:pointer;transition:all 0.2s ease;}
.del-x-btn:hover{background:#ff3ea0;color:#fff;border-color:#ff3ea0;box-shadow:0 0 12px rgba(255,62,160,0.5);}

/* ---------- Tasks Page Grid & Calendar ---------- */
.tasks-grid-layout { display: grid; grid-template-columns: 1fr 350px; gap: 24px; align-items: start; }
@media (max-width: 900px) { .tasks-grid-layout { grid-template-columns: 1fr; } }
.cal-day-cell { aspect-ratio: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; border-radius: 10px; background: rgba(255,255,255,0.025); border: 1px solid rgba(255,255,255,0.07); cursor: pointer; transition: all 0.2s ease; position: relative; }
.cal-day-cell:hover { background: var(--inner-box-border); border-color: rgba(var(--accent-rgb),0.3); transform: translateY(-2px); }
.cal-day-cell.is-selected {
  background: rgba(var(--accent-rgb), 0.22) !important;
  color: var(--accent) !important;
  font-weight: 900 !important;
  border: 2px solid var(--accent) !important;
  box-shadow: 0 0 16px rgba(var(--accent-rgb), 0.5) !important;
}
.cal-day-cell.is-today { border: 2px solid #00FFA3 !important; background: rgba(0, 255, 163, 0.08) !important; }
.cal-day-cell.is-today::after { content: ''; position: absolute; top: 3px; right: 3px; width: 6px; height: 6px; border-radius: 50%; background: #00FFA3; box-shadow: 0 0 8px #00FFA3; }
.cal-day-cell.is-selected.is-today::after { background: #00FFA3 !important; box-shadow: 0 0 8px #00FFA3 !important; }
.cal-day-cell .cal-num { font-size: 13px; font-weight: 700; font-family: 'Space Grotesk', sans-serif; }
.cal-day-cell.is-selected .cal-num { color: var(--accent) !important; text-shadow: 0 0 8px rgba(var(--accent-rgb), 0.6) !important; font-weight: 900 !important; }
.cal-day-dots { display: flex; gap: 3px; margin-top: 3px; height: 4px; }

/* Custom Dropdown Select */
select, select.onb-input, .htable select {
  background: rgba(14, 18, 26, 0.95) !important;
  color: var(--text) !important;
  border: 1px solid var(--card-brd) !important;
  border-radius: 12px !important;
  padding: 10px 38px 10px 14px !important;
  font-family: 'Inter', sans-serif !important;
  font-size: 13.5px !important;
  font-weight: 600 !important;
  cursor: pointer !important;
  outline: none !important;
  appearance: none !important;
  -webkit-appearance: none !important;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%2300E5FF' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E") !important;
  background-repeat: no-repeat !important;
  background-position: right 14px center !important;
  transition: all 0.2s ease !important;
}
select:hover, select:focus {
  border-color: var(--accent) !important;
  box-shadow: 0 0 14px rgba(var(--accent-rgb), 0.3) !important;
}
select option {
  background-color: #0b0e14 !important;
  color: #f3f4f7 !important;
  padding: 12px 16px !important;
  font-size: 13px !important;
  font-weight: 600 !important;
}

/* Custom Sleek Glass Scrollbars */
::-webkit-scrollbar { width: 7px; height: 7px; }
::-webkit-scrollbar-track { background: rgba(0, 0, 0, 0.25); }
::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.16); border-radius: 99px; }
::-webkit-scrollbar-thumb:hover { background: var(--accent); }

/* Settings Swatches */
.color-circle {
  width: 40px; height: 40px; border-radius: 12px; cursor: pointer;
  border: 2px solid transparent; transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1);
  display: flex; align-items: center; justify-content: center; position: relative;
}
.color-circle:hover { transform: scale(1.12); z-index: 2; }
.color-circle.active { border-color: #ffffff !important; box-shadow: none !important; transform: scale(1.15); }
.color-circle.active::after { content: '✓'; color: #000; font-size: 14px; font-weight: 900; }

#page-settings *, #page-settings .card, #page-settings .card:hover {
  box-shadow: none !important;
}

/* ---------- habit grid (month checkbox) ---------- */
/* backdrop-filter removed: scroll container with 23 position:sticky descendants
   (thead th + td.habit-name) — same stale-surface artifact as .hub-table-wrap.
   Measured visual contribution of the blur here was 0 px in both themes. */
.habit-grid-wrap{overflow-x:auto;border-radius:var(--radius);border:1px solid var(--card-brd);background:rgba(10,13,20,0.8);box-shadow:0 12px 32px rgba(0,0,0,0.5);}
table.habit-grid{border-collapse:collapse;width:100%;font-size:12.5px;}
table.habit-grid th,table.habit-grid td{padding:0;text-align:center;}
table.habit-grid thead th{padding:12px 6px;color:var(--text-faint);font-weight:700;font-size:11px;border-bottom:1px solid var(--card-brd);position:sticky;top:0;background:rgba(10,13,20,.95);}
table.habit-grid thead th.today-col{color:var(--cyan);font-weight:800;}
table.habit-grid td.habit-name{text-align:left;padding:12px 18px;color:var(--text);font-weight:600;white-space:nowrap;position:sticky;left:0;background:rgba(11,14,21,.96);border-right:1px solid var(--card-brd);min-width:200px;max-width:200px;overflow:hidden;text-overflow:ellipsis;font-size:13px;}
.habit-name-inner{display:flex;align-items:center;gap:10px;}
.habit-swatch{width:10px;height:10px;border-radius:4px;flex-shrink:0;}
.habit-mini-pct{margin-left:auto;font-size:11px;font-weight:700;font-family:'Space Grotesk';opacity:.85;}
table.habit-grid tbody tr:not(:last-child) td{border-bottom:1px solid rgba(255,255,255,.04);}

.chk{
  width:16px;
  height:16px;
  border-radius:4px;
  border:1.5px solid rgba(255,255,255,.45);
  background:var(--inner-box-bg-light) !important;
  cursor:pointer;
  margin:2px auto;
  display:block;
  transition: all 0.18s cubic-bezier(0.16, 1, 0.3, 1);
  position:relative;
}
.chk:hover{
  border-color:var(--accent);
  background:rgba(var(--accent-rgb),.2) !important;
  transform:scale(1.15);
}
.chk.on{
  transform:none;
  background:#FFFFFF !important;
  border:1.5px solid #FFFFFF !important;
  box-shadow:none !important;
}
.chk.on::after{
  content:'✓';
  position:absolute;
  inset:0;
  display:flex;
  align-items:center;
  justify-content:center;
  font-size:12px;
  color:#07090E !important;
  font-weight:900;
}
.chk.future{
  opacity:1 !important;
  pointer-events:auto !important;
  cursor:pointer;
}
td.today-col{background:rgba(var(--cyan-rgb),.07);}

/* ---------- Bottom sheet tabs bar ---------- */
.sheet-tabs-bar {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  height: 42px;
  background: rgba(10, 13, 20, 0.95);
  backdrop-filter: blur(20px);
  border-top: 1px solid var(--card-brd);
  display: flex;
  align-items: center;
  padding: 0 20px;
  z-index: 100;
}
.sheet-tabs-list {
  display: flex;
  gap: 3px;
  height: 100%;
  align-items: flex-end;
  overflow-x: auto;
  scrollbar-width: none;
}
.sheet-tabs-list::-webkit-scrollbar {
  display: none;
}
.sheet-tab {
  background: rgba(18, 24, 36, 0.8);
  border: 1px solid var(--card-brd);
  border-bottom: none;
  border-radius: 8px 8px 0 0;
  padding: 6px 18px;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--text-dim);
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 7px;
  transition: all 0.18s ease;
  height: 34px;
  user-select: none;
}
.sheet-tab:hover {
  background: rgba(26, 34, 50, 0.9);
  color: var(--text);
}
.sheet-tab.active {
  background: var(--bg);
  color: var(--cyan);
  border-top: 3px solid var(--cyan);
  height: 37px;
  padding-top: 5px;
  font-weight: 700;
}
.sheet-tab-indicator {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--text-faint);
}
.sheet-tab.active .sheet-tab-indicator {
  background: var(--cyan);
  box-shadow: 0 0 8px var(--cyan);
}

.chart-card canvas{max-height:210px;}

/* ---------- task page ---------- */
.day-tabs{display:flex;gap:8px;margin-bottom:18px;}
.day-tab .n{display:block;font-family:'Space Grotesk';font-weight:700;font-size:16px;color:var(--text);margin-top:3px;}
.day-tab.active{border-color:rgba(var(--accent-rgb),.5);background:linear-gradient(160deg, rgba(var(--accent-rgb),.2), rgba(var(--accent-rgb),.05));color:var(--accent);box-shadow:0 0 16px rgba(var(--accent-rgb),.2);}
.day-tab.active .n{color:var(--accent);}
.focus-box{border:1px solid rgba(var(--accent-rgb),.35);background:linear-gradient(160deg, rgba(var(--accent-rgb),.12), rgba(var(--accent-rgb),.02));border-radius:14px;}
.focus-box h3{font-size:13.5px;color:var(--accent);font-weight:700;margin-bottom:14px;}
.focus-goal{display:flex;align-items:center;gap:12px;padding:8px 0;font-size:14px;}
.focus-goal input[type=checkbox]{accent-color:var(--accent);width:18px;height:18px;}
.focus-goal.done label{text-decoration:line-through;color:var(--text-faint);}

/* Kanban Board CSS */
.kanban-board { display: flex; gap: 18px; min-height: 420px; overflow-x: auto; padding-bottom: 14px; }
.kanban-col { flex: 1; min-width: 290px; background: rgba(14,18,26,.6); border: 1px solid var(--card-brd); border-radius: 16px; display: flex; flex-direction: column; backdrop-filter:blur(16px); }
.k-header { padding: 14px 18px; font-family: 'Space Grotesk', sans-serif; font-weight: 700; font-size: 13px; text-transform: uppercase; letter-spacing: 0.06em; background: var(--inner-box-bg); border-radius: 16px 16px 0 0; border-bottom: 1px solid var(--card-brd); }
.k-tasks { padding: 14px; flex: 1; display: flex; flex-direction: column; gap: 12px; overflow-y: auto; }
.k-task-card { background: linear-gradient(145deg, rgba(255,255,255,.04), rgba(255,255,255,.015)); border: 1px solid var(--card-brd); border-radius: 12px; padding: 14px; cursor: grab; position: relative; overflow: hidden; transition: all 0.22s ease; }
.k-task-card:hover { transform: translateY(-3px); border-color: rgba(255,255,255,.18); box-shadow: 0 8px 24px rgba(0,0,0,0.5); }
.k-task-card:active { cursor: grabbing; }
.k-task-pri { position: absolute; left: 0; top: 0; bottom: 0; width: 4px; }
.k-task-pri.high { background: var(--danger); box-shadow: 0 0 10px var(--danger); }
.k-task-pri.med { background: var(--amber); box-shadow: 0 0 10px var(--amber); }
.k-task-pri.low { background: var(--lime); box-shadow: 0 0 10px var(--lime); }
.k-task-title { font-size: 14px; font-weight: 600; color: var(--text); margin-bottom: 10px; padding-left: 8px; }
.k-task-meta { display: flex; justify-content: space-between; align-items: center; font-size: 11.5px; color: var(--text-dim); padding-left: 8px; }
.k-task-actions button { background: none; border: none; color: var(--text-faint); cursor: pointer; font-size: 15px; padding: 2px 6px; transition: 0.2s; }
.k-task-actions button:hover { color: var(--danger); }
.k-task-actions .star-btn.on { color: var(--amber); }
.add-row{display:flex;gap:10px;margin-top:16px;}
.add-row input,.add-row textarea{flex:1;background:rgba(15,19,28,.8);border:1px solid var(--card-brd);border-radius:12px;padding:12px 14px;color:var(--text);font-size:13.5px;font-family:'Inter';resize:vertical;transition:all .2s;}
.add-row input:focus,.add-row textarea:focus{outline:none;border-color:var(--accent);box-shadow:0 0 12px rgba(var(--accent-rgb),.25);}
.add-btn{border:none;border-radius:12px;padding:0 22px;font-weight:700;font-size:13.5px;cursor:pointer;transition:all .2s ease;}
.add-btn.habit, .add-btn.task, .add-btn.money, .add-btn{
  background:linear-gradient(135deg, rgba(var(--accent-rgb),1), rgba(var(--accent-rgb),.8));
  color:#07090E;
  box-shadow: 0 4px 16px rgba(var(--accent-rgb), 0.35);
}
.add-btn:hover{transform:translateY(-2px);box-shadow: 0 6px 22px rgba(var(--accent-rgb), 0.5);}

/* ---------- pomodoro timer ---------- */
.pomo-tabs { display: flex; justify-content: center; gap: 10px; margin-bottom: 34px; }
.pomo-tab { background: rgba(255,255,255,0.05); border: 1px solid var(--card-brd); color: var(--text-dim); padding: 9px 20px; border-radius: 99px; font-weight: 600; font-size: 13.5px; cursor: pointer; transition: 0.22s; }
.pomo-tab:hover { color: var(--text); background: var(--inner-box-border); }
.pomo-tab.active { background: var(--accent); color: var(--on-accent, #07090E); border-color: var(--accent); font-weight: 700; box-shadow: 0 0 18px rgba(var(--accent-rgb),.5); }
.pomo-circle { position: relative; width: 250px; height: 250px; margin: 0 auto 36px auto; filter: drop-shadow(0 0 24px rgba(var(--accent-rgb),0.25)); }
.pomo-circle svg { width: 100%; height: 100%; transform: rotate(-90deg); }
.pomo-bg { fill: none; stroke: var(--surface-border); stroke-width: 12; }
.pomo-progress { fill: none; stroke: var(--accent); stroke-width: 12; stroke-linecap: round; stroke-dasharray: 691; stroke-dashoffset: 0; transition: stroke-dashoffset 1s linear; }
.pomo-time { position: absolute; inset:0; display: flex; align-items: center; justify-content: center; font-size: 52px; font-weight: 800; font-family: 'Space Grotesk', sans-serif; color: #FFF; text-shadow:0 0 20px rgba(var(--accent-rgb),.4); }

/* ---------- money page ---------- */
.tx-table{width:100%;border-collapse:collapse;font-size:13.5px;}
.tx-table th{text-align:left;padding:10px 12px;color:var(--text-faint);font-weight:700;font-size:10.5px;text-transform:uppercase;letter-spacing:.05em;border-bottom:1px solid var(--card-brd);}
.tx-table td{padding:12px 12px;border-bottom:1px solid rgba(255,255,255,.05);}
.tx-table tr:hover td{background:rgba(255,255,255,.025);}
.tx-table tr:last-child td{border-bottom:none;}
.tag{font-size:11px;padding:4px 10px;border-radius:99px;background:rgba(255,255,255,.06);color:var(--text-dim);font-weight:600;}
.amt-in{color:var(--lime);font-weight:700;text-shadow:0 0 10px rgba(var(--lime-rgb),.3);}
.amt-out{color:var(--danger);font-weight:700;text-shadow:0 0 10px rgba(255,84,112,.3);}
.recur-item{display:flex;justify-content:space-between;align-items:center;padding:12px 4px;border-bottom:1px solid rgba(255,255,255,.05);font-size:13.5px;}
.recur-item:last-child{border-bottom:none;}
.recur-item .d{color:var(--text-faint);font-size:12px;margin-top:2px;}
/* ---------- layout grids ----------
   These column definitions used to sit in style="" attributes on the markup.
   A normal stylesheet rule loses to an inline one, so `.form-grid { 1fr }` in
   the 1024px block below was dead on the two pages that set their columns
   inline — those forms stayed five-across at every width. Column counts belong
   here so a breakpoint can reach them.

   (An `!important` rule does beat a normal inline style — verified — but that
   is an escape hatch for one-offs, not a reason to leave layout in markup.) */
.form-grid{display:grid;grid-template-columns:1.3fr 1fr .8fr 1fr auto;gap:10px;margin-top:16px;}
.form-grid--habit{grid-template-columns:1.3fr 1fr 1fr 1fr auto;gap:8px;}
.form-grid--money{grid-template-columns:2fr 1fr 1fr 1fr auto;gap:8px;}
.bento--4up{grid-template-columns:repeat(4,1fr);gap:12px;}
.ch-cols-1{grid-template-columns:repeat(1,1fr);gap:12px;}
.ch-cols-2{grid-template-columns:repeat(2,1fr);gap:12px;}
.ch-cols-3{grid-template-columns:repeat(3,1fr);gap:12px;}
.ch-cols-4{grid-template-columns:repeat(4,1fr);gap:12px;}
.ch-cols-5{grid-template-columns:repeat(5,1fr);gap:12px;}
.grid-week{display:grid;grid-template-columns:repeat(7,1fr);}
.grid-2up{display:grid;grid-template-columns:repeat(2,1fr);}
/* One cell per day of a challenge. auto-fit keeps it from forcing 15 columns
   into a phone, where each cell would be about 20px wide. */
.arena-grid{display:grid;grid-template-columns:repeat(15,1fr);gap:6px;}

.c-top-res-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  color: var(--text);
  border: none;
  padding: 0;
  font-size: 22px; /* Increased from 19px, but smaller than 28px */
  cursor: pointer;
  vertical-align: middle;
  z-index: 10;
  position: absolute !important;
  right: 16px !important;
  top: auto !important;
  bottom: auto !important;
  margin: 0 !important;
}

/* Ensure the title itself can stretch and layout the icon properly */
.challenge-card .cc-title {
  display: flex !important;
  align-items: center !important;
  width: 100% !important;
  flex: 1 !important;
  padding-right: 32px !important; /* Prevent overlap with absolute positioned icon */
}

.form-grid input,.form-grid select{background:rgba(15,19,28,.8);border:1px solid var(--card-brd);border-radius:12px;padding:11px 13px;color:var(--text);font-size:13px;font-family:'Inter';transition:all .2s;}
.form-grid input:focus,.form-grid select:focus{outline:none;border-color:var(--amber);box-shadow:0 0 14px rgba(var(--amber-rgb),.3);}

/* ---------- insights ---------- */
.insight-line{display:flex;gap:14px;padding:14px 6px;border-bottom:1px solid rgba(255,255,255,.05);font-size:14px;line-height:1.6;}
.insight-line:last-child{border-bottom:none;}
.insight-line .ic{font-size:20px;flex-shrink:0;}
.insight-line b{color:var(--text);}
textarea#reflectionInput{width:100%;min-height:100px;background:rgba(15,19,28,.8);border:1px solid var(--card-brd);border-radius:16px;padding:14px 16px;color:var(--text);font-size:13.5px;font-family:'Inter';resize:vertical;transition:all .2s;}
textarea#reflectionInput:focus{outline:none;border-color:var(--magenta);box-shadow:0 0 16px rgba(var(--magenta-rgb),.35);}

/* ---------- ONBOARDING & NEW PAGES ---------- */
#onboarding {
  display: none !important;
}
.onb-wrap { width: 100%; max-width: 460px; }
.onb-hero { text-align: center; margin-bottom: 40px; }
.onb-hero .wordmark { font-size: 28px; display: block; margin-bottom: 10px; font-family: 'Space Grotesk', sans-serif; font-weight: 800; letter-spacing: 0.05em; }
.onb-hero p { color: var(--text-dim); font-size: 14.5px; }
.onb-step { display: none; animation: fade .2s ease; }
.onb-step.active { display: block; }
.onb-label { font-family: 'Space Grotesk', sans-serif; text-transform: uppercase; letter-spacing: 0.06em; font-size: 13.5px; color: var(--text-dim); margin-bottom: 12px; }
.onb-nav { display: flex; gap: 12px; margin-top: 28px; }
.progress-dots { display: flex; gap: 8px; justify-content: center; margin-bottom: 28px; }
.dot { width: 8px; height: 8px; border-radius: 50%; background: var(--card-brd); }
.dot.on { background: var(--accent); box-shadow:0 0 10px var(--accent); }

.onb-input {
  width: 100%; background: rgba(15,19,28,.8); border: 1px solid var(--card-brd);
  color: var(--text); padding: 13px; border-radius: 12px; font-size: 15px; margin-bottom:14px;
}
.onb-input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 14px rgba(var(--accent-rgb), 0.3); }

/* The Display Mode card is gone from Settings on every tier. The header's
   light/dark button is the control now, and it is on both — a second switch for
   the same setting, one screen down, was the kind of duplication that leaves
   people wondering which one is authoritative. Hidden rather than deleted: the
   markup still carries the toggle other code reads. */
#settingsDisplayModeCard { display: none !important; }

/* Edit Profile only — the Profile Info and Password tabs. Scoped to the modal on
   purpose: .onb-input is also the onboarding form's input, and this is a styling
   request for this dialog, not a change to every form in the app.
   90% width is the requested 10% trim; the two fields that were asked to grow
   get it back through flex:1.1 on their column in index.html. */
#editProfileModal .onb-input,
#editProfileModal input[type="text"],
#editProfileModal input[type="password"],
#editProfileModal input[type="email"],
#editProfileModal input[type="tel"] {
  border-radius: 10px !important;
}

/* The 10% trim goes on the ROW, not the input. A percentage width on the input
   itself resolves against a flex column whose own width depends on its content,
   which collapsed every field to 30-60px — Full Name came out narrower than
   Username. Trimming the row keeps each input at 100% of its column, so the
   flex:1.1 / flex:0.9 split still decides which of the pair is wider. */
#editProfileModal #epViewPersona > div { width: 90%; }
#editProfileModal #epViewPersona > div > div { min-width: 0; }

.onb-btn {
  background: var(--accent); color: var(--on-accent, #07090E); border: none; font-weight: 800;
  padding: 13px 18px; border-radius: 12px; cursor: pointer; font-size: 14px;
  text-transform: uppercase; letter-spacing: 0.05em; width: 100%;
  box-shadow: 0 4px 16px rgba(var(--accent-rgb), 0.35); transition: all 0.2s ease;
}
.onb-btn:hover { transform: translateY(-2px); box-shadow: 0 6px 20px rgba(var(--accent-rgb), 0.5); }
.onb-btn.secondary { background: var(--surface-border); color: var(--text); border: 1px solid var(--card-brd); box-shadow: none; }
.onb-cat-row { display: flex; gap: 10px; margin-bottom: 10px; align-items: center; }

.attack-room-btn {
  background: rgba(255,84,112,0.12); border: 1px solid rgba(255,84,112,0.4);
  color: #FF5470; padding: 26px; border-radius: 18px; font-size: 24px; font-weight: 800;
  cursor: pointer; display: flex; flex-direction: column; align-items: center; gap: 10px;
  transition: all 0.25s ease; width: 100%; margin-bottom: 28px;
  box-shadow: 0 8px 24px rgba(255,84,112,0.15);
}
.attack-room-btn:hover { background: rgba(255,84,112,0.22); transform: translateY(-3px); box-shadow: 0 12px 32px rgba(255,84,112,0.3); }

.q-field { margin-bottom:16px; }
.q-field label { display:block; font-size:13.5px; color:var(--text-dim); margin-bottom:8px; font-weight:600; }
.q-field label .num { color:var(--accent); font-family:'Space Grotesk',monospace; margin-right:8px; font-weight:800; }
.radio-row, .segmented-control {
  display: flex;
  gap: 4px;
  background: rgba(0, 0, 0, 0.5);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 10px;
  padding: 4px;
  box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.5);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}
.radio-opt {
  flex: 1;
  text-align: center;
  padding: 7px 10px;
  border-radius: 7px;
  cursor: pointer;
  font-family: 'Space Grotesk', sans-serif;
  font-size: 12px;
  font-weight: 700;
  color: var(--text-dim);
  background: transparent;
  border: none;
  transition: all 0.22s cubic-bezier(0.16, 1, 0.3, 1);
  user-select: none;
  outline: none;
  display: flex;
  align-items: center;
  justify-content: center;
}
.radio-opt:hover:not(.sel) {
  color: var(--text);
  background: rgba(255, 255, 255, 0.08);
}
.radio-opt.sel {
  border-color: transparent;
  background: var(--accent);
  color: var(--on-accent, #050B14) !important;
  font-weight: 900;
  box-shadow: none !important;
}

.onb-textarea {  width:100%; background:rgba(15,19,28,.8); border:1px solid var(--card-brd); color:var(--text); padding:13px; border-radius:12px; font-size:14px; margin-bottom:14px; resize:vertical; font-family:'Inter', sans-serif;  overflow-y:hidden; transition:height 0.1s ease; }
.onb-textarea:focus, .onb-input:focus {
  outline: none !important;
  border-color: var(--accent) !important;
  box-shadow: none !important;
}

.lb-row { display:flex; align-items:center; gap:14px; padding:18px; border:1px solid var(--card-brd); border-radius:14px; margin-bottom:10px; background:rgba(255,255,255,0.025); transition:all .2s; }
.lb-row:hover { background:rgba(255,255,255,0.05); transform:translateX(3px); }
.lb-rank { font-family:'Space Grotesk',monospace; color:#00E5FF; font-weight:800; width:34px; text-align:center; font-size:20px; }
.lb-name { flex:1; font-size:15.5px; font-weight:600; }
.lb-stats { text-align:right; font-family:'Space Grotesk',monospace; font-size:14.5px; color:var(--text-dim); }
.lb-stats .streak { color:#FFB224; font-weight:800; }

/* ---------- TYPOGRAPHY OVERHAUL ---------- */
:not(#page-today).page .card,
:not(#page-today).page p,
:not(#page-today).page input,
:not(#page-today).page select,
:not(#page-today).page textarea,
:not(#page-today).page button,
:not(#page-today).page .mini-task {
    font-size: 15px;
}
:not(#page-today).page .section-title {
    font-size: 14px;
    letter-spacing: 0.06em;
}
:not(#page-today).page .page-hero-text p {
    font-size: 16px;
}

/* ---------- RESPONSIVENESS ---------- */
/* ---- Bina space ka lamba text ----
   Ek naam jismein 500 akshar hon aur ek bhi space na ho, wo apne box se baahar
   nikal jaata tha. Naapa gaya: task row 345px chaudi, uska content 4132px —
   yaani us row ke delete aur edit button pahunch se hi bahar chale jaate the.
   Document horizontally scroll nahi hota (upar kahin clip ho jaata hai), isliye
   ye aankhon se dikhta nahi tha; row bas istemal ke layak nahi rehti thi.

   "anywhere" chuna hai, "break-all" nahi: ye sirf tab todta hai jab shabd sach
   mein fit nahi ho raha. Aur "break-word" isliye nahi ki wo min-content ki
   ginti mein apne break ko nahi maanta, to flex row uske saath bhi chaudi hi
   reh jaati — "anywhere" box ko sikudne deta hai. */
   Sirf phone tier par. Pehli koshish har viewport par thi aur usne desktop ka
   money page 1201px se 1240px lamba kar diya — table cells alag jagah wrap hone
   lage. Naapi hui dikkat 375px ki thi; desktop ke paas jagah hai, aur uska
   baseline waisa hi rehna chahiye. */
@media (max-width: 640px) {
  .task-row, .task-row *,
  .tx-table td, .tx-table th,
  #journalList, #journalList *,
  #purposeList, #purposeList *,
  #problemCaseList, #problemCaseList * { overflow-wrap: anywhere; }
}

/* ---- History window Load More ---- */
.lp-load-more {
  display: block; width: 100%; text-align: center; margin-top: 10px;
  padding: 12px; background: var(--surface-bg); border: 1px dashed var(--surface-border);
  border-radius: 12px; font-size: 13px; color: var(--text-dim); cursor: pointer;
  transition: 0.2s ease;
}
.lp-load-more:hover { background: var(--card-hover); color: var(--text); border-style: solid; }
.lp-load-more-n {
  display: inline-block; margin-left: 8px; padding: 2px 6px;
  background: rgba(255,255,255,0.05); border-radius: 6px; font-size: 11px;
}
.light-theme .lp-load-more-n { background: rgba(0,0,0,0.05); }
/* Phone par har tappable cheez 44px maangi jaati hai; 12px padding se ye
   43.4px par ruk jaata tha. */
@media (max-width: 640px) { .lp-load-more { min-height: 44px; } }
/* Table hi asli standings hai; card list sirf phone tier par khulti hai. */
#communityRankCards { display: none; }

@media (max-width: 1024px) {
  .bento { display: flex; flex-direction: column; gap: 14px; }
  .s1, .s2, .s3, .s4, .s5, .s6, .s7, .s8, .s9, .s12 { grid-column: span 12; width: 100%; }
  .topbar2 { flex-wrap: wrap; gap: 12px; justify-content: center; height: auto; padding: 14px; }
  .page-switcher { position: relative; margin: 0 auto; }
  .ps-menu { width: 100%; left: 0; right: 0; top: 100%; position: absolute; }
  .stat-card .num { font-size: 24px; }
  .sheet-tabs-list { flex-wrap: wrap; }
  .form-grid { grid-template-columns: 1fr; }
  
  /* Additional UI scaling & Responsive Layout Fixes */
  .hub-top { display: flex; flex-direction: column; gap: 20px; align-items: center; }
  .hub-top-left { width: 100%; padding: 0; margin: 0 auto; align-items: center; }
  .hub-top-right { width: 100%; padding-left: 0; }
  .rings-flower { justify-content: center; }
  
  .hub-table-wrap { overflow-x: auto; width: 100%; }
  table.hub-table { min-width: 700px; }
  
  .page-hero { flex-direction: column; text-align: center; }
  .page-hero .badge { margin: 12px auto 0; }
}

/* ---------- LEADERBOARD TABS ---------- */
.leaderboard-tab-bar {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px;
  background: rgba(15, 20, 32, 0.85);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 14px;
  margin-bottom: 24px;
  backdrop-filter: blur(24px);
  -webkit-backdrop-filter: blur(24px);
  box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.08), 0 8px 32px rgba(0, 0, 0, 0.4);
}
.lb-tab-btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 9px 20px;
  font-family: 'Space Grotesk', sans-serif;
  font-size: 13px;
  font-weight: 700;
  color: var(--text-dim);
  background: transparent;
  border: 1px solid transparent;
  border-radius: 10px;
  cursor: pointer;
  outline: none;
  appearance: none;
  -webkit-appearance: none;
  transition: all 0.22s cubic-bezier(0.16, 1, 0.3, 1);
  white-space: nowrap;
  user-select: none;
}
.lb-tab-btn:hover {
  color: var(--text);
  background: rgba(255, 255, 255, 0.07);
}
.lb-tab-btn.active {
  background: linear-gradient(135deg, var(--accent), rgba(var(--accent-rgb), 0.82)) !important;
  color: var(--on-accent, #07090E) !important;
  border-color: rgba(var(--accent-rgb), 0.6) !important;
  box-shadow: 0 4px 20px rgba(var(--accent-rgb), 0.45), inset 0 1px 0 rgba(255, 255, 255, 0.3);
  transform: translateY(0);
}
.lb-coming-soon-badge {
  font-size: 9.5px;
  font-weight: 800;
  padding: 3px 8px;
  border-radius: 6px;
  background: rgba(255, 178, 36, 0.18);
  color: var(--amber);
  border: 1px solid rgba(255, 178, 36, 0.4);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-left: 4px;
  transition: all 0.2s ease;
}
.lb-tab-btn.active .lb-coming-soon-badge {
  background: rgba(7, 9, 14, 0.85);
  color: var(--amber);
  border-color: rgba(255, 178, 36, 0.6);
}
.lb-live-dot {
  font-size: 9.5px;
  font-weight: 800;
  padding: 2px 7px;
  border-radius: 6px;
  background: rgba(16, 185, 129, 0.18);
  color: var(--lime);
  border: 1px solid rgba(16, 185, 129, 0.4);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-left: 4px;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  animation: livePulse 2s infinite ease-in-out;
}
.lb-tab-btn.active .lb-live-dot {
  background: rgba(7, 9, 14, 0.85);
  color: var(--lime);
  border-color: rgba(16, 185, 129, 0.6);
}
@keyframes livePulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.7; transform: scale(0.96); }
}

/* ---------- FAB ---------- */
.fab, .fab-btn {
  position: fixed;
  bottom: 58px;
  right: 28px;
  z-index: 250;
  width: 58px;
  height: 58px;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--accent), rgba(var(--accent-rgb), 0.8));
  color: var(--on-accent, #07090E);
  border: none;
  font-size: 28px;
  font-weight: 800;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: none;
  transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
.fab:hover, .fab-btn:hover {
  transform: scale(1.12);
  box-shadow: none;
}
.fab:active, .fab-btn:active {
  transform: scale(0.95);
}
.fab-container #mainFabBtn, #mainFabBtn {
  position: relative !important;
  right: auto !important;
  bottom: auto !important;
}
.fab-container {
  position: fixed;
  bottom: 58px;
  right: 28px;
  z-index: 250;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 12px;
}
.fab-menu {
  display: flex;
  flex-direction: column;
  gap: 10px;
  opacity: 0;
  transform: translateY(20px) scale(0.9);
  pointer-events: none;
  transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
.fab-menu.open {
  opacity: 1;
  transform: translateY(0) scale(1);
  pointer-events: auto;
}
.fab-item {
  background: rgba(12, 15, 22, 0.94);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border: 1px solid var(--card-brd);
  color: var(--text);
  padding: 11px 18px;
  border-radius: 14px;
  font-family: 'Inter', sans-serif;
  font-size: 13.5px;
  font-weight: 600;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 10px;
  transition: all 0.2s;
  box-shadow: 0 8px 24px rgba(0,0,0,0.5);
}
.fab-item:hover {
  background: rgba(255, 255, 255, 0.1);
  border-color: rgba(var(--accent-rgb), 0.4);
  transform: translateX(-3px);
}
.fab-btn.open, .fab.open {
  transform: rotate(45deg);
}


/* Tactile Button Feedback */
button, .onb-btn { transition: transform 0.1s cubic-bezier(0.16,1,0.3,1), background 0.2s, box-shadow 0.2s; }
button:active:not(:disabled), .onb-btn:active:not(:disabled) { transform: scale(0.94) !important; }

@keyframes slideUpFade {
  0% { transform: translateY(20px) scale(0.95); }
  100% { transform: translateY(0) scale(1); }
}

/* Targeted Light Mode Overrides */

body.light-theme .task-filter-pill.active {
  background: var(--accent) !important;
  color: var(--on-accent, #000000) !important;
  font-weight: 800 !important;
}
body.light-theme .task-filter-pill:not(.active) {
  background: transparent !important;
  color: #475569 !important;
}

html.light-theme, body.light-theme { min-height: 100vh; background: #F5F7FA !important; }

body.light-theme .card {
  background: #ffffff !important;
  border: 1px solid rgba(0, 0, 0, 0.12) !important;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05), 0 1px 3px rgba(0, 0, 0, 0.03) !important;
}

body.light-theme .segmented-control,
body.light-theme .pomo-tabs {
  background: rgba(0, 0, 0, 0.04) !important;
  border-color: rgba(0, 0, 0, 0.08) !important;
}

body.light-theme .segmented-control button:not(.active):not(.sel),
body.light-theme .radio-opt:not(.sel),
body.light-theme .journal-template-pill:not(.active),
body.light-theme .pomo-tab:not(.active) {
  color: #475569 !important;
}

body.light-theme .radio-opt.sel {
  background: var(--accent) !important;
  color: var(--on-accent, #000000) !important;
  font-weight: 800 !important;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1) !important;
}

body.light-theme select,
body.light-theme textarea,
body.light-theme input:not([type="button"]):not([type="submit"]):not([type="checkbox"]),
body.light-theme .onb-input,
body.light-theme .onb-textarea {
  background: #FFFFFF !important;
  border-color: rgba(0, 0, 0, 0.12) !important;
  color: #0F172A !important;
}

/* Dark Mode Specific Overrides for Problems Page */
#page-problems .p-short { display: none; }

body:not(.light-theme) #page-problems .q-field {
  background: rgba(255, 255, 255, 0.03) !important;
  border: 1px solid rgba(255, 255, 255, 0.18) !important;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2) !important;
}

body:not(.light-theme) #page-problems #problemCaseList > div {
  background: rgba(255, 255, 255, 0.03) !important;
  border: 1px solid rgba(255, 255, 255, 0.18) !important;
}

body:not(.light-theme) #page-problems #problemCaseList > div:hover {
  border-color: rgba(255, 62, 160, 0.45) !important;
}

body:not(.light-theme) #page-problems .segmented-control {
  border: 1px solid rgba(255, 255, 255, 0.18) !important;
}

body:not(.light-theme) #page-problems input,
body:not(.light-theme) #page-problems textarea,
body:not(.light-theme) #page-problems select {
  border: 1px solid rgba(255, 255, 255, 0.18) !important;
}


/* ROOT CAUSE FIX: Viewport Canvas Bleed Prevention */
html {
  background: var(--bg) !important;
}
html, body {
  min-height: 100vh;
  margin: 0;
}
body {
  background: var(--bg) !important;
}
html.light-theme {
  background: #F5F7FA !important;
}
body.light-theme .modal-content,
body.light-theme .modal-overlay .card,
body.light-theme #manageCategoriesModal .modal-content {
  background: #ffffff !important;
  color: #0f172a !important;
  border: 1px solid rgba(0,0,0,0.12) !important;
  box-shadow: 0 20px 50px rgba(15,23,42,0.15) !important;
}

/* The purpose-level (queued/active/…) dialog is a bare #id div, not a
   .modal-overlay, so the rule above missed it: its card kept a hardcoded dark
   gradient in light mode while its text used var(--text) (dark), which then
   vanished on the dark card. In light mode the card goes white, and the stage
   rows take a visible border so none of them read as an empty box. */
body.light-theme #purposeLevelModal > div {
  background: #ffffff !important;
  border: 1px solid rgba(0,0,0,0.12) !important;
  box-shadow: 0 20px 50px rgba(15,23,42,0.15) !important;
}
body.light-theme #purposeLevelModal .purpose-stage-card {
  border-color: rgba(0,0,0,0.14) !important;
}
body.light-theme #purposeLevelModal .purpose-stage-card:first-child {
  background: #f1f5f9 !important;
}

/* ---------- loading screen (was inline inside #globalLoader) ---------- */
    @keyframes loaderPulse {
      0% { transform: scale(0.95); opacity: 0.8; }
      50% { transform: scale(1.05); opacity: 1; box-shadow: 0 0 40px rgba(0, 229, 255, 0.4); }
      100% { transform: scale(0.95); opacity: 0.8; }
    }
    @keyframes loaderBar {
      0% { transform: translateX(-100%); }
      100% { transform: translateX(200%); }
    }
    .gl-logo {
      width: 100px;
      height: 100px;
      border-radius: 24px;
      object-fit: cover;
      margin-bottom: 32px;
      animation: loaderPulse 2s infinite ease-in-out;
      border: 1px solid var(--inner-box-border);
    }
    .gl-skeleton-track {
      width: 180px;
      height: 4px;
      background: var(--inner-box-border);
      border-radius: 4px;
      overflow: hidden;
      position: relative;
    }
    .gl-skeleton-bar {
      position: absolute;
      top: 0; left: 0; bottom: 0;
      width: 50%;
      background: linear-gradient(90deg, transparent, var(--accent), transparent);
      animation: loaderBar 1.5s infinite linear;
    }

/* ==========================================================================
   RESPONSIVE FOUNDATION                                            (Phase 8)

   Three tiers, and only three:

     phone     <= 640px
     tablet    641px - 1024px
     desktop   > 1024px

   Everything above this block is the desktop design. It does not get edited
   for mobile — mobile rules live down here, inside a media query, so desktop
   cannot change by accident. If a stubborn inline style ever blocks a rule,
   an !important declaration here does beat a normal inline one (measured),
   but that is a last resort, not the usual tool.

   Two things are deliberately NOT here:

   - Touch-target sizing. Measured at 375px, the undersized controls are 341
     habit checkboxes at 16px — and 44px each would make that grid 1364px
     wide — plus the top bar, which Phase 9 replaces outright. Sizing either
     now would be thrown away. Phase 9 and Phase 10.2 own those components.

   - .main2's overflow-x. Flipping it from auto to visible is the plan's 8.3,
     but content still reaches 420px at a 390px viewport. Combined with
     body{overflow-x:hidden} that would clip the excess rather than let it
     scroll, and unreachable is worse than scrollable. It flips once phases 9
     and 10 have removed the overflow itself.
   ========================================================================== */

:root {
  /* 0px fallbacks so these stay usable in calc() where there is no notch. */
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  --pad-page: 24px;
}

@media (max-width: 640px) {
  :root { --pad-page: 14px; }

  /* The page itself, plain white and plain black.

     The desktop palette sits its cards on a tinted ground — #F5F7FA in light and
     #07090E in dark — which works when the cards are the surface and the ground
     is only ever seen between them. On a phone the frames are gone and that
     ground IS the page, so the tint reads as a grey wash over everything.
     Phone tier only; the desktop keeps its palette. */
  :root { --bg: #000000; }
  body.light-theme { --bg: #FFFFFF; }
  /* The light ground is painted on <html>, not <body> — body is
     `background: transparent !important` and html.light-theme carries the colour
     with !important of its own, so the token alone does not reach it. */
  html.light-theme,
  html.light-theme body.light-theme { background: #FFFFFF !important; }

  /* --inner-box-border is defined in the dark :root as
     `--inner-box-border: var(--inner-box-border)` — a custom property whose value
     is itself. That is a cycle, and a cycle makes the property invalid at
     computed-value time, so in dark mode it has no value at all and every
     `border: 1px solid var(--inner-box-border)` simply does not draw. That is 124
     places across the three files, and it is why the task rows had no edges in
     the dark theme and could not be told apart. Light mode was fine because
     line 125 gives the same name a real colour.

     Given a value here, scoped to the phone, because the same broken line is
     feeding the desktop and desktop is not being touched yet. Two more in that
     block have the same shape — --inner-box-bg and --inner-box-bg-light — and
     are left alone deliberately: they are backgrounds, nothing is reported wrong
     with them, and giving them values would change surfaces nobody asked about. */
  :root { --inner-box-border: rgba(255, 255, 255, 0.12); }

  /* dvh follows the browser chrome as it hides and shows; vh is the largest
     viewport, which leaves a phantom scroll on a short page. */
  html, body { min-height: 100dvh; }

  .main2 { padding: 12px var(--pad-page) calc(72px + var(--safe-bottom)); }

  /* iOS zooms the whole page when a focused field is under 16px, and all 86
     text inputs here were. Only the text scales — the padding around it is
     untouched, so controls keep their proportions.

     !important is earned here, not lazy: without it this reached 16 of the 86.
     34 fields carry an inline font-size, which no normal rule can outrank, and
     36 more are set by desktop rules with heavier selectors. This is the escape
     hatch described at the top of this block, and it is scoped to the phone
     tier and to one property.

     The `:root body` prefix is not decoration either. `.htable select` and
     `select.onb-input` already set this !important at specificity (0,1,1); a
     plain `select` is (0,0,1) and loses even though it comes later. The prefix
     lifts this to (0,1,2) so it wins outright rather than by source order,
     which would break the moment anything is appended below. */
  :root body input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]),
  :root body select,
  :root body textarea { font-size: 16px !important; }

  /* Larger text widened fields enough to push the page from 420px to 436px at a
     390px viewport. A field should never be the thing that makes the page wider
     than the screen; box-sizing keeps the padding inside the cap. */
  :root body input,
  :root body select,
  :root body textarea {
    max-width: 100%;
    box-sizing: border-box;
  }

  /* The 1024px block already wraps .topbar2 itself, but its three inner groups
     are flex rows that never wrap, and the widest is 482px at a 390px screen —
     the single reason content still runs off the edge. The 16px field rule above
     widened the month select and made it worse, so this pays that back.
     Phase 9 replaces this bar outright; until then, wrapping beats clipping. */
  .topbar2 > div { flex-wrap: wrap; }

  /* Two decorative radial gradients, 650px and 700px, permanently composited.
     Phone GPUs are weaker than the desktop ones where this project's banding
     artifacts already appeared, and neither blob is load-bearing. */
  body::before, body::after { display: none; }

  /* A blur across a large surface is the exact pattern every ghost-box here
     came from. Phone gets the opaque background token instead. */
  .topbar2, .top-tabs {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    background: var(--bg);
  }

  /* The bar and the page were the same flat colour with nothing between them, so
     the header read as part of whatever it was sitting above. One line. */
  .topbar2 {
    border-bottom: 1px solid var(--card-brd);
    /* Line, not shadow. The light theme puts a 0 1px 4px under this bar; a
       shadow says 'floating' and all this has to say is 'the page starts here'. */
    box-shadow: none !important;
  }
}

/* Someone who asks the OS to reduce motion means it. Entrance animations only
   move transform now, so cutting the duration lands them on their final state
   immediately rather than hiding anything. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* ==========================================================================
   MOBILE SHELL                                                     (Phase 9)

   Hidden entirely above 640px, so the desktop shell is untouched — the pixel
   baseline stays at 0.000% drift through this phase.

   Why a bottom bar at all: the ten-tab strip at the top is a scroller, so
   reaching Settings meant swiping a 40px-tall row near the top of the screen.
   The four destinations people open daily now sit under the thumb, and the
   rest live one tap away in a sheet.
   ========================================================================== */

.bottom-nav { display: none; }
.sheet, .sheet-scrim { display: none; }

@media (max-width: 640px) {
  :root { --bottom-nav-h: 56px; }

  .bottom-nav {
    display: flex;
    position: fixed;
    left: 0; right: 0; bottom: 0;
    z-index: 80;
    background: var(--bg);           /* opaque, per the token rule above */
    border-top: 1px solid var(--card-brd);
    padding-bottom: var(--safe-bottom);
  }

  .bottom-tab {
    flex: 1 1 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    min-height: var(--bottom-nav-h);   /* comfortably past the 44px minimum */
    padding: 6px 2px;
    border: none;
    background: none;
    color: var(--text-dim);
    font-family: inherit;
    font-size: 10.5px;
    font-weight: 700;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
  }
  .bottom-tab .bt-icon { font-size: 19px; line-height: 1; }
  .bottom-tab.active { color: var(--accent); }
  /* Colour alone should not be the only signal. */
  .bottom-tab.active .bt-icon { transform: translateY(-1px); }
  .bottom-tab.active .bt-label { text-decoration: underline; text-underline-offset: 3px; }

  /* Clear the fixed bar, plus the notch inset underneath it. */
  .main2 { padding-bottom: calc(var(--bottom-nav-h) + var(--safe-bottom) + 16px); }

  /* The top bar keeps what identifies the page and drops what does not: the XP
     readout and the page subtitle are both glanceable extras that cost a whole
     row each on a 390px screen. Nothing here is a control, so nothing becomes
     unreachable — the level and XP still show on Today. */
  .xp-bar-container { display: none !important; }
  #headerPageSubTitle { display: none !important; }

  /* ---- phone top bar: identity only ----

     [logo] Page name .......................... [avatar]

     Everything else that used to sit up here is a control, and a control that
     vanishes is a feature that vanishes — so both moved rather than being
     dropped: the month selector to Settings, where a card now carries it, and
     the theme toggle to the Display Mode row Settings already had. What stays
     is what tells you where you are, plus one tap to your account.

     The 1024px tier wraps this bar and lets it grow to fit; that is what made
     it three rows and ~140px tall on a phone. Here it is one row, 56px, and
     the name truncates instead of wrapping. */
  .topbar2 {
    flex-wrap: nowrap;
    justify-content: space-between;
    gap: 10px;
    /* The inset is added to both the height and the top padding, and
       box-sizing is border-box, so the row the user sees stays exactly 56px —
       the extra is the strip under the status bar, which this bar now paints
       itself. It has to, because the alternative is the system painting it
       from the manifest's single hardcoded colour, which cannot follow a theme
       the user switches at runtime: that is why the bar stayed black in light
       mode. --safe-top was declared at the top of this file and then used
       nowhere. Off a notched, edge-to-edge screen the inset is 0 and none of
       this changes anything. */
    height: calc(56px + var(--safe-top));
    min-height: calc(56px + var(--safe-top));
    padding: var(--safe-top) var(--pad-page) 0;
  }

  .topbar2 > div { flex-wrap: nowrap; }        /* undoes the stopgap above */

  .tb-left { gap: 10px !important; flex: 0 0 auto; }

  /* The wordmark reads 'LifePlaner · Desktop System', which is desktop wording on
     a phone, and the mark alone identifies the app. Dropping it gives the page
     name the room it needs. */
  /* Replace logo with drawer menu icon on mobile but keep structural DOM for tests */
  .logo .mark {
    content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24' fill='none' stroke='%23F8FAFC' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='3' y1='12' x2='21' y2='12'%3E%3C/line%3E%3Cline x1='3' y1='6' x2='21' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='18' x2='21' y2='18'%3E%3C/line%3E%3C/svg%3E");
    width: 24px;
    height: 24px;
    border-radius: 0;
    margin-right: -4px;
  }
  
  body.light-theme .logo .mark {
    content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24' fill='none' stroke='%230F172A' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='3' y1='12' x2='21' y2='12'%3E%3C/line%3E%3Cline x1='3' y1='6' x2='21' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='18' x2='21' y2='18'%3E%3C/line%3E%3C/svg%3E");
  }

  .logo { gap: 4px; }
  .logo .word { display: none; }

  /* The middle group is the page name now: left-aligned, right of the mark, and
     free to take the space between the mark and the avatar. */
  #headerPageTitle {
    flex: 1 1 auto;
    min-width: 0;
    align-items: flex-start !important;
    text-align: left !important;
  }
  #headerPageTitle > div {
    max-width: 100%;
    font-size: 16px !important;
    gap: 0 !important;
  }
  #headerPageIcon, #headerPageMainTitle { display: none; }
  #headerPageShortTitle {
    display: block;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  #headerThemeModeBtn { display: none !important; }

  /* Two System Guide buttons were showing side by side in the phone header, and
     both opened openPageGuideModal(). #pageGuideBtn is the desktop bar's quick
     icon; #headerGuideBtn is the phone's own 44px target, added later next to
     the profile and hidden off the phone. The half that was never meant to be
     here is this one — the desktop bar keeps it, untouched. */
  #pageGuideBtn { display: none !important; }

  /* The month picker is hidden by visibility, not display, and that is a
     measured choice rather than a stylistic one.

     display:none on this element — a native <select> — reproducibly changed the
     DESKTOP capture: light-habits differed by 4796 pixels every run, while the
     rule itself cannot apply at 1280px. Checked before believing it: every rect
     on that page at 1280 is identical to HEAD's (179 elements, only the two new
     class names differ), and the diff has no vertical offset (scoring the band
     at dy -3..+3 puts dy=0 five times ahead). What moved was glyph
     antialiasing, nothing else — the rasterisation drift this project has
     documented twice, here with a reproducible trigger. Hiding the button next
     to it the same way is clean; only the select does it.

     This hides it just as completely: absolutely positioned at zero size, so it
     takes no space, and visibility:hidden takes it out of the tab order too, so
     nothing is reachable that cannot be seen. The control itself is not lost —
     Settings carries it on this tier. */
  .month-navigator {
    visibility: hidden;
    position: absolute;
    width: 0;
    height: 0;
    overflow: hidden;
  }

  /* Icon only, and a full 44px target — it is the one tap out of every page. */
  #topbarCloudUserBtn {
    flex: 0 0 auto;
    gap: 0;
    width: 44px;
    height: 44px;
    padding: 0;
    border-radius: 50%;
    justify-content: center;
  }
  #topbarCloudUserBtn img { width: 26px !important; height: 26px !important; }
  #topbarUserLabel, #topbarSubBadge { display: none !important; }

  /* Page-guide button: a 44px icon target sitting just left of the profile. */
  #headerGuideBtn {
    display: flex;
    flex: 0 0 auto;
    width: 44px;
    height: 44px;
    border-radius: 50%;
  }
  .mobile-only { display: block; }

  /* The Workspace Page Modules & Visibility Manager is desktop machinery.
     It hides .top-tab elements and the quick launcher's buttons; the phone
     navigates from .bottom-tab and the More sheet, which it does not touch
     (applyPageVisibility() reaches for #mn_<page> ids that are not in this
     document). So on a phone every toggle in it appeared to do nothing.
     Hidden rather than fixed: the owner's call is that the phone's five-tab
     bar is not something to make configurable. */
  #workspaceVisibilityCard { display: none !important; }


  /* The ten-tab strip is redundant once the bottom bar exists. */
  .top-tabs { display: none !important; }

  /* ---- bottom sheet ---- */
  .sheet-scrim {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 299;
    background: rgba(0, 0, 0, 0.55);
  }
  .sheet {
    display: flex;
    flex-direction: column;
    position: fixed;
    left: 0; top: 0;
    height: 100%;
    width: min(86%, 340px);
    z-index: 300;
    overflow-y: auto;
    background: var(--glass-bg-card);
    border-right: 1px solid var(--card-brd);
    border-radius: 0;
    padding: calc(18px + var(--safe-top)) 0 calc(18px + var(--safe-bottom));
    box-shadow: 12px 0 40px rgba(0, 0, 0, 0.5);
    transform: translateX(-100%);
    transition: transform 0.3s cubic-bezier(0.1, 0.9, 0.2, 1);
  }
  .sheet:not([hidden]) { transform: translateX(0); }
  .sheet[hidden], .sheet-scrim[hidden] { display: none !important; }

  .sheet-grip {
    display: none;
  }
  #drawerTriggerBtn { margin-right: 10px !important; }

  .sheet-row {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    min-height: 52px;
    padding: 10px 12px;
    border: none;
    border-radius: 12px;
    background: none;
    color: var(--text);
    font-family: inherit;
    font-size: 13.2px;
    font-weight: 700;
    text-align: left;
    cursor: pointer;
  }
  .sheet-row .sr-icon { font-size: 20px; width: 26px; text-align: center; }
  .sheet-row .sr-desc {
    display: block;
    font-size: 11.5px;
    font-weight: 500;
    color: var(--text-dim);
    margin-top: 2px;
  }
  .sheet-row.active { background: rgba(var(--accent-rgb), .12); color: var(--accent); }

  /* ---- existing modals become sheets ----
     Every one of these is a centred card on a fixed overlay. On a phone that
     leaves it floating mid-screen with its actions out of thumb reach; the
     same card docked to the bottom reads and reaches better. Only the phone
     tier is affected, so the desktop dialogs are unchanged. */
  #editProfileModal > .card,
  #manageCategoriesModal > .card,
  #customConfirmModal > .card,
  #defaultHabitsModal > .card,
  #challengeModal > .card,
  #pageGuideModal > .card,
  #fabModal > .card,
  #purposeLevelModal > div {
    max-width: 100% !important;
    width: 100% !important;
    /* margin-top:auto docks it; margin-bottom must be zeroed too, or a card
       carrying its own bottom margin floats a few pixels clear of the edge and
       the sheet reads as a card that failed to land. */
    margin-top: auto !important;
    margin-bottom: 0 !important;
    max-height: 85dvh !important;
    border-radius: 20px 20px 0 0 !important;
    padding-bottom: calc(20px + var(--safe-bottom)) !important;
  }
  #editProfileModal, #manageCategoriesModal, #customConfirmModal,
  #defaultHabitsModal, #challengeModal, #pageGuideModal,
  #fabModal, #purposeLevelModal {
    align-items: flex-end !important;
    padding: 0 !important;
  }
}

/* ==========================================================================
   TODAY PAGE — phone interior                                   (Phase 10.1)

   The page already fits 390px (Phase 8) and is reachable (Phase 9). What was
   left is that it fits badly: measured at 390px, two of the three hub blocks
   were still pinned to their desktop widths and sat as narrow strips in the
   middle of the screen, and the habit table gave half its visible width to a
   column of names.
   ========================================================================== */

@media (max-width: 640px) {

  /* The 1024px tier already sets width:100% on both of these, but the desktop
     rule's max-width (180px / 260px) has equal specificity and comes first, so
     the cap survived and the width never took effect. Measured at a 390px
     viewport: the stats card rendered 180px inside a 362px column, the rings
     260px. Clearing the caps is what actually widens them. */
  .hub-top-left, .hub-top-right { min-width: 0; max-width: 100%; }

  /* Two fixed columns of 124px left the rings small with the card now full
     width; auto-fit spends whatever is there — two ~175px rings at 390px, and
     it degrades to one column on a 320px screen instead of overflowing. */
  .rings-flower-sm { grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); }

  /* The month grid is a legitimate horizontal scroller (the plan's rule: data
     tables scroll, pages do not). Two things were wrong with it anyway.

     First, min-width:700px is not a phone concession — it is far too narrow for
     what this table holds. Measured at 390px: 37 columns share those 700px, and
     after the name column, two spacers and the three summary columns take their
     fixed widths, each of the 31 day columns is left 6.6px. The checkbox inside
     one is 16px, so the boxes overlapped each other and the day numbers ran
     together into an unreadable stripe. 1180px is the width at which those 31
     columns come out at 24px each — tappable, and the digits fit.

     That is wider in absolute terms, but a scroller's width is not what costs
     the user anything; being unable to read or hit a cell is.

     Second, the name column is sticky, so it is subtracted from the visible
     width at every scroll position — 180px of the 361px on screen. 120px still
     fits the longest habit name at 13px, and ellipsis handles the rest.

     !important is required and not laziness: renderHubTable writes
     style="width:180px" onto these cells, and no selector outranks an inline
     declaration. Note that only `width` does anything here — min-width and
     max-width do not apply to table cells under table-layout:fixed, which is
     also why the inline 180px caps below are not in the way. */
  table.hub-table { min-width: 1180px; }
  table.hub-table .hub-name-h,
  table.hub-table td.hub-name {
    width: 120px !important;
    font-size: 13px;
    padding-left: 8px; padding-right: 6px;
    overflow: hidden; text-overflow: ellipsis;
  }
  .hub-name-ic { margin-right: 5px; font-size: 14px; }

  /* ---- the frozen habit column, finished ----

     It was half built, and the half that was missing is what you saw: the body
     cells were sticky, the two header cells of the same column were not.
     Measured at 390px after scrolling the grid 300px right — body cell left:
     15px (frozen), header cell left: -285px (gone). So the day numbers slid
     through the space above the names and the names looked like they were
     floating on top of the grid.

     Four things, and all four are needed:
       1. the header cells stick too, at the same left:0
       2. the background goes fully opaque — it was .98, and 2% is enough for a
          24px column of digits to ghost through behind the names
       3. an explicit stacking order, so the corner sits above both
       4. an edge, shown only once something is actually scrolled under it

     Phone tier only. The same bug exists on desktop, and it stays there until
     that half of the app is opened up: fixing it here would repaint the desktop
     table and the baseline would — correctly — fail. */
  table.hub-table .hub-name-h,
  table.hub-table td.hub-name {
    position: sticky;
    left: 0;
    /* The page colour, not a panel colour. Point 2 above still holds — var(--bg)
       is fully opaque, so nothing ghosts through — and the frozen column now
       sits flush on the page instead of reading as a lighter strip down the
       left of it. Follows the theme in both directions. */
    background: var(--bg);
  }
  table.hub-table td.hub-name  { z-index: 5; }
  table.hub-table .hub-name-h  { z-index: 6; }
  table.hub-table thead th[class*="th-wk-"] { z-index: 4; }

  /* One solid block, not eleven stacked cells. The table draws a hairline under
     every row, which down the frozen column read as gaps in what should be a
     single panel of colour behind the names. */
  table.hub-table tbody tr td.hub-name { border-bottom: none; }

  /* Only once it covers something. At scrollLeft 0 there is nothing underneath
     and a line there would just be a stray rule down the table.

     A 1px edge, not the 8px shadow this started as: the shadow announced itself
     more than the column it was marking. */
  .hub-table-wrap.is-scrolled table.hub-table .hub-name-h,
  .hub-table-wrap.is-scrolled table.hub-table td.hub-name {
    box-shadow: 1px 0 0 var(--card-brd);
  }

  /* ============================ Today = feed ============================

     The page was eighteen bordered surfaces, boxes inside boxes, each one 14px
     short of the screen on both sides. It reads as a feed now: every section
     runs kinare se kinare, nothing is framed, and one 5px grey band separates
     one section from the next — a gap you look past, not a thing you look at.

     Scoped to #page-today. The other nine pages are not being touched yet, and
     the phone tier is the only tier being touched at all.
     ===================================================================== */

  /* Full bleed. .main2 keeps its 14px gutter for every other page; these pull
     back out of it, 362px -> 390px. The gutter comes back on the sections that
     hold text, so nothing but data touches the glass. */
  #page-today > .hub-panel,
  #page-today > #homeTaskBox,
  #page-today > #circularWheelBox {
    margin-left: calc(-1 * var(--pad-page)) !important;
    margin-right: calc(-1 * var(--pad-page)) !important;
    width: auto !important;
    border-radius: 0 !important;
  }

  /* No frames. !important is earned: both of these carry border, background and
     padding as inline style attributes, which no ordinary selector outranks. */
  #page-today .hub-top-left,
  #page-today #homeTaskBox,
  #page-today #circularWheelBox,
  #page-today #circularWheelBox .card {
    background: none !important;
    border: none !important;
    box-shadow: none !important;
    border-radius: 0 !important;
  }
  #page-today #homeTaskBox,
  #page-today #circularWheelBox {
    padding: 18px var(--pad-page) !important;
    margin-top: 0 !important;
  }
  #page-today #circularWheelBox .card { padding: 0 !important; }

  /* The bands, with air on each side so the band is a gap between two sections
     rather than a rule stuck to the top of one. The grid is not in this list:
     with the hero hidden it is the first thing on the page, and a band above it
     would be a line under the header.

     10px, matching Habits and Tasks. Today was built first and kept 5px while
     the two pages after it settled on 10; at a glance the seams on this page
     read as a hairline and theirs as a gap, which is the difference between a
     rule and a break. One number for all three feeds now. */
  #page-today .feed-head--graphs,
  #page-today #homeTaskBox,
  #page-today #circularWheelBox,
  #page-today #circularWheelBox > div:last-child {
    border-top: 10px solid var(--feed-sep) !important;
    margin-top: 10px !important;
    padding-top: 10px !important;
  }

  /* ---- the scrollbar under the month grid ----
     The grid is 921px of content in a 320px window, so the wrap drew an 8px
     horizontal bar across the bottom of it -- a grey line the width of the
     section, sitting between the grid and whatever came next, which is exactly
     what the bands are for.

     The scrolling itself is untouched: scrollWidth, scrollLeft and the
     week-at-a-time snap all work the same, and the grid still opens on the
     current week with part of the next one showing, which is the cue that there
     is more to the side. Both properties are needed -- the standard one and
     WebKit's pseudo-element -- because neither covers every engine on a phone. */
  #page-today .hub-table-wrap { scrollbar-width: none; }
  #page-today .hub-table-wrap::-webkit-scrollbar { display: none; }

  /* ---- an empty day gets a button, not a sentence ----
     The note tells you to click "+ Add Task", which is a control on the Tasks
     page; on this page there was nothing to press. The phone swaps it for the
     button it was describing. */
  #page-today .today-empty-note { display: none; }
  #page-today .today-add-cta {
    display: block;
    width: 100%;
    min-height: 44px;
    margin: 4px 0 2px;
    background: rgba(var(--accent-rgb), .1);
    border: 1px solid var(--accent);
    border-radius: 10px;
    color: var(--accent);
    font-family: 'Space Grotesk', system-ui, sans-serif;
    font-size: 13px;
    font-weight: 800;
    letter-spacing: .01em;
    cursor: pointer;
  }

  /* And the planner button steps aside while it is up. .is-empty-day is put on
     by the render, so this follows the day rather than the page. */
  #page-today #homeTaskPlannerRow.is-empty-day { display: none; }

  /* ---- the hero is hidden ----
     Asked for directly: the day's ring, the Perfect days / Best day / Tasks /
     Habits rows and the four habit rings all go, and the page opens on the month
     grid. Note that those four numbers appear nowhere else on Today — Insights
     is where they live now. */
  #page-today .hub-top-left,
  #page-today .hub-top-right { display: none !important; }
  #page-today #circularWheelBox > div:last-child {
    margin: 18px calc(-1 * var(--pad-page)) 0 !important;
    padding: 18px var(--pad-page) 0 !important;
  }

  /* ---- hero: summary left, its rings right, in one row ----

     The summary used to be three pieces with a chart wedged between them: ring +
     stats, then both graphs, then the four habit rings 216px further down. One
     thing, in three places.

     display:contents dissolves .hub-top so its five children become children of
     the panel itself, and then they can be placed: summary and rings side by
     side, the month grid across the full width under them, the graphs under
     that. Column one takes what the summary needs and column two takes the
     rest — no fixed half-and-half. */
  /* minmax(150px, …) and not plain auto: the summary's widest child is the 86px
     ring, and the stat rows are space-between flex rows with no intrinsic width
     to contribute, so an auto column sized itself to the ring and crushed
     "Perfect days" into 86px. 150px is what the longest stat row needs at 13px;
     the rings take everything left over. */
  /* ---- the feed's order ----

     The sections are not siblings in the markup: the graphs live inside .hub-top
     inside .hub-panel, while the task box and the wheel are children of the page.
     Nothing can be reordered across those boxes — so the boxes are dissolved.
     display:contents on both makes every section a direct child of the page for
     layout, and then `order` puts them in the order asked for:

         grid heading -> grid -> task -> graphs heading -> graphs -> wheel

     The markup does not move, so the desktop layout is untouched. */
  #page-today.active { display: flex; flex-direction: column; }
  #page-today .hub-panel,
  #page-today .hub-top { display: contents; }
  #page-today .hub-top-spacer { display: none; }

  #page-today .feed-head--grid   { order: 1; }
  #page-today .hub-table-wrap    { order: 2; }
  #page-today #homeTaskBox       { order: 3; }
  #page-today .feed-head--graphs { order: 4; }
  #page-today .hub-top-mid       { order: 5; padding: 0 var(--pad-page) 18px !important; }
  #page-today #circularWheelBox  { order: 6; }

  /* .hub-panel has no box any more, so the bleed moves onto each section.

     width:auto matters as much as the margins: both of these carry width:100%
     from the desktop rules, and an explicit width beats a flex item's stretch —
     so the negative margins only slid them left instead of widening them. 365px
     of grid, shifted 14px off the screen. */
  #page-today .feed-head,
  #page-today .hub-table-wrap,
  #page-today .hub-top-mid {
    width: auto !important;
    margin-left: calc(-1 * var(--pad-page));
    margin-right: calc(-1 * var(--pad-page));
  }

  /* Section headings — a feed with no frames still has to say what each part is. */
  #page-today .feed-head {
    display: block;
    background: var(--bg);
    padding: 14px var(--pad-page) 10px;
    font-family: 'Space Grotesk';
    font-size: 13px; font-weight: 800; letter-spacing: .04em;
    text-transform: uppercase; color: var(--text);
  }
  #page-today .feed-sub {
    display: block;
    margin-top: 4px;
    font-family: 'Inter', system-ui, sans-serif;
    font-size: 11.5px; font-weight: 500; letter-spacing: 0;
    text-transform: none; color: var(--text-dim);
  }

  /* The grid's name is the grid's own top row, not a label hovering above it:
     same surface, same left edge, and the box's top border moves up to sit above
     the heading so the two read as one thing. */
  #page-today .feed-head--grid {
    display: flex; align-items: center; gap: 10px;
    background: var(--table-bg);
    border-top: 1px solid var(--card-brd);
    padding: 8px var(--pad-page);
  }
  /* Small and flat, with a caret so it reads as something you can open. The
     caret is a background image because a select cannot hold a child. */
  /* Small, flat, and with a caret in its own ink.

     The caret is a background image because a <select> cannot hold a child
     element, and its colour is written twice — once here, once in the light
     block below — because a data: URI cannot read a CSS variable. Grey #888 was
     the first attempt and it disappeared into both themes.

     No padding on the left: the text was being clipped, and 40% of the row is
     not much to hold "📌 DEFAULT DEMO DATA". */
  /* Small, flat, and with a caret in its own ink.

     Every declaration is !important, and that is not laziness: there is a
     global `select` rule at the top of this file where all of background,
     radius, padding and the caret image are themselves !important. It is what
     was still drawing an opaque box with 38px of padding and a cyan chevron
     here — the box, the clipped text and the invisible icon were all one rule.

     The caret colour is written twice, once per theme, because a data: URI
     cannot read a CSS variable. */
  #page-today .grid-month {
    display: block !important;
    margin-left: auto !important;
    min-height: 30px !important; height: 30px !important;
    width: auto !important; max-width: 44% !important;
    padding: 0 22px 0 10px !important;
    background-color: transparent !important;
    /* no-repeat, explicitly. body.light-theme sets `background: #FFFFFF` — the
       shorthand — which resets background-repeat to `repeat`, and it sits later
       in this file than the `select` rule that says no-repeat. In light mode the
       caret tiled across the whole control. */
    background-repeat: no-repeat !important;
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'><path d='M1 1l4 4 4-4' fill='none' stroke='%23F8FAFC' stroke-width='1.8' stroke-linecap='round'/></svg>") !important;
    background-position: right 10px center !important;
    border: 1px solid var(--card-brd) !important;
    border-radius: 4px !important;
    box-shadow: none !important;
    color: var(--text) !important;
    font-size: 11px !important; font-weight: 700 !important;
    text-transform: uppercase !important; letter-spacing: .02em !important;
  }
  #page-today .grid-month:hover,
  #page-today .grid-month:focus {
    border-color: var(--card-brd) !important;
    box-shadow: none !important;
  }
  body.light-theme #page-today .grid-month {
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'><path d='M1 1l4 4 4-4' fill='none' stroke='%230F172A' stroke-width='1.8' stroke-linecap='round'/></svg>") !important;
  }

  /* The wheel's row lines were rgba(0,0,0,0.02) in light mode — two per cent of
     black, which is nothing. They divide the habit list that runs into the
     wheel, so not seeing them means not seeing which row is which. Matched to
     what the dark theme uses (15% the other way). Phone tier only: the token is
     shared with the desktop tracker, which is not being touched. */
  body.light-theme { --ct-grid: rgba(15,23,42,0.16); }
  #page-today .hub-table-wrap {
    border-top: none !important;
    margin-bottom: 10px;
  }

  /* Nothing between the bar and the grid. .main2 opens with 12px of padding for
     every page; the panel pulls back through it so the first thing under the
     header is the grid's own top edge. */
  #page-today .feed-head--grid { margin-top: -12px; }

  /* The habit's emoji is decoration in a column that is 111px wide and holds a
     name that has to be readable. The name gets the space — and it gets the
     whole name, which is why this swaps in a separately built string rather
     than just hiding the icon span. */
  #page-today .hub-name-ic,
  #page-today .hub-name-d,
  #page-today .hub-h-d { display: none; }
  #page-today .hub-name-m,
  #page-today .hub-h-m { display: inline; }

  /* Two up, two down — the rings sit beside the summary now, not under it. */
  #page-today .rings-flower-sm { grid-template-columns: 1fr 1fr !important; }

  /* The grid keeps its frame; only its width changes. But the drop shadow and
     the right-edge fade were both saying "this box floats above the page", and
     in a feed nothing floats. */
  #page-today .hub-table-wrap {
    box-shadow: none !important;
    -webkit-mask-image: none;
    mask-image: none;
    /* The side borders cost the two pixels that decide whether seven days fit:
       the frame is 390 wide, so its content box was 388, and 388 - 145 is 6.9
       columns. They were also drawing a hairline down the very edge of the
       screen, which a full-bleed section should not do. */
    border-left: none;
    border-right: none;
  }

  /* ==================== the month grid, sized for a phone ====================

     One week fills the screen. 390 = 112 (habit) + 34 (%) + 7 x 34.5, so the
     seven days of the current week are what you see when the page opens, and a
     swipe moves to the next seven rather than leaving half of two weeks on
     screen. The cells are square, which is what makes a column of ticks read as
     a column instead of a stripe.

     Three columns are gone here: Monthly Progress, the streak, and the desktop
     copy of %. All three sat to the right of thirty-one day columns, which on a
     390px screen means nobody has ever seen them. % earns its place back beside
     the names, where it can be read against the row it belongs to. */
  /* The day column is computed, not typed.

     111 + 34 + 7 x 35 = 390 was arithmetic for one phone. On a 393px or 412px
     screen the same numbers leave the seventh day half cut — which is exactly
     what came back from a real handset. The width is now whatever makes seven
     days fill THIS screen, so the sum is right on every one of them.

     The table sizes itself from these columns (width auto under table-layout:
     fixed), so a 28-day month is a 28-day table and no min-width has to be kept
     in step with anything. */
  #page-today .hub-table-wrap {
    /* Fallbacks. positionHubGrid() overwrites all three with whole pixels as
       soon as it runs; these keep the first paint sane. */
    --grid-frozen: 145px;
    --grid-name: 111px;
    --grid-day: calc((100vw - var(--grid-frozen)) / 7);
  }
  /* Stated, not left to auto. Under table-layout:fixed a table with width:auto
     takes its container's width and scales every column to fit — which turned
     35px day cells into 21px ones. The sum is the frozen block plus this month's
     days, and renderHubTable supplies the day count. */
  #page-today table.hub-table {
    width: calc(var(--grid-frozen) + (var(--grid-days, 31) * var(--grid-day))) !important;
    min-width: 0 !important;
  }

  #page-today .hub-prog-h,
  #page-today .hub-prog,
  #page-today .hub-pct-h,
  #page-today .hub-streak-h,
  #page-today td.hub-pct,
  #page-today td.hub-streak,
  #page-today .hub-spacer-h,
  #page-today .hub-spacer-td { display: none !important; }

  #page-today .hub-pct-mh,
  #page-today td.hub-pct-m {
    display: table-cell !important;
    /* 111px: exactly the name column's width. It was 112, and that one pixel
       was the gap between the two columns — a hairline of page colour running
       the full height of the block. */
    position: sticky; left: var(--grid-name); z-index: 5;
    width: 34px !important;
    background: var(--table-sticky-bg-solid);
    text-align: center;
    font-size: 11px; font-weight: 800;
    border-bottom: none;
  }
  #page-today .hub-pct-mh { z-index: 6; color: var(--text-dim); }

  /* 111 + 34 = the 145px frozen block declared above. */
  #page-today table.hub-table .hub-name-h,
  #page-today table.hub-table td.hub-name { width: var(--grid-name) !important; }

  /* Square, and big enough to hit. */
  #page-today table.hub-table thead th[class*="th-wk-"] { width: var(--grid-day) !important; }
  #page-today table.hub-table td[class*="td-wk-"],
  #page-today table.hub-table tbody tr td { height: var(--grid-day); }

  /* One swipe = one week. scroll-padding-left is what keeps the week from
     landing underneath the frozen columns. */
  #page-today .hub-table-wrap {
    scroll-snap-type: x mandatory;
    scroll-padding-left: var(--grid-frozen);
  }
  #page-today table.hub-table thead th.hub-wk-start { scroll-snap-align: start; }

  /* ---- the frozen block is one patti ----

     Four cells wide by however many habits tall — month box, "Habit", "%", and
     every name and percentage under them — and the whole of it is one unbroken
     surface. Not a border anywhere inside it: with border-collapse the table's
     own background shows through the merged border between two cells, and that
     hairline of page colour is what was visible above "Habit", between the two
     columns, and between every pair of rows.

     The lines that separate things are painted INSIDE the cells instead, as
     inset shadows over their own background. Two reasons, both learned the hard
     way here: an inset line cannot open a gap, and it cannot be covered — the
     outset shadow on the name column's right edge was being painted over by the
     % column, which is sticky, opaque and later in the DOM, which is why that
     line "was not added". */
  #page-today table.hub-table .hub-name-h,
  #page-today table.hub-table .hub-pct-mh,
  #page-today table.hub-table td.hub-name,
  #page-today table.hub-table td.hub-pct-m {
    border: none !important;
    background: var(--table-sticky-bg-solid) !important;
    background-clip: padding-box;
  }

  /* Right edge on both columns, and a divider under each row. Header cells get
     the right edge only, so the top of the patti reads as one block. */
  /* The month box, "Habit" and "%" are divided the same way as the rows under
     them — right edge and a line beneath — so the top of the patti belongs to
     the same grid rather than sitting on it. */
  #page-today table.hub-table .hub-name-h,
  #page-today table.hub-table .hub-pct-mh {
    box-shadow: inset -1px 0 0 var(--grid-rule-soft),
                inset 0 -1px 0 var(--grid-rule-soft) !important;
  }
  /* The month box and the % beside it also get a line above, so that first row
     is closed on both sides instead of bleeding into the heading over it. */
  #page-today table.hub-table thead tr:first-child .hub-name-h,
  #page-today table.hub-table .hub-pct-mh {
    box-shadow: inset -1px 0 0 var(--grid-rule-soft),
                inset 0 -1px 0 var(--grid-rule-soft),
                inset 0 1px 0 var(--grid-rule-soft) !important;
    padding-top: 0 !important;
    padding-bottom: 0 !important;
  }
  #page-today table.hub-table tbody td.hub-name,
  #page-today table.hub-table tbody td.hub-pct-m {
    box-shadow: inset -1px 0 0 var(--grid-rule-soft),
                inset 0 -1px 0 var(--grid-rule-soft) !important;
  }

  /* The last row of days gets a floor too. Every other row has one from
     tr:not(:last-child), so the names ended on a line and the dates beside them
     ended on nothing. */
  #page-today table.hub-table tbody tr:last-child td[class*="td-wk-"] {
    border-bottom: 1px solid var(--grid-rule-soft);
  }

  /* Cover the seam where the frozen block meets the scrolling days.

     The day column is (100vw - 145px) / 7, and on most phones that is not a
     whole number — 393px gives 35.43, 412px gives 38.14. Fractional columns put
     the boundary on a half pixel, and the table's own background shows through
     the sliver between the sticky cell and the day beside it. An outset shadow
     in the frozen colour, one pixel wide, paints over it; the sticky cell is
     positioned so its shadow lands above the day cells rather than under them. */
  #page-today table.hub-table .hub-pct-mh {
    box-shadow: inset -1px 0 0 var(--grid-rule-soft),
                inset 0 -1px 0 var(--grid-rule-soft),
                inset 0 1px 0 var(--grid-rule-soft),
                1px 0 0 var(--table-sticky-bg-solid) !important;
  }
  #page-today table.hub-table tbody td.hub-pct-m {
    box-shadow: inset -1px 0 0 var(--grid-rule-soft),
                inset 0 -1px 0 var(--grid-rule-soft),
                1px 0 0 var(--table-sticky-bg-solid) !important;
  }

  /* This heading was the accent colour, which is the theme's loudest ink and is
     being spent here on a label. It follows the mode instead: dark type in light,
     light type in dark. !important because the colour is an inline attribute. */
  #page-today .monthly-sum-title { color: var(--text) !important; }

  /* ---- the task date ----
     A row of chrome — border, background, two arrows and a Today button — to
     hold one value. The icon beside the heading opens the same picker. */
  #page-today .task-date-pill { display: none !important; }
  #page-today .task-date-icon {
    display: inline-flex; align-items: center; justify-content: center;
    width: 44px; height: 44px; margin: -10px 0 -10px auto;
    background: none; border: none; padding: 0;
    font-size: 19px; line-height: 1; cursor: pointer;
  }

  /* ---- the three monthly notes ----
     Read by default, written on request. Three always-open textareas is a
     screen and a half of empty grey boxes saying nothing. */
  #page-today .note-edit {
    display: inline-flex; align-items: center; justify-content: center;
    width: 44px; height: 44px; margin: -12px -8px -12px auto;
    background: none; border: none; padding: 0;
    font-size: 15px; line-height: 1; cursor: pointer;
  }
  #page-today .note-read {
    display: block;
    font-size: 13px; line-height: 1.55; color: var(--text);
    white-space: pre-wrap;
    /* Content-sized, overriding the height syncMonthlyNotesHeight() writes
       inline. That function sizes all three boxes together from the number of
       newlines in the value — max(38, lines * 22 + 16) — and on this tier the
       arithmetic cannot come out right: 13px at 1.55 inside 10px of padding
       needs 40px for a single line, and the empty-state text ("Key execution
       observations & patterns for this month...") wraps to two, so it ran out
       through the bottom border. The boxes are stacked here with a rule between
       them, so they have no reason to share one height anyway. Desktop keeps
       the inline height and its matched row untouched. */
    height: auto !important;
    min-height: 40px !important;
  }
  #page-today .note-read.is-empty { color: var(--text-faint); font-style: italic; }
  /* The field behind it, once the pencil opens it: the same inline height was
     landing a 38px box under the keyboard. */
  #page-today .note-editing > textarea[id^="monthly"] {
    min-height: 96px !important;
    height: auto !important;
  }

  /* One under the other with a rule between, the way the task header is drawn —
     three floating cards in a column is three frames for three lines of text. */
  #page-today #circularWheelBox .card + .card {
    border-top: 1px solid var(--card-brd) !important;
    padding-top: 14px !important;
    margin-top: 4px;
  }

  /* Save sits at the bottom right of the field it saves, and only while that
     field is open. */
  #page-today .note-editing > .note-save {
    display: inline-flex; align-items: center; justify-content: center; gap: 6px;
    margin-left: auto;
    min-height: 40px; padding: 0 16px;
    background: rgba(var(--accent-rgb), 0.14);
    border: 1px solid var(--accent); border-radius: 10px;
    color: var(--accent); font-family: inherit;
    font-size: 12.5px; font-weight: 800; cursor: pointer;
  }
  /* Direct children, not descendants. #circularWheelBox is itself a .card, so
     `.card:not(.note-editing) textarea` matched through the wrapper and kept the
     field hidden even while its own card was in edit mode. */
  #page-today textarea[id^="monthly"] { display: none; }
  #page-today .note-editing > textarea[id^="monthly"] { display: block; }
  #page-today .note-editing > .note-read { display: none; }

  /* The wheel keeps its button — asked for, after being asked against. It stays
     collapsed until it is pressed, which is also what keeps 441 SVG nodes off
     the page until somebody wants them. */

  /* AUG 2026, in the box above the habit names. Hidden everywhere else. */
  #page-today .hub-month-m {
    display: block;
    padding-left: 10px;
    font-family: 'Space Grotesk';
    font-size: 11px; font-weight: 800; letter-spacing: .06em;
    text-transform: uppercase; color: var(--text-dim);
  }

  /* Today gets a line down both sides, not just a tint — a tint is easy to miss
     against five week colours. */
  #page-today table.hub-table th.hub-today,
  #page-today table.hub-table td.hub-today {
    box-shadow: inset 1px 0 0 var(--grid-rule), inset -1px 0 0 var(--grid-rule);
  }

  /* ---- which week is this one ----

     The five week colours only ever said "a new week starts here". Nothing said
     which one is being lived in, and the grid opened on day 1 — on the 21st,
     three swipes from anything current. renderHubTable() marks the columns and
     scrolls to them; these are the marks.

     Colour is not the only signal (it cannot be — the five week colours are
     already colour): the current week is underlined and heavier, today's column
     gets a tint down the full height. */
  /* Bold only. This had an underline too, which was never asked for — the ask
     was that the current week be visible, and the week's own colour plus the
     weight does that without drawing a second line across the header. */
  table.hub-table thead th.hub-cw {
    font-weight: 800 !important;
  }
  table.hub-table thead th.hub-today {
    box-shadow: inset 0 -2px 0 var(--accent);
  }
  table.hub-table td.hub-today {
    background: rgba(var(--accent-rgb), 0.10);
  }

  /* A scroller with a hard edge reads as a table that got cut off. The fade
     says the row continues. Right edge only — the left edge is where the
     sticky name column sits, and fading that would just make the names look
     broken.

     Deliberately a mask and not a backdrop-filter: this element is the exact
     scroll-container-with-sticky-children shape that produced the stale
     black/white rectangles documented above .hub-table-wrap. Verified in both
     themes at 390px after the change. */
  .hub-table-wrap {
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 26px), transparent);
    mask-image: linear-gradient(to right, #000 calc(100% - 26px), transparent);
  }

  /* The task box's summary ring is a 140px column, which on a phone wraps onto
     its own row and sits as a stub against the left edge. Full width with the
     ring beside its label reads as a summary bar and costs ~60px less height.
     !important because both boxes carry the layout inline; this is the escape
     hatch described at the top of the phone tier, scoped to one component. */
  #todayTasks2 { min-width: 0 !important; }

  /* ---- task progress: a line, not a ring ----

     80px of SVG arc inside its own bordered 140px column, to show one
     percentage — and measured on the phone it came out 0x0, so it had not been
     visible at all. A labelled bar says the same thing in 40px and can be read
     without decoding an arc. */
  #homeTaskRingBox {
    width: 100% !important;
    display: block !important;
    background: none !important;
    border: none !important;
    padding: 0 !important;
  }
  #homeTaskRingBox > div:first-child,
  #homeTaskRingText { display: none !important; }

  .task-flat { display: block; }
  .task-flat-top {
    display: flex; align-items: baseline; justify-content: space-between;
    margin-bottom: 8px;
  }
  .task-flat-label {
    font-size: 11px; font-weight: 800; letter-spacing: .06em;
    text-transform: uppercase; color: var(--text-dim);
  }
  .task-flat-count {
    font-family: 'Space Grotesk'; font-size: 15px; font-weight: 800;
    color: var(--text); font-variant-numeric: tabular-nums;
  }
  .task-flat-track {
    height: 6px; border-radius: 3px; overflow: hidden;
    /* Not var(--inner-box-border): see the note in the phone tier. The unfilled
       part of a progress bar has to be visible or the bar says nothing. */
    background: rgba(255, 255, 255, 0.14);
  }
  body.light-theme .task-flat-track { background: rgba(0, 0, 0, 0.10); }
  .task-flat-fill {
    height: 100%; width: 0;
    background: var(--accent); border-radius: 3px;
    transition: width .5s ease-out;
  }

  /* The wheel card is the tallest thing on the page — 1164px measured at 390px,
     around 40% of the whole page — and it draws 441 SVG nodes for a chart whose
     detail does not survive being scaled to a phone. It opens on a tap instead.

     This is a disclosure, not a deferral: the wheel is built on the same
     schedule it always was, and nothing here watches scroll position or a
     timer. Rule 5 of the plan is about moving work in time; this moves it
     behind an intent. */
  #wheelToggleBtn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    min-height: 44px;
    padding: 0 16px;
    border: 1px solid var(--accent);
    border-radius: 10px;
    background: rgba(var(--accent-rgb), .12);
    color: var(--accent);
    font-family: inherit;
    font-size: 13px;
    font-weight: 800;
    cursor: pointer;
  }
  #circularWheelBox:not(.wheel-open) #wheelBody,
  #circularWheelBox:not(.wheel-open) #wheelLegend { display: none; }

  /* 380px was reserving desktop's wheel height; the phone wheel measures ~294px
     and the difference was blank card. */
  .wheel-body { min-height: 0; }
}

/* ==========================================================================
   HABITS PAGE — phone interior                                  (Phase 10.2)

   Same shape as 10.1: the page fits, and fit is not the same as usable. The
   month grid is this page's reason to exist and its checkboxes were 16px in
   ~19px columns. There is also a control here that is genuinely off-screen.
   ========================================================================== */

@media (max-width: 640px) {

  /* ---- the "+ Add" category button was unreachable ----
     .cat-add-box is a nowrap flex row: a text input that will not shrink below
     its intrinsic width, a 95px emoji button, then this. Measured at 390px the
     row needs ~345px and gets 317, so the button's right edge landed at 431 —
     41px of it past the viewport, clipped by body{overflow-x:hidden} rather
     than scrollable. It is a control the user cannot press.

     Nothing else on the page reported this: the 390px fit check reads
     scrollWidth, which stays 390 precisely because the overflow is clipped.
     Found by listing elements whose right edge clears the viewport and which
     have no scrolling ancestor — worth keeping as a check in Phase 11. */
  .cat-add-box { flex-wrap: wrap; }
  .cat-add-box > #newCatNameInput { flex: 1 1 100%; }

  /* Both of these were laid out by inline padding and came out 30px and 26px
     tall. min-height does not fight the inline declaration — it raises the box
     around the same padding — so no !important is needed. */
  .cat-add-box .add-btn, .cat-manage-btn { min-height: 36px; }

  /* ---- month consistency grid ----
     The plan's 10.2 line: 200px name column down to 120px, and checkbox cells
     to a real touch size. Measured before: name 200px, day columns ~19px,
     .chk 16x16, 143 of them.

     32px is the number the plan settled on, and the reason it is not 44px is
     worth repeating: this grid grows to 31 columns by month end, and 44px each
     would make it 1364px + the name column. At 32 it is 1112px at month end
     and 536px today — a scroller either way, which is correct for a data grid.

     No !important needed anywhere here, unlike the hub table in 10.1: this
     table carries no inline widths and uses table-layout:auto, where min-width
     on a cell is honoured. */
  /* ======================= Habits = feed =======================

     The same grammar Today got: sections run kinare se kinare, the frames come
     off, and a 5px grey band with 10px of air on each side separates one from
     the next. Order is set here rather than in the markup — display:contents
     dissolves the two .bento wrappers so their cards become children of the page
     and can be placed individually, which is the only way the progress rings can
     reach the bottom while the heatmap stays in the middle.

     The four stat cards keep their boxes. Asked for directly, so the first bento
     stays a grid — two up, two down — instead of dissolving. */
  #page-habits.active { display: flex; flex-direction: column; }
  /* !important on this one: .hb-an-row carries display:flex as an inline style,
     and without it the row stayed a flex container — so .hb-an-left was ordered
     inside that row instead of on the page, and landed at the top 1374px wide. */
  #page-habits > .hb-analytics,
  #page-habits > .hb-log,
  #page-habits .hb-analytics .card.s7 { display: contents; }
  #page-habits .hb-an-row { display: contents !important; }

  #page-habits .hb-stats {
    display: grid !important;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
    order: 1;
    margin: 0 0 10px !important;
  }
  /* grid-column and width both have to be cleared: the 1024px tier gives every
     .s3 `grid-column: span 12` and `width: 100%`, which in a two-column grid
     still makes each card claim a whole row. That is why the four of them stayed
     stacked when this became a grid. */
  #page-habits .hb-stats .card {
    margin: 0 !important;
    grid-column: auto !important;
    width: auto !important;
    padding: 14px !important;
  }

  /* The page in the order it is used, not the order it was written:
     stats, the add control, challenges, the habit list, the month grid, the
     75-day arena when one is running, the heatmap, and the rings last. */
  #page-habits .hb-add        { order: 2; }
  #page-habits .hb-an-cat     { order: 3; }
  #page-habits .hb-log .card  { order: 4; }
  #page-habits .hb-challenges { order: 5; }
  #page-habits > #separateChallengeArena { order: 6; }
  #page-habits .hb-grid       { order: 7; }
  #page-habits .hb-an-head    { order: 8; }
  #page-habits .hb-an-left    { order: 9; }
  #page-habits .hb-analytics .card.s5 { order: 10; }

  /* Kinare se kinare, and no frames. !important throughout: every one of these
     carries padding, background and border as inline style attributes. */
  #page-habits .hb-grid,
  #page-habits .hb-add,
  #page-habits .hb-challenges,
  #page-habits .hb-analytics .card,
  #page-habits .hb-log .card {
    margin-left: calc(-1 * var(--pad-page)) !important;
    margin-right: calc(-1 * var(--pad-page)) !important;
    margin-bottom: 0 !important;
    width: auto !important;
    background: none !important;
    border: none !important;
    border-radius: 0 !important;
    box-shadow: none !important;
    padding: 14px var(--pad-page) !important;
  }

  /* The bands. Every section carries one on top; the stats block is the page's
     first thing and does not. */
  #page-habits .hb-grid,
  #page-habits .hb-add,
  #page-habits .hb-challenges,
  #page-challenges .hb-challenges,
  #page-habits .hb-analytics .card,
  #page-habits .hb-log .card {
    border-top: 10px solid var(--feed-sep) !important;
    margin-top: 10px !important;
    padding-top: 10px !important;
  }

  /* ---- 75 days, all of them, on one screen ----

     The matrix is repeat(15, 1fr) and its column is min-width:480px, so on a
     390px screen it was a scroller inside a scroller — you swiped sideways to
     count days. Ten across is 8 rows for 75, and at 390px full bleed that puts
     each box at about 33px, which is a real target. The "15 Days per Row" label
     is hidden rather than rewritten, because it is right on the desktop. */
  #page-habits .arena-grid,
  #separateChallengeArena .arena-grid {
    grid-template-columns: repeat(10, 1fr) !important;
    gap: 4px !important;
  }
  /* Both columns go full width so the matrix has the whole screen, and both
     min-widths have to go: the left one is 280px and the right 480px, and
     between them the row stayed 517px wide on a 390px screen. */
  #separateChallengeArena .arena-col,
  #separateChallengeArena .arena-side {
    min-width: 0 !important;
    flex: 1 1 100% !important;
  }

  /* The arena is a section like any other: no frame, both edges. It was still a
     bordered card sitting in the page gutter while everything around it had
     already stopped being one. */
  /* No bleed here. The arena wrapper is already pulled to both edges further
     down, and doing it again on the card inside took the section 14px past the
     glass on each side — x-14 to 404 on a 390px screen. The frame comes off, the
     padding stays, the width comes from the wrapper. */
  #separateChallengeArena > .card,
  #page-habits #separateChallengeArena > div {
    width: auto !important;
    background: none !important;
    border: none !important;
    border-radius: 0 !important;
    box-shadow: none !important;
    padding: 12px var(--pad-page) !important;
  }

  /* "Habits" over the frozen column, so the names have a label like the dates
     have numbers. Phone only — the desktop header cell is empty by design. */
  #page-habits .hg-h-m {
    display: block;
    font-family: 'Space Grotesk';
    font-size: 11px; font-weight: 800; letter-spacing: .04em;
    text-transform: uppercase; color: var(--text-dim);
  }

  /* The header row of the month grid gets the same rules as the body, so the top
     of the frozen column is closed the way the bottom is. */
  #page-habits table.habit-grid thead th.hg-name-h {
    box-shadow: inset -1px 0 0 var(--grid-rule-soft),
                inset 0 -1px 0 var(--grid-rule-soft),
                inset 0 1px 0 var(--grid-rule-soft) !important;
  }
  #page-habits table.habit-grid thead th:not(.hg-name-h) {
    box-shadow: inset 0 -1px 0 var(--grid-rule-soft),
                inset 0 1px 0 var(--grid-rule-soft);
    /* This row is sticky at the top of the scroller, and it was 95% opaque —
       the ticks slid under it and showed through. Same rule as the frozen
       column: anything that other content passes beneath is solid. */
    background: var(--table-sticky-bg-solid) !important;
  }
  #separateChallengeArena .arena-rowlabel { display: none !important; }

  /* The four challenge cards are a carousel. They already scroll sideways; this
     makes a swipe land on a card instead of between two. */
  #page-habits #challengeBentoGrid {
    scroll-snap-type: x mandatory;
    scroll-padding-left: var(--pad-page);
  }
  #page-habits #challengeBentoGrid > * { scroll-snap-align: start; }

  /* ---- progress rings: three to a row ---- */
  #page-habits #ringsFlower {
    display: grid !important;
    grid-template-columns: repeat(3, 1fr) !important;
    gap: 12px 8px !important;
    justify-content: stretch !important;
  }
  #page-habits #ringsFlower > * { width: auto !important; min-width: 0 !important; }

  /* ---- heatmap beside the day it is showing ----

     The card held two stacked things on the left (the heatmap, and the panel for
     whichever day you tapped) and the category breakdown on the right. Almost a
     thousand pixels of page, and the panel — the part that answers "what did I
     actually do that day" — sat below the fold of its own column.

     display:contents on the left column dissolves it, so the heatmap and its
     panel become items of the same row and can sit side by side. The category
     breakdown then drops below both as its own section, with a band above it.

     No JS needed for "show today when nothing is picked": renderHeatmap already
     ends with selectHeatDay(TODAY). */
  #page-habits .hb-an-div { display: none !important; }
  /* The heatmap and its day panel are a row of their own now — the card that
     used to hold them is display:contents, so this is what keeps them paired. */
  #page-habits .hb-an-left {
    display: flex !important;
    flex-wrap: wrap;
    gap: 10px;
    align-items: stretch;
    width: auto !important;
    flex: 0 0 auto !important;
    margin: 0 calc(-1 * var(--pad-page)) !important;
    padding: 0 var(--pad-page) !important;
  }

  /* The heatmap's heading carries the band, because the heading is where the
     section starts — on .hb-an-left the line landed between the title and the
     thing it titles. */
  #page-habits .hb-an-head {
    margin: 10px calc(-1 * var(--pad-page)) 10px !important;
    padding: 10px var(--pad-page) 0 !important;
    border-top: 10px solid var(--feed-sep);
  }

  /* Double the air on this one instead: it is the seam between the heatmap and
     the rings, the last two sections of the page. */
  #page-habits .hb-analytics .card.s5 {
    margin-top: 20px !important;
    padding-top: 20px !important;
  }

  /* Kinare tak, like every other section. */
  #page-habits > #separateChallengeArena {
    margin-left: calc(-1 * var(--pad-page)) !important;
    margin-right: calc(-1 * var(--pad-page)) !important;
    width: auto !important;
  }

  /* Same band as every other section on this page — the arena only renders
     (display:block) while a 75-day challenge is active, so this line and its
     air above/below appear with it and vanish with it, nothing extra needed
     for the inactive case. Sits on the outer div so it lands between the
     month grid above (order 6) and the arena's own card below. */
  #page-habits > #separateChallengeArena {
    border-top: 10px solid var(--feed-sep) !important;
    margin-top: 10px !important;
    padding-top: 10px !important;
  }

  #page-habits #heatmapSubLabel { order: 1; width: 100%; margin-bottom: 6px !important; }
  #page-habits .heatmap-wrap { order: 2; flex: 0 0 auto; }
  #page-habits #heatDetail {
    order: 3;
    flex: 1 1 0;
    min-width: 0;
    margin-top: 0 !important;
    padding: 10px 12px !important;
    font-size: 12px;
  }

  /* Its own section, below the heatmap. */
  #page-habits .hb-an-cat {
    /* No order here. It is set with the rest of the page order above, and this
       copy — left over from when this section sat between the heatmap and the
       rings — came later in the file and won, which is why picking Add Category
       dropped the form below challenges instead of under its own button. */
    /* flex-basis, not width: this column carries `flex:1` inline, and a flex item
       with a basis of 0 ignores width entirely — it stayed on the same line as
       the heatmap at 87px wide. */
    /* The basis carries the bleed: at 100% it is the parent content box, and a
       negative right margin cannot stretch a flex item past its basis, so the
       band stopped 28px short of the right edge. */
    flex: 0 0 calc(100% + (2 * var(--pad-page))) !important;
    width: auto;
    min-width: 0 !important;
    margin: 12px calc(-1 * var(--pad-page)) 0 !important;
    padding: 12px var(--pad-page) 0 !important;
    border-top: 5px solid var(--feed-sep);
  }

  /* Smaller cells, so the heatmap takes a little under half the row and leaves
     the rest to the day panel. */
  #page-habits { --heat-cell: 17px; }

  /* ---- Your Habits ----

     Three things, as asked: the name, its category, and delete. Trigger, why and
     consistency are hidden — hidden, not removed: the cells are still rendered,
     still carry their values, and the desktop table is untouched. Nothing is
     dropped from state either, so a cue typed on a laptop is still there.

     Hiding by column index because that is what the markup gives us; the header
     and body cells share the same three positions. */
  #page-habits #habitDetailTable th:nth-child(3),
  #page-habits #habitDetailTable th:nth-child(4),
  #page-habits #habitDetailTable th:nth-child(5),
  #page-habits #habitDetailTable td:nth-child(3),
  #page-habits #habitDetailTable td:nth-child(4),
  #page-habits #habitDetailTable td:nth-child(5) { display: none !important; }

  #page-habits #habitDetailTable { width: 100%; }
  #page-habits #habitDetailTable th { font-size: 10.5px !important; }
  #page-habits #habitDetailTable td { padding: 8px 6px !important; vertical-align: middle; }
  /* The category cell's own content (the select) is desktop-only now — its
     information moved into the name cell's mobile row below, next to the
     kebab that edits it. The name cell takes the whole row. */
  #page-habits #habitDetailTable tr td:first-child { width: 100% !important; padding-right: 0 !important; }
  /* Both the header cell and every row's data cell — the header's own
     "Category" column was still visible and sized (~86px), and a table's
     auto layout sizes every row's column to match its header, so the name
     cell in every row stopped 86px short of the row's actual right edge no
     matter what width was set on it directly. Hiding only the td left the
     line looking cut in half. */
  #page-habits #habitDetailTable th:nth-child(2),
  #page-habits #habitDetailTable td:nth-child(2) { display: none !important; }

  /* Scoped to the log table. Unscoped, this also hid the names in the month
     grid — .habit-name-inner is the class both of them use for a name cell, and
     the grid ended up as a column of blank space beside the ticks. */
  #page-habits #habitDetailTable .habit-name-inner { display: none !important; }

  /* The phone's read-only name+category block and its kebab button — replaces
     the old always-open name field and category select, which between them
     left no empty row surface and looked like two mismatched boxes. Editing
     now happens through the kebab's menu (openHabitActionsMenu, app.js), so
     this line is just information plus one place to act on it. */
  #page-habits .hb-row-mobile {
    display: flex !important;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    width: 100%;
  }
  #page-habits .hb-row-mobile-info {
    display: flex;
    align-items: center;
    gap: 10px;
    min-width: 0;
  }
  #page-habits .hb-row-mobile-text { min-width: 0; }
  #page-habits .hb-row-mobile-name {
    font-size: 13.5px;
    font-weight: 700;
    color: var(--text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  #page-habits .hb-row-mobile-cat {
    font-size: 10.5px;
    font-weight: 600;
    color: var(--text-dim);
    margin-top: 1px;
  }
  /* Just the icon — no button box around it. Still a full 44px touch target
     (padding, not a visible background/border), pinned flush to the row's
     right edge (the cell it sits in carries no right padding, above). */
  #page-habits .hb-kebab-btn {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    margin-right: -10px;
    background: transparent;
    border: none;
    box-shadow: none;
    color: var(--text-dim);
    font-size: 18px;
    cursor: pointer;
  }
  /* "✕ Delete" is a lot of label for a column that has to fit beside a name and
     a category, so the row keeps the ✕ and drops the word. font-size:0 hides the
     text, which also collapses the button — hence the explicit box, and
     !important on all of it because .del-x-btn brings its own sizing. */
  #page-habits .del-x-btn {
    display: inline-flex !important;
    align-items: center; justify-content: center;
    width: 44px !important; height: 44px !important;
    min-width: 44px !important; min-height: 44px !important;
    padding: 0 !important;
    font-size: 0 !important;
  }
  #page-habits .del-x-btn::after {
    content: '✕';
    font-size: 15px;
    color: var(--danger);
  }

  /* ---- add habit: a disclosure, not a standing form ---- */
  #page-habits .hb-add-head { cursor: pointer; }
  #page-habits .hb-add-caret {
    margin-left: auto;
    font-size: 20px; font-weight: 800; line-height: 1;
    color: var(--accent);
    transition: transform .2s ease;
  }
  #page-habits .hb-add:not(.is-closed) .hb-add-caret { transform: rotate(45deg); }
  /* ---- the add control ----

     Two panels under one segmented button. The habit form is in this section;
     the category form is the .hb-an-cat block, which lives inside the analytics
     card in the markup and is pulled up here by order:3. Only the selected one
     is shown, and Add Habit is the one that starts selected. */
  #page-habits .hb-seg {
    display: flex;
    gap: 6px;
    padding: 4px;
    margin-bottom: 12px;
    background: var(--surface-hover);
    border: 1px solid var(--card-brd);
    border-radius: 12px;
  }
  /* Three equal thirds. flex-basis 0 with min-width 0 is what makes them equal:
     at basis auto each one sizes to its own word and "Challenges" is wider than
     "Habit". */
  #page-habits .hb-seg-btn {
    flex: 1 1 0;
    min-width: 0;
    min-height: 38px;
    border: none;
    border-radius: 9px;
    background: transparent;
    color: var(--text-dim);
    font-family: inherit;
    font-size: 11.5px;
    font-weight: 600;
    letter-spacing: .02em;
    cursor: pointer;
  }

  /* ---- one header shape for all three panels ----
     Challenges had a title and a line of explanation; the two forms had a bare
     control each. Same block, same type, same spacing, whichever tab is open. */
  #page-habits .hb-panel-head {
    display: block;
    margin: 0 0 12px;
  }
  #page-habits .hb-panel-head b {
    display: block;
    font-family: 'Space Grotesk';
    font-size: 15px;
    font-weight: 800;
    color: var(--text);
  }
  #page-habits .hb-panel-head span {
    display: block;
    margin-top: 3px;
    font-size: 11.5px;
    font-weight: 500;
    color: var(--text-dim);
    line-height: 1.45;
  }
  /* The panels' own headings, each a different shape, replaced by the above. */
  /* :not() matters here — the panel header is now the first div in this section,
     and without it this rule hid the very header it was meant to replace. */
  #page-habits .hb-challenges > div:first-of-type:not(.hb-panel-head),
  #page-habits .hb-cmd-badge { display: none !important; }

  /* The category panel is a grid, so its header has to span it. */
  /* Explicit rows. The header spans the grid and lands on row 1, so the name —
     auto-placed — went looking for the first free row and found row 3, because
     the two buttons were pinned to row 2. It reads top to bottom now. */
  #page-habits .hb-an-cat .hb-panel-head { grid-column: 1 / -1; grid-row: 1; }

  /* ---- every button in these panels, one shape ----
     Same height, same radius, same type. They were a 35px gradient pill, a 36px
     outlined chip and a 44px block, which is what made the section look assembled
     rather than designed. */
  #page-habits .hb-add .add-btn,
  #page-habits .hb-an-cat .add-btn,
  #page-habits .hb-manage-btn,
  #page-habits .cat-manage-btn,
  #page-habits .hb-challenges button,
  #page-habits .hb-add button[onclick*="openDefaultHabits"] {
    min-height: 44px !important;
    border-radius: 10px !important;
    font-family: inherit !important;
    font-size: 12.5px !important;
    font-weight: 800 !important;
    letter-spacing: .01em !important;
    box-shadow: none !important;
  }

  /* The category select in the habit form is left exactly as it is, and that is
     a decision rather than an oversight.

     Styling it — any of it: a caret, a flat background, a border — moved 4752
     pixels in the light-habits desktop capture, every one at an emoji edge in
     the challenge deck, while the DOM, the computed styles and the geometry all
     measured identical and the rule sat inside max-width:640px where a 1280px
     capture cannot match it. Bisected to this block hunk by hunk: HEAD passes
     three runs, this block fails three, removing it passes again.

     Not shipped, and the baseline not re-recorded to hide it. The field still
     works and still shows the global chevron; it just does not get its own
     styling until this is understood. */

  /* The delete column gets a label like the two beside it. Empty on the desktop
     by design, so this is a phone-only span. */
  #page-habits .hb-del-h { display: inline; }
  #page-habits #habitDetailTable th:last-child { text-align: center; }

  /* ---- one radius, and no chips in the section headers ----

     Every corner on this page is 10px. It was 14, 16 and 18 depending on which
     card, which reads as three different components rather than one page.

     The little labels at the right of the headers — Monthly Overview, Habit
     Rings, Active Habits, 30-Day Matrix — are gone. They name the section they
     sit in, next to a heading that already names it. The four stat cards keep
     theirs, because those carry a value rather than a repeat of the title. */
  /* Only the things that are actually cards. The feed sections are full-bleed
     strips with a 10px band drawn on top of them, and a corner radius bent the
     ends of that band into curves — a separator between two sections has no
     business being rounded. */
  #page-habits .hb-stats .card,
  #page-habits .challenge-card,
  #page-challenges .challenge-card { border-radius: 10px !important; }
  #page-habits .hb-add,
  #page-habits .hb-grid,
  #page-habits .hb-challenges,
  #page-challenges .hb-challenges,
  #page-habits .hb-log .card,
  #page-habits .hb-analytics .card { border-radius: 0 !important; }

  /* Mobile dynamic challenge layout */
  /* Every 5th card, or an odd card if it's the last one, becomes full width */
  #page-habits #challengeBentoGrid > .challenge-card:nth-child(5n),
  #page-habits #challengeBentoGrid > .challenge-card:nth-child(odd):last-child,
  #page-challenges #challengeBentoGrid > .challenge-card:nth-child(5n),
  #page-challenges #challengeBentoGrid > .challenge-card:nth-child(5n + 1):last-child {
    grid-column: 1 / -1;
  }
  
  /* Inactive full width: text and button side-by-side */
  #page-habits #challengeBentoGrid > .challenge-card:nth-child(5n):not(.is-active),
  #page-habits #challengeBentoGrid > .challenge-card:nth-child(odd):last-child:not(.is-active),
  #page-challenges #challengeBentoGrid > .challenge-card:nth-child(5n):not(.is-active),
  #page-challenges #challengeBentoGrid > .challenge-card:nth-child(5n + 1):last-child:not(.is-active) {
    flex-direction: row !important;
    align-items: flex-end !important;
    min-height: auto !important; /* Shorter box when inactive */
  }
  #page-habits #challengeBentoGrid > .challenge-card:nth-child(5n):not(.is-active) > div:nth-child(2),
  #page-habits #challengeBentoGrid > .challenge-card:nth-child(odd):last-child:not(.is-active) > div:nth-child(2),
  #page-challenges #challengeBentoGrid > .challenge-card:nth-child(5n):not(.is-active) > div:nth-child(2),
  #page-challenges #challengeBentoGrid > .challenge-card:nth-child(5n + 1):last-child:not(.is-active) > div:nth-child(2) {
    flex: 1 1 auto;
    min-width: 0;
    margin-right: 20px; /* 20px margin from subtext to join button */
  }
  /* Force text container to absorb empty space so title and subtext stay grouped tightly at the top */
  #page-habits #challengeBentoGrid > .challenge-card > div:nth-child(2),
  #page-challenges #challengeBentoGrid > .challenge-card > div:nth-child(2) {
    flex-grow: 1 !important;
  }
  
  /* Align title text and resource icon on the exact same vertical line */
  #page-habits #challengeBentoGrid > .challenge-card > div:nth-child(2) > div:first-child,
  #page-challenges #challengeBentoGrid > .challenge-card > div:nth-child(2) > div:first-child {
    align-items: center !important;
  }
  
  /* Margin between name row and subtext */
  #page-habits #challengeBentoGrid > .challenge-card:nth-child(5n):not(.is-active) > div:nth-child(2) > div:first-child,
  #page-habits #challengeBentoGrid > .challenge-card:nth-child(odd):last-child:not(.is-active) > div:nth-child(2) > div:first-child,
  #page-challenges #challengeBentoGrid > .challenge-card:nth-child(5n):not(.is-active) > div:nth-child(2) > div:first-child,
  #page-challenges #challengeBentoGrid > .challenge-card:nth-child(5n + 1):last-child:not(.is-active) > div:nth-child(2) > div:first-child {
    margin-bottom: 10px !important; /* Reduced to 10px as requested */
  }
  #page-habits #challengeBentoGrid > .challenge-card:nth-child(5n):not(.is-active) > [id^="action-btn-"],
  #page-habits #challengeBentoGrid > .challenge-card:nth-child(odd):last-child:not(.is-active) > [id^="action-btn-"],
  #page-challenges #challengeBentoGrid > .challenge-card:nth-child(5n):not(.is-active) > [id^="action-btn-"],
  #page-challenges #challengeBentoGrid > .challenge-card:nth-child(5n + 1):last-child:not(.is-active) > [id^="action-btn-"] {
    flex: 0 0 auto;
  }
  /* Match full-width Join button height and text size to 1x1 buttons, while keeping width slightly larger */
  #page-habits #challengeBentoGrid > .challenge-card:nth-child(5n):not(.is-active) [id^="action-btn-"] .c-btn,
  #page-habits #challengeBentoGrid > .challenge-card:nth-child(odd):last-child:not(.is-active) [id^="action-btn-"] .c-btn,
  #page-challenges #challengeBentoGrid > .challenge-card:nth-child(5n):not(.is-active) [id^="action-btn-"] .c-btn,
  #page-challenges #challengeBentoGrid > .challenge-card:nth-child(5n + 1):last-child:not(.is-active) [id^="action-btn-"] .c-btn {
    margin-top: 0 !important;
    padding: 11px 24px !important; /* Standard height (11px vertical), wider width (24px horizontal) */
    font-size: 13px !important; /* Same as standard c-btn */
  }
  
  /* Hide status icon since resource icon handles active indicator if present */
  #page-habits #challengeBentoGrid > .challenge-card [id^="status-"],
  #page-challenges #challengeBentoGrid > .challenge-card [id^="status-"] {
    display: none !important;
  }

  /* The chips inside the stat cards are a fifth of the height of the cards, and
     a 10px corner on a 20px chip is nearly a pill. Its own rule — folded into
     the selector list above, the 3px went to the cards as well. */
  #page-habits .hb-stats .badge { border-radius: 3px !important; }

  #page-habits .hb-an-head .badge,
  #page-habits .hb-log .badge,
  #page-habits .hb-grid .badge,
  #page-habits .hb-analytics .card.s5 .badge { display: none !important; }

  /* ---- the ring track, visible in both themes ----

     The unfilled part of each ring is stroked rgba(255,255,255,.08) inline —
     white on white in light mode, so the ring only existed where it was already
     complete and there was nothing to show progress against. A presentation
     attribute loses to any CSS rule, so this is enough to replace it, and it is
     scoped to the phone tier and to the light theme: the dark ring is right as
     it is, and the desktop is not being touched. */
  body.light-theme #page-habits .ring-track { stroke: rgba(15, 23, 42, 0.16); }

  /* Challenge cards do not fly in. Every .card carries an entrance animation, and
     revealing four of them at once is four transforms starting together — which
     is the flicker on opening this tab. */
  /* Neither the deck nor the panel around it. .hb-challenges is itself a .card,
     so revealing the tab ran cardIn on the container as well as on the four
     cards inside it — the container is the one you actually see move. */
  #page-habits .challenge-card,
  #page-habits .hb-challenges,
  #page-challenges .challenge-card,
  #page-challenges .hb-challenges,
  #page-habits .hb-add,
  #page-habits .hb-log .card { animation: none !important; }
  #page-habits .hb-seg-btn.is-on {
    background: var(--accent);
    color: var(--on-accent, #000);
  }

  /* The old heading and its disclosure caret are what the segmented button
     replaces. */
  #page-habits .hb-add-head,
  #page-habits .hb-add-caret { display: none !important; }

  /* Three panels under the one control, one at a time. */
  /* The habit panel is its header plus its form, and both belong to that tab —
     hiding only the form left "New habit" standing over the category form. */
  #page-habits.hb-mode-cat .hb-add .form-grid,
  #page-habits.hb-mode-cat .hb-add .hb-panel-head { display: none; }
  #page-habits:not(.hb-mode-cat) .hb-an-cat { display: none !important; }

  /* Delete & Manage sits beside Add and opens the list below, which is otherwise
     out of the way: it is a management screen, not something to scroll past on
     the way to everything else. */
  #page-habits .hb-add .form-grid { grid-template-columns: 1fr auto !important; }
  #page-habits #newHabitInput,
  #page-habits #newHabitCat { grid-column: 1 / -1; }
  #page-habits .hb-manage-btn {
    display: inline-flex;
    align-items: center;
    grid-column: 1;
    justify-self: start;
    min-height: 44px;
    padding: 0 14px;
    background: rgba(255, 62, 160, 0.08);
    border: 1px solid rgba(255, 62, 160, 0.25);
    border-radius: 10px;
    color: var(--danger);
    font-family: inherit;
    font-size: 12px;
    font-weight: 700;
    cursor: pointer;
  }
  #page-habits .hb-manage-btn.is-on {
    background: rgba(255, 62, 160, 0.2);
    border-color: var(--danger);
  }
  #page-habits:not(.hb-show-log) .hb-log .card { display: none !important; }

  /* Add and manage only. The percentage bars are hidden — hidden, not removed:
     the element still renders and the desktop card is untouched. */
  #page-habits #categoryBalance,
  #page-habits .hb-an-cat > div > div:first-child { display: none !important; }

  /* Pulled up under the control, so it carries the control's spacing rather
     than a band of its own. */
  #page-habits .hb-an-cat {
    border-top: none !important;
    margin-top: 0 !important;
    padding-top: 0 !important;
  }

  /* ---- challenges, shaped like the stat boxes ---- */
  #page-habits #challengeBentoGrid,
  #page-challenges #challengeBentoGrid {
    display: grid !important;
    grid-template-columns: 1fr 1fr !important;
    gap: 8px !important;
    overflow: visible !important;
    scroll-snap-type: none !important;
    margin-left: 0 !important;
    margin-right: 0 !important;
    padding: 0 !important;
  }
  #page-habits #challengeBentoGrid > *,
  #page-challenges #challengeBentoGrid > * {
    width: auto !important;
    min-width: 0 !important;
    max-width: none !important;
    margin: 0 !important;
    padding: 12px !important;
    flex: none !important;
  }

  /* ---- the heatmap, turned a quarter ----

     It was a column per week with the weekday names running down the left. Turned
     ninety degrees it is the shape every calendar has: the seven weekday names
     across the top, one week per row underneath. grid-auto-flow goes from column
     to row and the template moves from rows to columns; renderHeatmap writes
     grid-template-columns inline, which is why this needs !important.

     Four to five rows of 17px instead of six columns, so it also stops being the
     tallest thing in its section. */
  #page-habits .heatmap-wrap { flex-direction: column; gap: 6px; }
  #page-habits .heat-daylabels {
    display: grid;
    grid-template-columns: repeat(7, var(--heat-cell));
    grid-template-rows: none;
    gap: 7px;
    margin-right: 0;
  }
  #page-habits .heat-daylabels span { text-align: center; line-height: 1.2; font-size: 9px; }
  #page-habits #heatGrid {
    grid-auto-flow: row !important;
    grid-template-columns: repeat(7, var(--heat-cell)) !important;
    grid-template-rows: none !important;
  }

  /* The month is named by the header above and by the picker; this heading only
     has to say what the section is. */
  #page-habits .heat-month-part { display: none; }
  #page-habits .heat-cat-part { display: none; }

  /* ---- the day panel: a line, not a box ----
     Beside the heatmap, with a rule down the middle instead of a frame around
     it. Turning the heatmap made it narrow enough for the two to sit together —
     seven 17px columns is about 160px, and the panel takes the rest. Smaller and
     unbolded, because it is a list of names and the heatmap is the thing being
     read. */
  #page-habits .hb-an-left {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: stretch;
    justify-content: flex-start !important;
  }
  /* The sub-label is a child of this row, so it was taking the first slot in it
     and pushing the heatmap 61px in. Its own line, above the pair. */
  #page-habits #heatmapSubLabel { flex: 1 1 100%; margin-bottom: 8px; }
  #page-habits .heatmap-wrap { flex: 0 0 auto; }
  #page-habits #heatDetail {
    /* basis 0, not auto: at auto the list measured its own text, found it wider
       than the space left beside the heatmap, and wrapped onto its own line. */
    flex: 1 1 0;
    min-width: 0;
    background: none !important;
    border: none !important;
    border-left: 1px solid var(--card-brd) !important;
    border-radius: 0 !important;
    margin: 0 !important;
    padding: 0 0 0 10px !important;
    font-size: 10.5px !important;
    line-height: 1.45;
  }
  #page-habits #heatDetail div {
    font-size: 10.5px !important;
    font-weight: 500 !important;
  }

  /* The date and the percentage sit on the weekday row, and the names start where
     the squares start. They already shared a top edge, but the header was 18px
     against the labels' 11 — so it read as its own line rather than the same one.
     Matched to the row and to the gap under it. */
  #page-habits .heat-detail-head {
    height: 11px !important;
    margin-bottom: 6px !important;
    font-size: 10px !important;
    line-height: 11px !important;
  }
  #page-habits .heat-detail-head span { font-size: 10px !important; }

  /* ---- challenges: no emoji on the actions, and one small Active chip ---- */
  #page-habits .c-emo,
  #page-habits .c-join-d,
  #page-challenges .c-emo,
  #page-challenges .c-join-d { display: none !important; }
  #page-habits .c-join-m,
  #page-challenges .c-join-m { display: inline !important; }
  
  #page-habits #challengeBentoGrid > *,
  #page-challenges #challengeBentoGrid > * { position: relative; }
  

  /* The phone's name for the habit list — the three columns it was named after
     are hidden here. */
  #page-habits .hb-log-d,
  #page-habits .hb-log-sub-d { display: none; }
  #page-habits .hb-log-m,
  #page-habits .hb-log-sub-m { display: inline; }

  /* The category form keeps its input and its Add button; the emoji picker is a
     95px control for something that has a sensible default. */
  #page-habits #newCatEmojiBtn { display: none !important; }

  /* ---- the category form, laid out ----

     Name on its own line, then Manage on the left and Add on the right. The two
     buttons live in different parents in the markup — Add inside .cat-add-box,
     Manage in a row of its own below the (hidden) percentage bars — so those
     wrappers are dissolved and all three controls become items of one flex box
     that can order them. */
  /* A grid, not a wrapping flex row. This element carries flex-direction:column
     and align-items:center as inline styles, so a flex row here fought both and
     lost — the three controls came out stacked and centred. Grid places them
     outright: the name across the top, Manage bottom-left, Add bottom-right. */
  #page-habits .hb-an-cat {
    display: grid !important;
    grid-template-columns: 1fr auto;
    align-items: center;
    gap: 10px;
  }
  #page-habits .hb-an-cat > div,
  #page-habits .cat-add-box { display: contents !important; }
  #page-habits #newCatNameInput {
    grid-column: 1 / -1;
    grid-row: 2;
    min-height: 44px !important;
    margin: 0 !important;
  }
  #page-habits .cat-manage-btn {
    grid-column: 1;
    grid-row: 3;
    justify-self: start;
    min-height: 44px;
    padding: 0 14px !important;
    font-size: 12px !important;
  }

  /* The two management buttons drop their gear emoji on the phone; the category
     one also swaps its long label ("Manage / Delete Categories") for the short
     "Delete Category". */
  #page-habits .hb-manage-emoji { display: none; }
  #page-habits .cat-manage-btn .cat-full { display: none; }
  #page-habits .cat-manage-btn .cat-short { display: inline; }

  /* Both Add buttons are the same control doing the same job, so they are the
     same size, and both sit at the right end of their row. */
  /* Both on row two, and the same width — they are the same control doing the
     same job, and "+ Add Habit" is a longer word than "+ Add". */
  /* grid-column/row only on the category one: the habit form is a single-column
     grid, and asking for column 2 there created an implicit column that put the
     button 5px past the screen. */
  #page-habits .hb-an-cat .add-btn { grid-column: 2; grid-row: 3; }
  #page-habits .hb-an-cat .add-btn,
  #page-habits .hb-add .add-btn {
    margin-left: auto !important;
    justify-self: end;
    width: 132px !important;
    min-height: 44px !important;
    padding: 0 22px !important;
    font-size: 13px !important;
    border-radius: 10px !important;
  }

  /* ---- the same fill the Tasks add button got ----

     Add Habit, Add Category and the five Join buttons were all the raw accent,
     #00E5FF. Two of them carry white labels, which on that cyan measures
     1.54:1 -- the same reading that sent the Tasks button to a deeper fill --
     and the Join buttons were black on it, so three controls doing the same
     kind of thing looked like three different controls.

     One fill for all of them: 5.79:1 for white at the light end, 8:1 at the
     dark. The glow behind the add buttons goes with it, including the larger
     one :hover paints, which a phone can still fire on tap. */
  #page-habits .add-btn.habit,
  #page-habits .c-btn,
  #page-challenges .c-btn {
    background: linear-gradient(135deg, #00707F, #0B4EA2) !important;
    color: #fff !important;
  }
  #page-habits .add-btn.habit,
  #page-habits .add-btn.habit:hover,
  #page-habits .c-btn,
  #page-habits .c-btn:hover,
  #page-challenges .c-btn,
  #page-challenges .c-btn:hover { box-shadow: none !important; }

  /* Tap and it stays pink: .del-x-btn:hover paints itself solid magenta, and a
     phone keeps :hover on the last thing touched. The row is a list, not a
     warning. */
  #page-habits .del-x-btn:hover,
  #page-habits .del-x-btn:focus,
  #page-habits .del-x-btn:active {
    background: rgba(255, 62, 160, 0.14) !important;
    border-color: rgba(255, 62, 160, 0.35) !important;
    box-shadow: none !important;
  }

  /* The rule down the right of the 75-day checklist is a desktop divider between
     two columns; on the phone they are stacked and it hangs off the side. */
  #separateChallengeArena .arena-side {
    border-right: none !important;
    padding-right: 0 !important;
  }
  /* Cue and why are hidden here too, since nothing on the phone shows them.
     They keep rendering, and anything already stored stays stored. */
  #page-habits #newHabitCue,
  #page-habits #newHabitWhy { display: none !important; }

  /* Section headings, matching Today's. */
  #page-habits .hb-sec .section-title,
  #page-habits .hb-sec h3 {
    font-family: 'Space Grotesk';
    font-size: 13px !important;
    font-weight: 800;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--text) !important;
  }

  /* The grid wrapper carries margin:0 -22px inline to escape the card padding
     that is no longer there. Zero, and the section's own bleed does the work. */
  #page-habits .habit-grid-wrap {
    margin: 0 calc(-1 * var(--pad-page)) !important;
    /* Ten pixels of scrollable space after the last column. The column maths
       already leaves a gap at the normal reading position, but today's week sits
       at the end of the table, so the grid is scrolled hard right and the last
       date landed flush against the glass. */
    padding-right: 10px;
    /* No scrollbar drawn across the bottom. It still scrolls — and it snaps a
       week at a time, so the bar was reporting a position nobody reads. */
    scrollbar-width: none;
  }
  #page-habits .habit-grid-wrap::-webkit-scrollbar { display: none; }

  /* The fade on the right of these scrollers reads as a shadow hanging off the
     section. Nothing else in the feed floats, so neither do they. */
  #page-habits .habit-grid-wrap,
  #page-habits .htable-wrap {
    -webkit-mask-image: none !important;
    mask-image: none !important;
    box-shadow: none !important;
  }

  /* ---- one week fills the screen ----

     Superseding the 32px minimum below it: that made a scroller of a fixed cell
     size, and what a month grid on a phone actually needs is a week you can see
     all of. The width is (screen - frozen) / 7 in whole pixels, with the
     remainder given to the name column, and positionHabitGrid() writes both —
     the same arithmetic as Today's hub table, and the same reason it has to be
     integers: a fractional column puts the boundary on a half pixel and the tail
     of the day that scrolled past shows beside the frozen names.

     Cells are square, because a column of ticks should read as a column. */
  #page-habits table.habit-grid {
    table-layout: fixed;
    width: calc(var(--hg-name, 111px) + (var(--hg-cols, 31) * var(--hg-day, 35px))) !important;
  }
  #page-habits table.habit-grid .hg-name-h,
  #page-habits table.habit-grid td.habit-name {
    width: var(--hg-name, 111px);
    min-width: var(--hg-name, 111px);
    max-width: var(--hg-name, 111px);
    padding: 0 8px; font-size: 12.5px;
  }
  #page-habits table.habit-grid thead th:not(.hg-name-h),
  #page-habits table.habit-grid td:not(.habit-name) {
    width: var(--hg-day, 35px);
    min-width: var(--hg-day, 35px);
    height: var(--hg-day, 35px);
  }

  /* The frozen column, as one patti. The header cell of that column was not
     sticky at all — the same half-built freeze that put scrolled-past dates over
     the habit names on Today. No borders inside it either: with border-collapse
     the table's background shows through the merged border between two cells,
     and the dividing lines are inset shadows over each cell's own background. */
  #page-habits table.habit-grid .hg-name-h,
  #page-habits table.habit-grid td.habit-name {
    position: sticky; left: 0;
    background: var(--table-sticky-bg-solid);
    border: none;
    box-shadow: inset -1px 0 0 var(--grid-rule-soft),
                inset 0 -1px 0 var(--grid-rule-soft);
  }
  #page-habits table.habit-grid .hg-name-h { z-index: 6; box-shadow: inset -1px 0 0 var(--grid-rule-soft); }
  #page-habits table.habit-grid td.habit-name { z-index: 5; }
  #page-habits table.habit-grid thead th:not(.hg-name-h) { z-index: 4; }

  /* Today gets a line down both sides — the tint alone is easy to miss — and the
     last row gets a floor, so the dates end where the names do. */
  #page-habits table.habit-grid th.today-col,
  #page-habits table.habit-grid td.today-col {
    box-shadow: inset 1px 0 0 var(--grid-rule), inset -1px 0 0 var(--grid-rule);
  }
  #page-habits table.habit-grid tbody tr:last-child td:not(.habit-name) {
    border-bottom: 1px solid var(--grid-rule-soft);
  }

  /* One swipe, one week. scroll-padding-left keeps the week from landing under
     the frozen names. */
  #page-habits .habit-grid-wrap {
    scroll-snap-type: x mandatory;
    scroll-padding-left: var(--hg-name, 111px);
  }
  #page-habits table.habit-grid thead th.hg-wk-start { scroll-snap-align: start; }

  /* The cell is only the target if the button fills it — a 32px cell around a
     16px button is still a 16px tap, because the onclick is on the button and
     not the cell. Scoped to this grid: the same .chk class is in Today's hub
     table, whose columns are 24px wide (10.1) and would overflow if it grew. */
  table.habit-grid .chk { width: 30px; height: 30px; }
  table.habit-grid .chk.on::after { font-size: 18px; }

  /* ---- heatmap ----
     22px cells, and .has-data makes them clickable. One token moves the cells,
     the row track, the weekday labels and the JS-written column track together
     — sizing any of them separately desynchronises the two grids. Six week
     columns at 30px plus gaps is 215px, so this still fits with no scroller.

     Retuning the token rather than the columns is what makes this work at all:
     renderHeatmap() writes the column track inline, so a rule here would lose,
     and the week count is 4-6 depending on the month so a literal repeat()
     would be wrong half the time. The legend swatches keep their own 13px rule
     and are not controls. 32px matches the grid above rather than sitting one
     pixel under it. */
  :root { --heat-cell: 32px; }

  /* ---- challenge cards become a snap carousel ----
     Four cards at ~160px each stacked cost ~640px of a 4900px page, and they
     are a browse-and-pick set, which is what the plan lists carousels for.
     78% width leaves the next card peeking, which is the only thing that tells
     a user the row scrolls. The negative margin lets it bleed to both screen
     edges while the padding keeps the first and last card off the edge. */
  #challengeBentoGrid {
    flex-direction: row;
    flex-wrap: nowrap;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    margin-left: calc(-1 * var(--pad-page));
    margin-right: calc(-1 * var(--pad-page));
    padding: 4px var(--pad-page);
  }
  #challengeBentoGrid::-webkit-scrollbar { display: none; }
  #challengeBentoGrid > .challenge-card {
    flex: 0 0 78%;
    scroll-snap-align: center;
  }

  /* ---- scroller edge fades ----
     Right edge only, same reasoning as the hub table in 10.1: the left edge is
     where the sticky name column sits. Both of these are legitimate data-table
     scrollers that the plan says to keep. */
  .habit-grid-wrap, .htable-wrap {
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 26px), transparent);
    mask-image: linear-gradient(to right, #000 calc(100% - 26px), transparent);
  }
}

/* ==========================================================================
   TASKS PAGE — phone interior                                   (Phase 10.3)

   This page had the worst version of the problem so far: both of its cards
   rendered 520px wide inside a 390px screen, so ~144px of every task row, the
   filter strip and the calendar was cut off and unreachable. As on Habits, no
   existing check saw it — the page reports scrollWidth 390 precisely because
   the excess is clipped rather than scrolled.
   ========================================================================== */

@media (max-width: 640px) {

  /* The 900px tier already collapses this to one column, so the track is 1fr
     and should be 362px. It was not, because a grid item's default min-width
     is auto: the track cannot shrink below the item's min-content width, and
     the item here measures 520px. `1fr` looks like a cap and is not one.

     minmax(0,1fr) frees the track and min-width:0 frees the item — the track
     and the item each have their own floor, and lifting only one still leaves
     the other in place. */
  .tasks-grid-layout { grid-template-columns: minmax(0, 1fr); }
  .tasks-grid-layout > * { min-width: 0; }

  /* What made that item 520px wide: the new-task row is a nowrap flex line of
     text field + recurrence select + Add button, and its min-content is the
     sum of all three. Wrapping it puts the field on its own line and the two
     controls under it. .add-row is shared with Money and Journal, which have
     the same three-part shape, so they get the same treatment. */
  .add-row { flex-wrap: wrap; }
  .add-row > input, .add-row > textarea { flex: 1 1 100%; }

  /* 28px tall, and they are the page's primary filter. */
  .task-filter-pill { min-height: 36px; }

  /* Every control on a task row was between 20px and 26px: the status circle,
     the star and the delete cross, all sized by inline padding on markup that
     carried no classes at all. They now carry one each, so these are ordinary
     rules rather than a fight with the style attribute.

     The row itself is also clickable (it toggles done), so the small controls
     sit inside a large target — which is exactly why they need to be big
     enough to hit deliberately. A mis-aimed tap on the delete cross lands on
     "mark this done" instead. */
  .task-row { gap: 8px; }
  .task-row-btn { min-width: 40px; min-height: 40px; font-size: 17px; }
  .task-check { width: 28px !important; height: 28px !important; }

  /* Growing the controls squeezed the one thing on the row that matters: the
     task text dropped to about 60px and wrapped over three or four lines. The
     status pill is what pays for it — "Pending" repeats the empty circle it
     sits beside and the "Active focus objectives" heading above it, and
     "Completed" repeats a filled green circle and a line through the text.
     Dropping it returns roughly 200px to the text, which is one clean line. */
  .task-badge { display: none; }

  /* Month arrows were 28x24 and "Today" 58x23. min-width as well as min-height:
     the arrows are single glyphs, so padding alone left them 28px across. */
  .cal-nav button { min-height: 36px; min-width: 40px; padding-left: 12px; padding-right: 12px; }
  .rollover-btn { min-height: 36px; }

  /* ======================= Tasks = feed =======================

     Same grammar Today and Habits already carry: every section runs kinare se
     kinare, the frames come off, and a 10px band separates one from the next.

     Measured before this block, 390x844 with five tasks on the day: the page was
     1980px (2.3 screens) across 49 framed boxes, and the four stat cards alone
     were 591px of it because they stood one under another.

     The four stat cards keep their boxes and go two by two — the same thing the
     Habits deck was asked for, and the same two declarations are needed to make
     it happen. Everything else loses its frame. */
  #page-tasks.active { display: flex; flex-direction: column; }

  /* display:contents dissolves the two-column wrapper so the planner and the
     calendar become children of the page and can be placed and bled
     individually. The 900px tier already made this one column, so nothing about
     the desktop arrangement is being undone here — the wrapper simply stops
     being a box. */
  #page-tasks > .tasks-grid-layout { display: contents; }

  #page-tasks .tk-stats {
    display: grid !important;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
    order: 1;
    margin: 0 0 10px !important;
  }
  /* Both of these are required, and for the reason the Habits deck recorded:
     the 1024px tier gives every .s3 `grid-column: span 12` and `width: 100%`,
     which inside a two-column grid still claims a whole row each. Clearing only
     one of the two leaves the cards stacked. */
  #page-tasks .tk-stats .card {
    margin: 0 !important;
    grid-column: auto !important;
    width: auto !important;
    padding: 14px !important;
  }
  /* The stat chips carried a 6px corner inline; Habits draws 3px, so these
     match it -- one radius across all three decks. */
  #page-tasks .tk-stats .badge { border-radius: 3px !important; }

  #page-tasks .tk-planner { order: 2; }

  /* Kinare se kinare, and no frames. !important throughout: padding, background,
     border and radius are all inline style attributes on this card. */
  #page-tasks .tk-planner {
    margin-left: calc(-1 * var(--pad-page)) !important;
    margin-right: calc(-1 * var(--pad-page)) !important;
    margin-bottom: 0 !important;
    width: auto !important;
    background: none !important;
    border: none !important;
    border-radius: 0 !important;
    box-shadow: none !important;
    padding: 14px var(--pad-page) !important;
    border-top: 10px solid var(--feed-sep) !important;
    margin-top: 10px !important;
    padding-top: 10px !important;
  }

  /* The completed log is the page's third section, but it lives inside the
     planner card rather than beside it, so its band has to be drawn on the
     heading itself. The 1px rule it carried becomes the 10px band, and the
     negative margins take that band the full width of the screen while the
     heading text stays on the gutter. */
  #page-tasks .tk-done-head {
    border-top: 10px solid var(--feed-sep) !important;
    margin: 10px calc(-1 * var(--pad-page)) 10px !important;
    padding: 10px var(--pad-page) 0 !important;
  }

  /* ---- the calendar card, and what replaces it ----

     483px measured at 390x844 -- a quarter of the page -- to do one thing the
     phone can do in a header: choose which date the list below is for. Its
     month arrows, its "Today" button and its per-day dots are all worth having
     on a desktop beside the list; on a phone they are a second screen you
     scroll past to reach a date picker the OS already draws better.

     Hidden, not removed. renderTaskCalendar() keeps filling it -- writing
     innerHTML into a hidden element is free and keeps the two renders in step
     -- and the desktop is untouched. */
  #page-tasks .tk-cal { display: none !important; }

  /* The pair that takes its place is the one the Today page already runs: a
     44px icon, and a zero-sized <input type="date"> it opens. No new data path
     is involved -- setTaskDate() writes state.selectedTaskDate, which is the
     same field addTask(), deleteTask() and toggleTaskStatus() already read. */
  #page-tasks .task-date-icon {
    display: inline-flex; align-items: center; justify-content: center;
    width: 44px; height: 44px; margin: -6px 0 -6px auto;
    background: none; border: none; padding: 0;
    font-size: 19px; line-height: 1; cursor: pointer;
  }

  /* The header is space-between, so with the icon added it had three items and
     the icon landed in the middle. Growing the title block pushes the icon to
     the right edge, and the progress row drops to its own line under both.

     `flex: 1 1 0`, not `1 1 auto`: with an auto basis the block asks for its
     content width first, and on today's date that content is 306px -- which
     with the 12px gap and the 44px icon comes to exactly 362, the width of the
     line. One pixel more in any font and the icon wrapped underneath, which is
     what it did on the phone. A zero basis means the block never outbids the
     icon for the line; the heading wraps inside itself instead. */
  #page-tasks .tk-head > div:first-child { flex: 1 1 0; min-width: 0; }
  #page-tasks .tk-progress { flex: 1 1 100%; }

  /* ---- the filters, on one line ----

     Five pills measuring 424px in a 315px track, so they wrapped onto two rows
     85px tall. Three things get them onto one, and the first is the one that
     makes the arithmetic work at all.

     1. The row's own rollover button goes. It is not a second control -- it
        calls rolloverTasks(), the same function as the full-width button in the
        fourth stat card above it -- and being a flex sibling it was taking
        124px off the line the filters needed. One control, one place. */
  #page-tasks .tk-filter-row .rollover-btn { display: none; }
  #page-tasks .tk-filter-row #taskFilterSegmented { flex: 1 1 100%; }

  /*  2. The trailing glyphs go. Each one repeats what its word already says --
        the hourglass beside "Pending", the tick beside "Completed" -- and
        together they were about 90px of the overflow. The words are untouched,
        and the desktop keeps its glyphs. */
  #page-tasks .tk-pill-icon { display: none; }

  /*  3. What is left is 233px of text at 11px in a 338px track (362 full-bleed,
        less the control's 8px of padding and its four 4px gaps), which leaves
        about 10px of padding per pill.

        `flex: 1 1 auto` rather than `1 1 0`: an equal fifth is 59px of text
        room, and "Completed" measured 56.8 of it -- 2.4px of headroom, which a
        font that renders a shade wider would swallow. Sizing from content and
        sharing out only the surplus gives every label the width it actually
        needs and hands the rest to whoever is next to it.

        !important on both: font-size and padding are inline on every pill. */
  #page-tasks #taskFilterSegmented { flex-wrap: nowrap; }
  #page-tasks .task-filter-pill {
    flex: 1 1 auto; min-width: 0;
    font-size: 11px !important;
    padding: 6px 4px !important;
    white-space: nowrap;
  }

  /* ---- the progress row ----
     The bar was a fixed 200px, which is why the row measured 326px inside a
     353px box and hung over its own padding. On its own line now (above), so
     the bar takes whatever the percentage and the count leave it.

     And the pill around it goes. On a page with no frames left it was the only
     boxed thing between the heading and the list, which read as a control
     rather than a readout. Padding, background, border and radius are all
     inline, hence the !important on each. */
  #page-tasks .tk-progress-track { width: auto !important; flex: 1 1 auto; }
  #page-tasks .tk-progress {
    background: none !important;
    border: none !important;
    border-radius: 0 !important;
    padding: 0 !important;
  }

  /* ---- a surface the filter track never had in the dark ----

     It is drawn on var(--inner-box-bg), and that variable references itself in
     the dark theme -- invalid, so it resolves to nothing and the computed
     background came out rgba(0,0,0,0). The add row and the progress row had the
     same problem, but both were asked to lose their frames outright (below), so
     a background on either would be putting back what was taken off. The filter
     track is the one that still wants one.

     Dark only: the light theme defines the variable properly and already looks
     right, and body:not(.light-theme) is how this file scopes the other
     dark-only repair. */
  body:not(.light-theme) #page-tasks #taskFilterSegmented {
    background: var(--surface-bg) !important;
  }

  /* The row is the tap target for "done" and the circle repeats it, so this is
     legibility rather than reach: 28px read as a dot beside 14px text. */
  #page-tasks .task-check { width: 32px !important; height: 32px !important; }

  /* ---- the add row loses its frame too ----
     A bordered tray holding a bordered field, a bordered select and a button:
     three surfaces deep for one line of input. The controls inside keep their
     own edges, which is what tells you where to type. */
  #page-tasks .add-row {
    background: none !important;
    border: none !important;
    border-radius: 0 !important;
    padding: 0 !important;
  }

  /* The field takes the first line and the two controls share the second, so
     the button goes to the right edge and the recurrence select keeps the left.
     margin-left:auto rather than justify-content, because the row is shared
     with Money and Journal and this is the only page being changed. */
  #page-tasks .add-row .add-btn { margin-left: auto; }

  /* White on the accent, as asked -- but not on the accent as it was. #00E5FF
     is a bright cyan: white on it measures 1.54:1, against the 4.5:1 a body
     label needs, and the black it replaces measured 13.65:1. So the fill goes
     deep enough for the white to read, keeping the same cyan-to-blue direction
     the button already had. */
  #page-tasks .add-row .add-btn.task {
    background: linear-gradient(135deg, #00707F, #0B4EA2) !important;
    color: #fff !important;
  }
  #page-tasks .add-row .add-btn.task,
  #page-tasks .add-row .add-btn.task:hover { box-shadow: none; }

  /* ---- the rollover button ----
     Two by two made its card half as wide, and "Rollover All Pending" no longer
     fit on one line: the label wrapped and the button grew to 53px with the
     text crushed against both edges. The tail of the label goes -- the card is
     titled "Pending Friction" and its badge counts the pending ones, so the
     word is said twice above it already -- and what is left sits on one line in
     a button tall enough to press. */
  #page-tasks .tk-roll-long { display: none; }
  #page-tasks .tk-label-full  { display: none; }
  #page-tasks .tk-label-short { display: block; }

  /* The fill was the accent at 28% to 50% alpha, which over a dark card is a
     wash rather than a colour: white on it measured 3.26:1 against the 4.5:1 a
     label needs. Solid, in the same magenta family, takes it to 5.87:1 at the
     light end and 8.43 at the dark, and the button reads as something you press
     rather than something that is greyed out. */
  #page-tasks .tk-rollover {
    min-height: 40px;
    padding: 8px 10px !important;
    font-size: 12.5px !important;
    font-weight: 800 !important;
    letter-spacing: .02em;
    white-space: nowrap;
    border-radius: 10px !important;
    background: linear-gradient(135deg, #C2185B, #96114F) !important;
    border: 1px solid #C2185B !important;
    color: #fff !important;
    box-shadow: none !important;
  }

  /* And it stands on the floor of its card, not in the middle of it. The card
     is a space-between column, but everything here lives inside one wrapper, so
     the wrapper has to become a column of its own height before an auto margin
     under the label has anything to push against. Measured before: 35px of card
     left below the button. */
  #page-tasks .tk-pend-body { flex: 1; display: flex; flex-direction: column; }
  #page-tasks .tk-rollover-wrap { margin-top: auto !important; }

  /* ---- the completed log ends above the FAB ----

     The FAB is 60x60, fixed, 20px in from the right, and its top edge sits
     118px above the bottom of the viewport. The completed log is the last thing
     on this page, so its final row ended 86px above the bottom -- 14px of the
     section's own padding plus .main2's 72 -- and the FAB covered the right end
     of that row, which is where the star and the delete cross are. A mis-aimed
     tap there hits the FAB instead of the task.

     Padding rather than margin: the section is full-bleed with a background of
     its own, and a margin would open a gap in the feed rather than inside the
     last section. .main2's 72 stays where it is -- it is shared with nine other
     pages, and this is the only one whose last row runs the full width. */
  #page-tasks .tk-planner { padding-bottom: 76px !important; }

  /* ---- the stat decks lose their prose ----
     One sentence under each number, on both pages, saying in words what the
     label above and the number itself already say. Eight cards, and dropping it
     takes a row off each deck. Hidden, not deleted: the desktop has the room
     for it and keeps it. */
  #page-tasks .stat-sub,
  #page-habits .stat-sub,
  #page-money .stat-sub { display: none; }
}

/* ==========================================================================
   MONEY PAGE — phone interior                                   (Phase 10.4)

   Nothing was clipped here: .add-row already wraps from 10.3, and this page
   uses the same three-part form. What it had instead was the smallest control
   in the app, and it deleted things.
   ========================================================================== */

@media (max-width: 640px) {

  /* An 11px-wide delete button, eight of them, one per transaction row — the
     smallest target measured anywhere in this app, and the only destructive
     one that small. The recurring-subscription delete was 19x22 and its "+ Log"
     53x26, sitting right next to each other, which is the arrangement most
     likely to turn a mis-tap into a deletion.

     40px for both crosses, and the log button grows too so the pair stay
     distinguishable by size rather than only by colour. */
  .tx-del, .rec-del {
    min-width: 40px; min-height: 40px;
    font-size: 16px;
  }
  .add-btn.money { min-height: 36px; }
  /* Once the header stopped squeezing them onto two lines, these settled at
     22px tall — the same fix the task filters got in 10.3. */
  .tx-filter-pill { min-height: 36px; }
  /* The carryover pencil is 11x14 and sits inline inside a line of text, so it
     gets a square target without becoming a block. */
  .edit-inline-btn {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 36px; min-height: 36px;
    font-size: 15px;
  }

  /* Measured: the container reserves 220px and the chart draws 107px of it, so
     113px of the card is blank. The SVG has a fixed viewBox and scales on
     width, so the reserved height can never be filled — forcing it taller would
     mean distorting the chart. Dropping the reservation lets the card hug the
     chart instead. !important because the height is an inline style.

     Only the day chart: #moneyCatChart's extra height is its legend, which is
     real content. */
  #moneyDayChart { min-height: 0 !important; }

  /* ---- transaction log becomes a card list ----
     This is the one table where scrolling was the wrong answer. It measured
     418px in a 317px container, and the column that fell off the right edge was
     Amount — the single thing a money log is read for. An edge fade would have
     advertised the scroll honestly and still left every figure off-screen.

     So the plan's alternative applies here: each row becomes a card. The cells
     keep their DOM order (date, description, category, amount, delete) and are
     re-ordered visually — description on its own line, then date and category
     under it with the amount pushed to the right edge. Everything is on screen
     and the amounts still line up down the right.

     Scoped to #txTable, not .tx-table: the leaderboard's rank table shares that
     class and is a different shape. */
  #txTable, #txTable tbody, #txTable tr, #txTable td { display: block; }
  #txTable thead { display: none; }

  #txTable tbody tr {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px 10px;
    padding: 10px 12px;
    margin-bottom: 8px;
    border: 1px solid var(--card-brd);
    border-radius: 10px;
    background: var(--inner-box-bg-light);
  }
  #txTable tbody td { border: none; padding: 0; }
  #txTable tbody td:nth-child(2) { flex: 1 1 100%; order: 1; font-size: 14px; }
  #txTable tbody td:nth-child(1) { order: 2; }
  #txTable tbody td:nth-child(3) { order: 3; }
  #txTable tbody td:nth-child(4) { order: 4; margin-left: auto; font-size: 14px; }
  #txTable tbody td:nth-child(5) { order: 5; }
  /* The empty-state row is a single full-width cell and must not be re-ordered
     into the layout above. */
  #txTable tbody td[colspan] { flex: 1 1 100%; order: 0; text-align: center; }

  /* Card headers are flex rows of title + badge with no wrap, and at 317px the
     two collide — "Expenses by Category" against "Category Distribution", and
     the Transaction Log title against its three filter pills. Letting them wrap
     puts the badge on its own line instead of squeezing the heading.
     Scoped to this page; the other pages get the same treatment in their own
     phases, once each one has been looked at. */
  #page-money div:has(> .section-title),
  #page-money div:has(> div > .section-title) { flex-wrap: wrap; }

  /* ======================= Money = feed =======================

     The fourth page to get this grammar: sections run kinare se kinare, the
     frames come off, and a 10px band separates one from the next. Measured
     before, 390x844 with five transactions and three subscriptions on the page:
     2705px across 20 framed boxes, and the four stat cards were 588 of it
     standing one under another.

     The order is the one asked for, and it is set here rather than in the
     markup, because two of these sections have to come out of the middle of a
     card. display:contents dissolves the three .bento wrappers and the log card
     so their children become children of the page and can be placed one by one
     -- which is the only way the transaction history can reach the bottom while
     the filters and the form it belongs to stay near the top. */
  #page-money.active { display: flex; flex-direction: column; }
  #page-money > .m-charts,
  #page-money > .m-deck,
  #page-money .m-log { display: contents; }

  /* The four stat cards keep their boxes and go two by two. Both declarations
     are needed, for the reason the other two decks recorded: the 1024px tier
     gives every .s3 `grid-column: span 12` and `width: 100%`, and clearing only
     one of them leaves the cards stacked. */
  #page-money .m-stats {
    display: grid !important;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
    order: 1;
    margin: 0 0 10px !important;
  }
  #page-money .m-stats .card {
    margin: 0 !important;
    grid-column: auto !important;
    width: auto !important;
    padding: 12px 14px !important;
  }

  /* Stats, the log's own header and form, the subscriptions, then the two
     graphs, and the history last. */
  /* The header itself dissolves so its two children can go to opposite ends of
     the page: the title stays up with the form it now names, and the filters
     travel down to sit against the history they filter. */
  #page-money .m-log-head { display: contents !important; }

  #page-money .m-title-full  { display: none; }
  #page-money .m-title-short { display: block; }

  #page-money .m-log-title      { order: 2; }
  #page-money .form-grid--money { order: 3; }
  #page-money .m-recur          { order: 4; }
  #page-money .m-chart-day      { order: 5; }
  #page-money .m-chart-cat      { order: 6; }
  #page-money .m-history-head   { order: 7; }
  #page-money .tx-filter-row    { order: 8; }
  #page-money .tx-table-wrap    { order: 9; }

  /* Kinare se kinare, and no frames. !important throughout: padding,
     background, border and radius are inline style attributes on every one of
     these. */
  #page-money .m-log-title,
  #page-money .m-history-head,
  #page-money .form-grid--money,
  #page-money .m-recur,
  #page-money .m-chart-day,
  #page-money .m-chart-cat,
  #page-money .tx-table-wrap {
    margin-left: calc(-1 * var(--pad-page)) !important;
    margin-right: calc(-1 * var(--pad-page)) !important;
    margin-bottom: 0 !important;
    width: auto !important;
    background: none !important;
    border: none !important;
    border-radius: 0 !important;
    box-shadow: none !important;
    padding: 14px var(--pad-page) !important;
    border-top: 10px solid var(--feed-sep) !important;
    margin-top: 10px !important;
    padding-top: 10px !important;
  }

  /* The "Add Transaction Log" heading names the form directly under it, so the
     two belong together -- the 10px feed band between them is dropped, and the
     form sits straight below its heading with no dark strip in the seam. */
  #page-money .form-grid--money {
    border-top: none !important;
    margin-top: 0 !important;
    padding-top: 0 !important;
  }

  /* Two headings, one shape. The section titles on this page are 16.5px Space
     Grotesk; the history's is written from scratch, so it is given the same. */
  #page-money .m-history-head {
    display: block;
    font-family: 'Space Grotesk', system-ui, sans-serif;
    font-size: 16.5px;
    font-weight: 800;
    color: var(--text);
  }

  /* The line under it, the same shape the "Add Transaction Log" heading already
     carries over its form -- 11.5px dim prose saying what the list below is.
     It lives inside .m-history-head, so the desktop rule that hides that heading
     hides this with it. */
  #page-money .m-history-sub {
    font-family: system-ui, sans-serif;
    font-size: 11.5px;
    font-weight: 600;
    color: var(--text-dim);
    margin-top: 2px;
  }

  /* The filters sit directly under that heading rather than opening a 10px feed
     band of their own between the two -- but only the band goes. This rule used
     to say border-top:none, which also rubbed out the segmented track's OWN 1px
     top edge set further down, so the control was drawn on three sides and the
     line along its top was simply missing. margin-top alone closes the seam and
     the border is left to the track. */
  #page-money .tx-filter-row {
    margin-top: 0 !important;
    padding-top: 4px !important;
  }
  /* And the history follows the filters just as closely. */
  #page-money .tx-table-wrap {
    border-top: none !important;
    margin-top: 0 !important;
    padding-top: 4px !important;
  }

  /* ---- the history ends above the FAB ----
     Same geometry as Tasks: the FAB is a 60px circle whose top edge sits 118px
     above the bottom of the viewport, and this is now the last section on the
     page with a delete cross at the right end of every row. Without this the
     final row's cross is under it, and a mis-aimed tap opens the FAB instead of
     deleting the transaction. */
  #page-money .tx-table-wrap { padding-bottom: 76px !important; }

  /* ---- the three filters become one control ----

     They were three separate 20px-radius pills with a 6px gap -- three objects
     where there is one choice. The same shape the Tasks filters were given: one
     track, three pills inside it, each taking its share of the line.

     The glyphs go with it. "Expenses 💸" and "Income 📈" repeat their own
     labels, and the words are untouched underneath.

     The track gets a real surface, because it is drawn on --inner-box-bg and
     that variable references itself in the dark theme -- so it resolves to
     nothing and the track had no background at all. Dark only: the light theme
     defines it properly. */
  #page-money .tx-pill-icon { display: none; }
  #page-money .tx-filter-row {
    flex: 1 1 100%;
    gap: 4px !important;
    padding: 4px;
    border-radius: 10px;
    border: 1px solid var(--inner-box-border);
  }
  body:not(.light-theme) #page-money .tx-filter-row { background: var(--surface-bg); }
  #page-money .tx-filter-pill {
    flex: 1 1 auto;
    min-width: 0;
    min-height: 36px;
    border-radius: 7px !important;
    font-size: 11.5px !important;
    padding: 6px 4px !important;
    white-space: nowrap;
  }

  /* The Tasks control was measured for these: its track already matched, but
     its chosen pill is solid accent with black on it and neither pill carries
     a border, where these were a 15% tint with cyan text and a 1px edge on
     both. Three differences, so the two read as different controls.

     !important on all of it because filterTx writes background, border and
     colour as inline styles on every click -- the class it toggles is what
     these key off instead. */
  #page-money .tx-filter-pill {
    background: transparent !important;
    border: none !important;
    color: var(--text-dim) !important;
    font-weight: 700 !important;
  }
  #page-money .tx-filter-pill.active {
    background: var(--accent) !important;
    color: var(--on-accent, #000) !important;
    font-weight: 800 !important;
  }

  /* ---- the log entry form, three rows deep ----

     The 1024px tier collapses .form-grid to a single column, which put all five
     controls one under another -- 250px for one line of input. Two columns
     instead: the description spans both, then category beside type, then the
     amount beside the button.

     The tray around it is already gone: .form-grid--money is in the full-bleed
     rule above, which takes its background, border and radius off. */
  /* minmax(0,1fr), not 1fr: a grid track's default minimum is the item's
     min-content, and the category select's longest option is wider than half
     the line -- so the two columns came out 241 and 121, and the pair together
     overran the section by 8px. The same floor that made the Tasks two-column
     layout refuse to shrink. */
  #page-money .form-grid--money {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) !important;
    gap: 8px !important;
    align-items: center;
  }
  #page-money .form-grid--money > #txDesc { grid-column: 1 / -1; }
  #page-money .form-grid--money > * { min-width: 0; }

  /* The same fill the Tasks add button wears, and the same reasoning: this one
     was #00FFA3 -> #00E5FF with black text and a 16px accent glow behind it.
     White on the deeper fill measures 5.79:1 at one end and 8:1 at the other. */
  #page-money .form-grid--money .add-btn.money,
  #page-money .form-grid--money .add-btn.money:hover {
    background: linear-gradient(135deg, #00707F, #0B4EA2) !important;
    color: #fff !important;
    box-shadow: none !important;
    min-height: 40px;
    border-radius: 10px !important;
  }

  /* ---- the stat deck's two loose ends ----

     The badges had no radius at all: square corners on Money where Habits
     draws 3px and Tasks 6px, which is why they read as stuck-on labels rather
     than chips. 5px, as asked.

     And the carryover pencil comes out of the flow. It is a 36px control
     sitting inline inside "Carryover: ₹0", so that one line of 11px text was
     36px tall and made the two cards holding it 159px against the other two at
     121. Absolutely placed in the card's bottom-right corner instead -- the
     card is already position:relative for its watermark -- so the line collapses
     to its text height and the target stays 36px.

     Above the mini-bar, not on it: the bar is the last thing in the card and
     runs its full width, so the button would otherwise sit on its right end. */
  #page-money .m-stats .badge { border-radius: 3px; }  /* match the Habits chips */

  /* Tucked flush into the card's bottom-right corner (was floating at
     bottom:18px, halfway up the taller card). With it out of the way the deck
     is also tightened: the value sits closer under the badge and the mini-bar
     closer under the value, which is what brings the two carryover cards down
     from 136 toward the 119 of the other two -- "box ki size kam ho". */
  #page-money .m-stats .edit-inline-btn {
    position: absolute;
    right: 6px;
    bottom: 8px;
    min-width: 36px;
    min-height: 36px;
    margin: 0;
  }
  #page-money .m-stats #mMonthBalance,
  #page-money .m-stats #mToSavings,
  #page-money .m-stats #mIncome,
  #page-money .m-stats #mExpense { margin-top: 4px !important; }
  #page-money .m-stats .mini-bar { margin-top: 8px !important; }

  /* ---- recurring subscriptions ----

     The Vault badge goes: it names the section a second time, beside a heading
     that already reads "Recurring Subscriptions". */
  #page-money .m-vault { display: none; }

  /* The add row was three stacked lines, 138px, because .add-row sends both of
     its inputs to 100% on a phone -- which is right for the three-part forms it
     was written for and wrong for a name and a small amount.

     A grid rather than that flex row, because the placement asked for cannot be
     reached by wrapping: name and amount share the first line, and the button
     sits on the second under the amount. With flex the button would only drop
     to its own line if the first one happened to be full, and where it landed
     would depend on how wide the placeholders rendered. Explicit areas leave
     nothing to chance.

     The tray around it goes too, as everywhere else on this page; the controls
     keep their own edges. */
  #page-money .m-recur-add {
    display: grid !important;
    grid-template-columns: minmax(0, 1fr) 104px;
    gap: 8px !important;
    background: none !important;
    border: none !important;
    border-radius: 0 !important;
    padding: 0 !important;
  }
  #page-money .m-recur-add > #newRecurName { grid-area: 1 / 1; flex: none; width: auto !important; }
  #page-money .m-recur-add > #newRecurAmt  { grid-area: 1 / 2; flex: none; width: auto !important; }
  #page-money .m-recur-add > .add-btn      { grid-area: 2 / 2; flex: none; width: auto !important; }

  /* Same fill as the log button beside it and the Tasks button both are copied
     from -- this one was solid amber with black on it, a third treatment for
     the same kind of control. */
  #page-money .m-recur-add .add-btn.money,
  #page-money .m-recur-add .add-btn.money:hover {
    background: linear-gradient(135deg, #00707F, #0B4EA2) !important;
    color: #fff !important;
    box-shadow: none !important;
    min-height: 40px;
    border-radius: 10px !important;
  }

  /* ---- each logged subscription is a proper box ----

     The row's own surface was rgba(255,255,255,0.024) over a 5%-white border --
     barely there, so it did not read as a box at all. (--inner-box-bg-light is
     no help here: it is self-referential in the dark theme and resolves to
     nothing.) --surface-bg is concrete in both themes, and the inner-box border
     is the 12%-white edge the segmented track already uses, so a subscription
     now sits in a box the eye can find. */
  #page-money #recurList .recur-item {
    background: var(--surface-bg) !important;
    border: 1px solid var(--inner-box-border) !important;
    border-radius: 12px !important;
  }

  /* The "+ Log" button inside each row carried the .add-btn.money glow -- a
     16px cyan drop shadow behind a small green chip, the one shadow left on this
     page. It is dropped, and the button is given the same solid fill and white
     text as every other action button on the page, so the row's two controls
     are the log and the delete cross and nothing floats behind them. */
  #page-money #recurList .add-btn.money,
  #page-money #recurList .add-btn.money:hover {
    background: linear-gradient(135deg, #00707F, #0B4EA2) !important;
    color: #fff !important;
    box-shadow: none !important;
    border: none !important;
    min-height: 36px;
    padding: 7px 14px !important;
    font-size: 11.5px !important;
    font-weight: 800 !important;
    border-radius: 8px !important;
  }

  /* ---- the category donut sits centred ----
     .donut-wrap is a wrapping flex row; with no justification the 256px donut
     pinned to the left of the 345px card and left ~90px of dead space on its
     right. Centred, the donut sits in the middle of the card as asked, and the
     legend below it centres too. Scoped to this page so the Insights donut keeps
     its own layout. */
  #page-money .donut-wrap { justify-content: center; }

  /* ---- a transaction reads on one line ----

     The earlier attempt put the description on its own line and the date /
     category / amount on a second. What actually rendered was neither: a wrapping
     flex row 62px tall with the whole thing on one line and a lot of empty space
     under it, because display:flex quietly ignored the grid-template it was
     paired with. Asked for one clean line instead -- "ek line mein details".

     One nowrap flex row: the description takes the slack and truncates with an
     ellipsis when it is long, so it can never push the figures off the row; the
     date, category and amount follow it at their natural widths, and the delete
     cross ends the line. Height is now the 40px cross plus padding, not a second
     line of text. */
  #page-money #txTable tbody tr {
    display: flex !important;
    flex-wrap: nowrap !important;
    align-items: center;
    gap: 8px !important;
    padding: 6px 12px !important;
  }
  #page-money #txTable tbody td:nth-child(2) {           /* description */
    order: 1;
    flex: 1 1 auto;
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    font-size: 13px;
    font-weight: 600;
    line-height: 1.3;
  }
  #page-money #txTable tbody td:nth-child(1) {           /* date */
    order: 2;
    flex: none;
    font-size: 11px;
    color: var(--text-faint);
    white-space: nowrap;
    line-height: 1.2;
  }
  #page-money #txTable tbody td:nth-child(3) {           /* category */
    order: 3;
    flex: none;
    font-size: 11px;
    color: var(--text-faint);
    white-space: nowrap;
    line-height: 1.2;
  }
  #page-money #txTable tbody td:nth-child(4) {           /* amount */
    order: 4;
    flex: none;
    margin-left: 0 !important;
    font-size: 13px;
    font-weight: 800;
    white-space: nowrap;
    line-height: 1.2;
  }
  #page-money #txTable tbody td:nth-child(5) {           /* delete */
    order: 5;
    flex: none;
  }
  /* The empty-state row is one cell spanning the table and must not be placed
     into the layout above. */
  #page-money #txTable tbody td[colspan] {
    order: 0;
    flex: 1 1 100%;
    text-align: center;
  }

  /* ---- a week is one block ----

     Every row was its own card with an 8px gap under it, so a month of spending
     read as a loose stack of identical boxes with nothing saying which of them
     belong together. The rows carry their week on a class now (renderMoney works
     it out from the date), and the run sharing a week is joined into a single
     block: no gap between them, one border around the outside rather than two
     between every pair, and only the block's own top and bottom corners rounded.
     The gap moves to the seam between one week and the next, which is now the
     only break in the list -- so the list is read week by week. */
  #page-money #txTable tbody tr.wk-first,
  #page-money #txTable tbody tr.wk-mid,
  #page-money #txTable tbody tr.wk-last,
  #page-money #txTable tbody tr.wk-only {
    margin-bottom: 0 !important;
    border-radius: 0;
  }
  /* Rows after the first in a week drop their top edge, so the row above's
     bottom edge is the single line between the two. */
  #page-money #txTable tbody tr.wk-mid,
  #page-money #txTable tbody tr.wk-last { border-top: none; }

  #page-money #txTable tbody tr.wk-first {
    border-top-left-radius: 10px;
    border-top-right-radius: 10px;
  }
  #page-money #txTable tbody tr.wk-last {
    border-bottom-left-radius: 10px;
    border-bottom-right-radius: 10px;
  }
  #page-money #txTable tbody tr.wk-only { border-radius: 10px; }

  /* The break between weeks. */
  #page-money #txTable tbody tr.wk-last,
  #page-money #txTable tbody tr.wk-only { margin-bottom: 10px !important; }
}

/* ==========================================================================
   JOURNAL PAGE — phone interior                                 (Phase 10.5)

   Nothing clipped and no scrollers: the new-entry form uses .add-row, which
   10.3 taught to wrap, so it arrived here already fixed — the same way Money
   did. What was left is the goal list, which had the Tasks problem again.
   ========================================================================== */

@media (max-width: 640px) {

  /* .focus-goal is one flex line: title, a ~130px status pill, a delete button.
     The title has flex:1 and min-width:0, so it is the only part that gives —
     and at 317px it gave down to about 70px, breaking goal names mid-word
     ("Master Algorithm / ic & AI / Engineeri / ng"). The two controls are
     flex-shrink:0, so they never yield.

     Wrapping the row lets the title own a full line and drops the controls
     underneath it, delete pushed to the far edge. Same shape of fix as the
     task rows in 10.3, for the same reason: the row's text is the content and
     the controls were taking its width. The children carry no classes, so they
     are addressed by position — the fourth child is the absolutely positioned
     progress bar and is deliberately left alone. */
  .focus-goal { flex-wrap: wrap; }
  /* !important because the title block carries style="flex:1" inline, which no
     selector outranks — wrapping the row alone changed nothing until this. */
  .focus-goal > div:nth-child(1) { flex: 1 1 100% !important; }
  .focus-goal > div:nth-child(3) { margin-left: auto; }

  /* 30x30 with the size set inline, so !important is the only way through. */
  .focus-goal > div:nth-child(3) > button {
    width: 40px !important; height: 40px !important;
    font-size: 15px;
  }
  .focus-goal > div:nth-child(2) > button { min-height: 36px; }

  /* Four template pills at 132x30. They wrap to two rows of two, which keeps
     all four readable — the plan suggested a snap strip instead, but a strip
     hides options behind a scroll and there are only four. Height is the only
     thing wrong with them. */
  .journal-template-pill { min-height: 36px; }

  /* The vision card's "✎ Edit" was 35x14; it reuses the inline-edit target
     defined for Money's carryover pencil in 10.4. */

  /* ======================= Journal = feed =======================
     The sixth page to take the phone grammar: the stat deck goes 2x2, every
     section runs kinare se kinare, the frames come off and a 10px band separates
     one from the next. The two big cards (New Reflection Log + its stream, and
     Core Purpose) become children of the page so each takes its own band. */
  #page-journal.active { display: flex; flex-direction: column; }
  #page-journal > .j-feed { display: contents; }

  #page-journal .j-stats {
    display: grid !important;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
    order: 0;
    margin: 0 0 10px !important;
  }
  #page-journal .j-stats > .card {
    margin: 0 !important;
    grid-column: auto !important;
    width: auto !important;
    padding: 12px 14px !important;
  }
  /* Only the top row on the phone: Monthly Entries and Purpose Mastery. Data
     Drops and Reflective Velocity are hidden, not removed — the markup, the ids
     the render writes into, and the desktop's four-card row are all untouched. */
  #page-journal .j-stats > .card:nth-child(3),
  #page-journal .j-stats > .card:nth-child(4) { display: none !important; }

  /* Kinare se kinare, no frame. !important throughout: padding, background,
     border, radius and shadow are inline on these cards. */
  #page-journal .j-feed > .card {
    margin-left: calc(-1 * var(--pad-page)) !important;
    margin-right: calc(-1 * var(--pad-page)) !important;
    margin-bottom: 0 !important;
    width: auto !important;
    background: none !important;
    border: none !important;
    border-radius: 0 !important;
    box-shadow: none !important;
    padding: 14px var(--pad-page) !important;
    border-top: 10px solid var(--feed-sep) !important;
    margin-top: 10px !important;
    padding-top: 10px !important;
  }

  /* Action-button glow off, as on Money and Insights. */
  #page-journal .onb-btn,
  #page-journal .add-btn,
  #page-journal .onb-btn:hover,
  #page-journal .add-btn:hover { box-shadow: none !important; }

  /* ---- New Reflection Log, cleaned up ----
     The log card holds two things: the entry editor and the reflection stream.
     It dissolves so both become sections of the feed in their own right -- the
     editor stays near the top, the stream drops to the very bottom, below the
     goals -- and the editor's own inner frame (a box inside the card, which is
     what put the double left/right inset) comes off. */
  #page-journal .j-log-card { display: contents !important; }
  #page-journal .j-editor,
  #page-journal .j-stream {
    margin-left: calc(-1 * var(--pad-page)) !important;
    margin-right: calc(-1 * var(--pad-page)) !important;
    margin-bottom: 0 !important;
    width: auto !important;
    background: none !important;
    border: none !important;
    border-radius: 0 !important;
    box-shadow: none !important;
    padding: 14px var(--pad-page) !important;
    border-top: 10px solid var(--feed-sep) !important;
    margin-top: 10px !important;
    padding-top: 10px !important;
  }
  /* Order: stats, editor, goals, then the stream last. */
  #page-journal .j-editor { order: 1; }
  #page-journal .j-feed > .card:not(.j-log-card) { order: 2; }
  #page-journal .j-stream { order: 3; }

  /* The decorative emoji on the pills, the log button and the dropdowns are
     hidden on the phone (kept in the markup for desktop). */
  #page-journal .j-emoji { display: none !important; }

  /* Templates on one line: four pills share the track, no wrap, no glyph. */
  #page-journal #journalTemplateSelector { flex-wrap: nowrap !important; gap: 4px !important; }
  #page-journal .journal-template-pill {
    flex: 1 1 0 !important;
    min-width: 0 !important;
    padding: 7px 4px !important;
    white-space: nowrap;
  }

  /* Entry Type and Tag / Mindset on one line, no tray, no shadow, one radius. */
  #page-journal .j-type-row {
    background: none !important;
    border: none !important;
    padding: 0 !important;
    flex-wrap: nowrap !important;
    gap: 8px !important;
  }
  #page-journal .j-type-row > div { flex: 1 1 0 !important; min-width: 0 !important; }
  #page-journal #journalTypeSelect,
  #page-journal #journalMoodSelect {
    box-shadow: none !important;
    border-radius: 8px !important;
  }

  /* Log Reflection Entry: white text (glow already off above). */
  #page-journal .j-editor > .onb-btn { color: #fff !important; }

  /* The reflection-category filter (and the search beside it): no shadow, the
     shared 8px radius. */
  #page-journal #journalFilterType,
  #page-journal #journalSearchInput {
    box-shadow: none !important;
    border-radius: 8px !important;
  }

  /* ---- Goal add: no boxes, name on top, category + button under it ----
     The two nested trays come off; the name field takes the top line and the
     category select and the Add Goal button share the line under it. The button
     wears white text like the other action buttons. */
  #page-journal .j-goal-add-outer,
  #page-journal .j-goal-add-inner {
    background: none !important;
    border: none !important;
    border-radius: 0 !important;
    padding: 0 !important;
    margin-bottom: 0 !important;
  }
  #page-journal .j-goal-row {
    display: grid !important;
    grid-template-columns: 1fr auto;
    gap: 8px !important;
  }
  #page-journal .j-goal-row > #newPurposeInput { order: 0; grid-column: 1 / -1; min-width: 0 !important; flex: none !important; }
  #page-journal .j-goal-row > #newPurposeCat { order: 1; grid-column: 1; min-width: 0 !important; }
  #page-journal .j-goal-row > .add-btn { order: 2; grid-column: 2; color: #fff !important; }

  /* The category select takes the shared 10px radius, no shadow. */
  #page-journal #newPurposeCat { border-radius: 10px !important; box-shadow: none !important; }

  /* Log Reflection Entry and Add Goal wear the Tasks "Add Task" colour: the deep
     teal-to-blue fill (a bright cyan fails white-on-fill contrast) with the white
     text the rules above already set. */
  #page-journal .j-editor > .onb-btn,
  #page-journal .j-goal-row > .add-btn {
    background: linear-gradient(135deg, #00707F, #0B4EA2) !important;
  }

  /* A clear 10px gap on each side, between the Add Goal control and the goal
     history that follows it. */
  #page-journal .j-goal-add-inner { margin-bottom: 10px !important; }
  #page-journal #purposeList { margin-top: 10px !important; }

  /* ---- goal card, phone layout ----
     The goal name reads down the left, wrapping to as many lines as it needs. The
     category is shown as a small lettered circle (in place of the text badge),
     and it sits with the status pill and the delete button in a group at the top
     right — circle just past the name, then the pill, then the delete.

     The circle is the card's last child in the DOM; everything is placed with
     flex `order` here, and the delete is pulled back into the flow (it was
     absolutely pinned before). Nothing wraps: the name column gives, the controls
     do not. */
  #page-journal .focus-goal {
    flex-wrap: nowrap !important;
    align-items: flex-start !important;
    justify-content: flex-start !important;
    gap: 8px !important;
    padding: 12px 14px !important;
    position: relative;
  }
  /* Name column — the whole left, wrapping. Its text category badge is dropped
     (the circle replaces it). */
  #page-journal .focus-goal > div:nth-child(1) {
    order: 1;
    flex: 1 1 auto !important;
    min-width: 0;
    overflow: hidden;
    padding-right: 0 !important;
    gap: 0 !important;
  }
  #page-journal .focus-goal > div:nth-child(1) > div:first-child { display: none !important; }
  /* The name stays on one line and truncates, so the card is a tight single row
     rather than a tall column of squeezed, wrapped text. */
  #page-journal .focus-goal > div:nth-child(1) > div:last-child {
    font-size: 14px !important;
    line-height: 1.3 !important;
    margin-top: 0 !important;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  /* The right-hand group, top-aligned: circle, then pill, then delete. */
  #page-journal .focus-goal > .fg-cat-circle {
    display: flex !important;
    order: 2;
    flex-shrink: 0;
    margin-top: 1px;
  }
  #page-journal .focus-goal > div:nth-child(2) { order: 3; flex-shrink: 0; margin-top: 0 !important; }
  /* The status pill drops its word ("Queued"/"Active"/…) on the phone, keeping
     just the emoji, percent and caret, so the goal name gets more of the row. */
  #page-journal .focus-goal .fg-lvl-word { display: none; }
  #page-journal .focus-goal > div:nth-child(3) {
    order: 4;
    position: static !important;
    margin: 0 !important;
    flex-shrink: 0;
  }

  /* The reflection log reads as separate cards, not a run-together timeline: the
     dot-and-line rail comes off on the phone and each entry stands on its own. */
  #page-journal #journalList > div > div:first-child { display: none !important; }
  #page-journal #journalList > div { padding-bottom: 12px !important; }
}

/* ==========================================================================
   PROBLEMS PAGE — phone interior                                (Phase 10.6)

   The smallest phase so far, and the measurements are why: nothing is clipped,
   there are no scrollers, and the diagnostic form already stacks because its
   rows use repeat(auto-fit, minmax(280px, 1fr)) — the responsive kind Phase 7
   deliberately left alone. Only the controls were wrong.
   ========================================================================== */

@media (max-width: 640px) {

  /* The Yes / No / Sometimes answers are the page's main input and were 31px
     tall — one pixel under the bar, which is the sort of thing that only shows
     up when it is measured rather than looked at. */
  .radio-opt { min-height: 36px; }

  /* 26x26, destructive, and sitting immediately beside the ▼ that expands the
     case — the same adjacency that made Money's 11px delete worth fixing
     first. A tap meant for "open this case" should not be able to land on
     "delete it". */
  .case-del {
    min-width: 40px; min-height: 40px;
    font-size: 15px;
  }

  /* ======================= Problems = feed =======================
     Seventh page. Stat deck 2x2, the diagnostic form and the resolved-case file
     both run edge to edge with a band between, frames and the 0 12px 40px drop
     off. The q-field inputs keep their own soft shadow -- that is field depth,
     not the accent glow the other pages shed. */
  #page-problems.active { display: flex; flex-direction: column; }
  #page-problems > .p-feed { display: contents; }

  #page-problems .p-stats {
    display: grid !important;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
    order: 0;
    margin: 0 0 10px !important;
  }
  #page-problems .p-stats > .card {
    margin: 0 !important;
    grid-column: auto !important;
    width: auto !important;
    padding: 12px 14px !important;
  }
  /* Top row only: Logged Cases and Avg Bandwidth Drain. Neutralization Rate and
     System Diagnostic are hidden on the phone, same as Journal's pair — still
     rendered, still there on the desktop. */
  #page-problems .p-stats > .card:nth-child(3),
  #page-problems .p-stats > .card:nth-child(4) { display: none !important; }

  #page-problems .p-feed > .card {
    margin-left: calc(-1 * var(--pad-page)) !important;
    margin-right: calc(-1 * var(--pad-page)) !important;
    margin-bottom: 0 !important;
    width: auto !important;
    background: none !important;
    border: none !important;
    border-radius: 0 !important;
    box-shadow: none !important;
    padding: 14px var(--pad-page) !important;
    border-top: 10px solid var(--feed-sep) !important;
    margin-top: 10px !important;
    padding-top: 10px !important;
  }

  /* Action-button glow off. */
  #page-problems .onb-btn,
  #page-problems .add-btn,
  #page-problems .onb-btn:hover,
  #page-problems .add-btn:hover { box-shadow: none !important; }

  /* The Log Diagnostic Case button drops its emoji on the phone. */
  #page-problems .p-emoji { display: none; }

  /* ...and its label goes black. The button is the page's one pink control —
     a magenta-to-violet gradient — and it carried color:#FFF inline, which on
     that gradient is the lighter of the two rather than the label. Everything
     else this tier paints on a solid accent is already black; see the chat
     send button above and text-on-accent generally. Phone only: the desktop
     button is unchanged, which is why this lives here and not on the element.
     The !important is not decoration — the colour it beats is an inline
     style. */
  #page-problems .onb-btn[onclick*="logProblemCase"] { color: var(--on-accent, #000) !important; }

  /* ---- the 8-step diagnostic: no boxes, numbered, in order ----
     Each step sat in its own q-field frame, and the three-column grid put them
     out of sequence on the phone (01,02,04 / 05,07,08 / 03,06). The frames come
     off, the grids dissolve, and each step is placed by its number so they read
     01..08 down the page, with the Log button last. */
  body #page-problems .p-diag-wrap .q-field {
    background: none !important;
    border: none !important;
    border-radius: 0 !important;
    box-shadow: none !important;
    padding: 0 !important;
    margin: 0 !important;
  }
  #page-problems .p-diag-grid { display: contents !important; }
  #page-problems .q-field:has(#diag1) { order: 1; }
  #page-problems .q-field:has(#diag2) { order: 2; }
  #page-problems .q-field:has(#diagPatternSelector) { order: 3; }
  #page-problems .q-field:has(#diag4) { order: 4; }
  #page-problems .q-field:has(#diag5) { order: 5; }
  #page-problems .q-field:has(#diagAuthoritySelector) { order: 6; }
  #page-problems .q-field:has(#diag7) { order: 7; }
  #page-problems .q-field:has(#diag8) { order: 8; }
  #page-problems .q-field:has(button[onclick*="logProblemCase"]) { order: 9; }

  /* Each answer field is one line to start and grows with its text (the grow is
     wired on the textareas with autoResizeTextarea). */
  #page-problems .onb-textarea { min-height: 38px !important; }

  /* ---- resolved case files: count up by the heading, search and filter below ----
     The count chip moves up beside the (shortened) heading, and the search box
     and the category filter share the line under it at equal width. The filter
     loses its shadow and takes the 10px radius. */
  #page-problems .p-word { display: none; }
  #page-problems .p-full { display: none; }
  #page-problems .p-short { display: inline; }
  #page-problems .p-controls { display: contents !important; }
  #page-problems .p-resolved-head { flex-wrap: wrap; align-items: flex-start; }
  #page-problems .p-resolved-head > div:first-child { flex: 1 1 auto; }
  #page-problems #problemCaseBadge { order: 1; align-self: flex-start; }
  #page-problems .p-controls > div:has(#problemSearchInput) {
    order: 2;
    flex: 1 1 calc(50% - 10px) !important;
    width: auto !important;
    min-width: 0;
  }
  #page-problems #problemFilterSeverity {
    order: 3;
    flex: 1 1 calc(50% - 10px) !important;
    min-width: 0;
    box-shadow: none !important;
    border-radius: 10px !important;
  }

  /* The placeholder (reference text) is smaller and lighter, and kept to one
     line, so it takes about half the room it did -- the value you type stays the
     field's normal size.

     11px, up from 9.5: at 9.5 it read as fine print rather than a hint. The
     ceiling was measured rather than guessed — the longest of the six, "Describe
     symptom, inefficiency, or friction behavior...", renders 277px against 326px
     of field at this size, so it still lands on one line with room to spare on
     a narrower phone. (12px fits at 375px too, with only 23px left, which is
     gone by 360.) */
  #page-problems .p-diag-wrap .onb-textarea::placeholder,
  #page-problems .p-diag-wrap .onb-input::placeholder {
    font-size: 11px;
    opacity: 0.5;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  /* Every step number wears the same magenta as step 02, instead of a different
     colour each. */
  #page-problems .p-diag-wrap .num {
    background: rgba(255, 62, 160, 0.15) !important;
    color: var(--magenta) !important;
  }

  /* The "Lock Audit / System Protocol" label above the Log button comes off. */
  #page-problems .q-field:has(button[onclick*="logProblemCase"]) > label {
    display: none !important;
  }

  /* The stat labels (one is "Neutralization Rate") were spilling past the card
     on the 2x2 deck. Smaller icon, tighter gap, a 10px label that can wrap. */
  #page-problems .p-stats > .card { gap: 10px !important; }
  #page-problems .p-stats > .card > div:nth-child(2) { width: 36px !important; height: 36px !important; }
  #page-problems .p-stats > .card > div:nth-child(3) { min-width: 0 !important; }
  #page-problems .p-stats > .card > div:nth-child(3) > div:first-child {
    font-size: 10px !important;
    letter-spacing: 0.2px !important;
    overflow-wrap: anywhere;
  }

  /* The eight steps sit closer together, their question headings are a little
     bigger, and the Log button is pulled up tight under the last one. */
  #page-problems .p-diag-wrap { gap: 8px !important; }
  #page-problems .p-diag-wrap .onb-textarea,
  #page-problems .p-diag-wrap .onb-input { margin-bottom: 0 !important; }
  #page-problems .p-diag-wrap .q-field > label { font-size: 13.5px !important; margin-bottom: 6px !important; }
  #page-problems .p-diag-wrap .q-field:has(button[onclick*="logProblemCase"]) { margin-top: -2px; }
}

/* ==========================================================================
   ATTACK PAGE — phone interior                                  (Phase 10.7)

   Two controls on this page could not be pressed at all, in two different
   ways: one ran off the right edge, and one sat underneath the floating
   action button. The second is not specific to this page — see below.
   ========================================================================== */

@media (max-width: 640px) {

  /* Start / Reset / Mission Mode is a nowrap flex row of 102 + 81 + 165px plus
     gaps, so Mission Mode's right edge landed at 411px on a 390px screen and
     the last 21px were clipped away. Wrapping puts it on its own line; the
     inline flex ratios still divide the first line between Start and Reset. */
  .pomo-actions { flex-wrap: wrap; }
  .pomo-actions > .onb-btn.secondary:last-child { flex: 1 1 100%; }

  /* 25px tall, and this is how the timer's mode is chosen. */
  .pomo-tabs button { min-height: 36px; }

  /* The duration presets wrap to a second line where the last one is a single
     full-width button at 30px. */
  #page-attack .onb-btn { min-height: 36px; }

  /* The floating action button is fixed at bottom:58px and is 58px tall, so it
     covers a band 116px up from the bottom of the viewport. On most pages that
     is survivable: they are 2,400-5,200px long, so scrolling moves anything out
     from under it. Attack is 793px in an 844px viewport — it does not scroll at
     all, and its "45m Deep Work" preset sat permanently underneath the FAB and
     could never be tapped. Measured, not inferred: the two rects overlap.

     Padding the page bottom does not help, because the preset is mid-page
     rather than last; the FAB is positioned against the viewport, not the
     content. Hiding it here does, and it suits the page: this is the timer, and
     the plan already wants less chrome on it. Every other page keeps the FAB,
     and it stays on Attack above 640px.

     Note the 11.1 reachability check does not catch this class of bug — it
     looks for content past the viewport edge, not content covered by a fixed
     element. Recorded in the plan as a second check worth having.

     !important because .fab-container carries display:flex inline — the fifth
     distinct instance of that pattern in this project. */
  body:has(#page-attack.active) .fab-container { display: none !important; }

  /* ======================= Attack = feed =======================
     Eighth page, and the shortest -- no stat deck, just the timer and the
     session card. They dissolve out of their centred bento and run edge to edge
     with a 10px band between, frames and drop shadow off, the timer's tab glow
     and the button glow off. */
  #page-attack.active { display: flex; flex-direction: column; }
  #page-attack > .a-feed { display: contents; }
  #page-attack .a-feed > .card {
    margin-left: calc(-1 * var(--pad-page)) !important;
    margin-right: calc(-1 * var(--pad-page)) !important;
    margin-bottom: 0 !important;
    max-width: none !important;
    width: auto !important;
    background: none !important;
    border: none !important;
    border-radius: 0 !important;
    box-shadow: none !important;
    padding: 14px var(--pad-page) !important;
    border-top: 10px solid var(--feed-sep) !important;
    margin-top: 10px !important;
    padding-top: 10px !important;
  }
  /* The first section opens the page, so it needs no band above it. */
  #page-attack .a-feed > .card:first-child {
    border-top: none !important;
    margin-top: 0 !important;
    padding-top: 10px !important;
  }

  /* Glow off: the active timer tab and the action buttons. */
  #page-attack .onb-btn,
  #page-attack .onb-btn:hover,
  #page-attack .pomo-tab.active { box-shadow: none !important; }

  /* The "Deep Work Mode" chip in the Focus Engine header comes off. */
  #page-attack .badge { display: none !important; }

  /* Start and Reset swap places: Reset takes the left, Start the right; Mission
     Mode stays on its own line below (kept by the :last-child rule above). And
     the emoji on Reset and Mission are hidden on the phone. */
  #page-attack .a-emoji { display: none !important; }
  #page-attack .pomo-actions > button[onclick*="resetPomoTimer"] { order: 1; }
  #page-attack #pomoStartBtn { order: 2; }
  #page-attack .pomo-actions > button[onclick*="startARFocus"] { order: 3; flex: 1 1 100% !important; }

  /* Start Session wears the Tasks "Add Task" colour — the deep teal-to-blue fill
     with white text — instead of the accent fill with dark text. */
  #page-attack #pomoStartBtn {
    background: linear-gradient(135deg, #00707F, #0B4EA2) !important;
    color: #fff !important;
  }
}

/* ==========================================================================
   HALL OF FAME — phone interior                                 (Phase 10.8)
   ========================================================================== */
#page-leaderboard .lb-short { display: none; }

@media (max-width: 640px) {

  /* 23-24px tall. */
  .badge-filter-btn { min-height: 36px; }

  /* ======================= Hall of Fame = feed =======================
     The ninth page to take the phone grammar. The stat deck goes 2x2, the two
     view cards (badges and community) run edge to edge with a 10px band, frames
     and drop shadows off, and the two tabs are shortened and share one line. */
  #page-leaderboard.active { display: flex; flex-direction: column; }
  #page-leaderboard > #leaderboardBadgesView,
  #page-leaderboard > #leaderboardCommunityView,
  #page-leaderboard #leaderboardBadgesView > .bento,
  #page-leaderboard #leaderboardCommunityView > .bento { display: contents; }

  #page-leaderboard .lb-stats {
    display: grid !important;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
    margin: 0 0 10px !important;
  }
  #page-leaderboard .lb-stats > .card {
    margin: 0 !important;
    grid-column: auto !important;
    width: auto !important;
    padding: 12px 14px !important;
  }
  /* The left column goes on the phone — Mastery Rank (1) and Productivity Index
     (3) are the two that sat on the left of the 2x2. What is left is Badges
     Unlocked and Community Rank, which were the right-hand pair; with the other
     two out of the grid they close up into the top row on their own. No
     repositioning rule needed for that — taking a grid item out of flow is what
     moves the rest up. Hidden, not removed. */
  #page-leaderboard .lb-stats > .card:nth-child(1),
  #page-leaderboard .lb-stats > .card:nth-child(3) { display: none !important; }

  /* The two big view cards, full-bleed. */
  #page-leaderboard #leaderboardBadgesView > .bento > .card,
  #page-leaderboard #leaderboardCommunityView > .bento > .card {
    margin-left: calc(-1 * var(--pad-page)) !important;
    margin-right: calc(-1 * var(--pad-page)) !important;
    margin-bottom: 0 !important;
    width: auto !important;
    background: none !important;
    border: none !important;
    border-radius: 0 !important;
    box-shadow: none !important;
    padding: 14px var(--pad-page) !important;
    border-top: 10px solid var(--feed-sep) !important;
    margin-top: 10px !important;
    padding-top: 10px !important;
    /* Switching tabs toggles the view from hidden to shown, which replays the
       card's cardIn slide — everything below the stat deck lurches up on every
       tap. The switcher now lives inside these cards, so kill the entrance. */
    animation: none !important;
  }

  /* ---- the switcher ----
     The long words are hidden on the phone (kept for desktop), and the two tabs
     share the track instead of stacking. The switcher itself is moved by JS into
     the active feed card (below its 10px band), so the old header row — which
     also carried the category filter pills — is dropped whole on the phone. */
  #page-leaderboard .lb-full { display: none; }
  #page-leaderboard .lb-short { display: inline; }
  #page-leaderboard .lb-tabs-row { display: none !important; }
  #page-leaderboard .lb-seg {
    display: flex !important;
    width: 100%;
    margin-bottom: 14px;
  }
  #page-leaderboard .lb-seg > .lb-tab-btn {
    flex: 1 1 0 !important;
    min-width: 0;
    padding: 9px 6px !important;
    justify-content: center;
    text-align: center;
    /* Match the Journal template segmented control. Those pills set no
       font-family, so they render in the default button font; revert drops this
       button back to that same font. Size and weight match too (11.5px / 700).
       Active label is black; inactive stays on the base dim colour so it does
       not vanish on the dark track. */
    font-family: revert !important;
    font-size: 11.5px !important;
    font-weight: 700 !important;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  #page-leaderboard .lb-seg > .lb-tab-btn.active { color: var(--on-accent, #000) !important; }

  /* Community arena ab phone par live hai. Saat column ki table 390px par
     samati nahi, isliye phone par wo chhup jaati hai aur #communityRankCards
     wahi rows card ki shakl mein dikhata hai — data ek hi hai, sirf shakl do.
     Podium teen tall cards ban jaata tha (minmax 240px = 390px par ek column),
     to yahan teenon ek line mein aa jaate hain. */
  #page-leaderboard #communityRankTable { display: none; }
  #page-leaderboard #communityRankCards {
    display: flex; flex-direction: column; gap: 8px;
    border: none; background: none; border-radius: 0;
  }
  #page-leaderboard #communityPodium {
    grid-template-columns: repeat(3, 1fr) !important;
    gap: 8px !important;
  }
  #page-leaderboard #communityPodium .card { padding: 12px 6px !important; }


  /* The participation toggle was invisible in light mode (white track on a light
     ground). A cyan-tinted track and dot read in both themes. */
  #lbToggleTrack {
    background: rgba(var(--cyan-rgb), 0.18) !important;
    border-color: rgba(var(--cyan-rgb), 0.55) !important;
  }
  #lbToggleThumb { background: var(--cyan) !important; }

  /* Action-button glow off. */
  #page-leaderboard .onb-btn,
  #page-leaderboard .onb-btn:hover { box-shadow: none !important; }

  /* The arena buttons (Push My Rank, Refresh, Turn ON Participation) drop their
     emoji on the phone. */
  #page-leaderboard .lb-emoji { display: none; }
}

/* ==========================================================================
   75-DAY CHALLENGE ARENA — phone                                (Phase 10.8)

   The plan files this under Hall of Fame; it actually lives on Habits, inside
   #separateChallengeArena, and renders only while a 75-day challenge is
   active. That is why 10.2 did not see it and why the 11.1 check does not
   report it — the markup is not in the DOM unless a challenge is running.
   Found by activating one deliberately.

   With a challenge active it is genuinely broken: the grid's column is pinned
   to min-width:480px inline, so at 390px the right side of a 75-box matrix is
   clipped with nothing to scroll it.

   The boxes are display-only (no handlers, just a title), and the header next
   to them says "15 Days per Row" — so the answer is to let the row scroll
   rather than to reflow it into fewer columns, which would make that label a
   lie and the shape unreadable. Cells stay at their 26px so the day numbers
   remain legible.
   ========================================================================== */

/* Reversed on 22 Aug 2026, and worth saying why rather than quietly deleting.

   The reasoning above was: keep the fifteen columns, let the row scroll, because
   reflowing would make the "15 Days per Row" label a lie. That held the label
   true and made the matrix false — you swiped sideways to count days, and a
   75-day board you cannot see is not a board.

   Asked for directly: all 75 on one screen, ten across. That is eight rows, the
   last one short, and at 390px full bleed each box lands near 33px. The label is
   hidden on the phone instead — it is still right on the desktop, which still
   shows fifteen. */
@media (max-width: 640px) {
  #separateChallengeArena div:has(> [id^="arenaGrid-"]) {
    min-width: 0 !important;
    overflow-x: visible;
    -webkit-mask-image: none;
    mask-image: none;
  }
  #separateChallengeArena [id^="arenaGrid-"] {
    min-width: 0;
    grid-template-columns: repeat(10, 1fr);
    gap: 4px;
  }
}

/* ==========================================================================
   SETTINGS PAGE — phone interior                                (Phase 10.9)

   The last page named in REACHABILITY_TODO, and it had the second-worst clip
   in the app: the visibility manager's right-hand column ran to 450px on a
   390px screen, taking four of the eight page toggles off the edge with it —
   Money, Journal, Attack and Hall of Fame could not be switched off at all
   from a phone.

   The two modals the plan lists here (Edit Profile, Manage Categories) were
   already docked as sheets in 9.3 and measure clean. What the plan does not
   mention is what the screenshots showed on top of them — see the FAB rule at
   the bottom of this file.
   ========================================================================== */

@media (max-width: 640px) {

  /* .grid-2up is repeat(2,1fr), and 1fr's automatic minimum is the item's
     min-content — here a 36px icon, a 12px gap and "Money Intelligence" on one
     line, which comes to ~200px. Two of those plus the gap is 415px in a 315px
     card, so the second column was clipped away entirely rather than shrunk.
     One row per page is the natural shape on a phone anyway.

     Scoped to this page: the same class holds Insights' 2-up stat tiles, which
     are 90px wide and correct as they are. */
  #page-settings .grid-2up { grid-template-columns: 1fr; }
  /* One column was not enough on its own. The last card carries
     style="grid-column: span 2" inline, and an item spanning two columns of a
     one-column grid does not shrink to fit — it creates an *implicit* second
     track, auto-sized, and the layout came back at 202px + 201px unchanged.
     The computed value read "1fr" the whole time; only measuring the tracks
     showed it. Nothing here needs to span anything once the grid is a list. */
  #page-settings .grid-2up > * { grid-column: auto !important; }

  /* ---- theme swatches ----
     20 swatches, one per row, 1468px of the page's 2940 — over a third of the
     page spent on a colour picker you have to scroll past to reach anything
     below it. auto-fill/minmax(180px,1fr) is why: 180px does not fit twice in
     316px, so it resolves to a single column.

     !important because renderThemeSwatches writes the track inline, and the
     card's own padding and gap are inline on every swatch too. minmax(0,1fr)
     rather than 1fr for the same reason the grid above was broken. */
  /* ---- colours as dots, seven to a line ----
     The picker was 20 name+hex cards, one third of the page. On the phone the
     name and hex go, the card frame comes off, and the swatches become plain
     circles laid seven to a line -- click a colour to pick it (the whole swatch
     keeps its onclick). 12 colours (13 Sept 2026) -> two rows, 7 + 5. */
  #themeSwatchesGrid {
    grid-template-columns: repeat(7, 1fr) !important;
    gap: 10px 8px !important;
  }
  #themeSwatchesGrid > div {
    padding: 0 !important;
    gap: 0 !important;
    background: none !important;
    border: none !important;
    justify-content: center;
  }
  #themeSwatchesGrid > div > div:last-child { display: none !important; }
  #themeSwatchesGrid .color-circle { width: 34px !important; height: 34px !important; }

  /* ---- toggles ----
     Nine of them (eight pages plus dark/light), all 44x24 with the size inline.
     Growing the pill would mean redrawing the knob offset, which is also
     inline, so the target grows instead of the control: a transparent overlay
     8px past the top and bottom edges takes it to 44x40. Every one of these
     already carries position:relative inline, which is what the overlay needs. */
  #page-settings [id^="toggle_vis_"]::after,
  #page-settings #themeModeToggle::after {
    content: '';
    position: absolute;
    inset: -8px -4px;
  }

  /* Profile / Export / Restore / Factory Reset, laid out by inline padding at
     27-29px tall. min-height raises the box around that padding, so it needs no
     !important. */
  #page-settings .onb-btn { min-height: 36px; }

  /* "Clear Cache & Update App" is an underlined text button with padding:0
     inline — 179x15, the smallest control on the page, and the one that throws
     the service worker away. It has no class, so it is addressed by its
     handler. inline-flex is what lets min-height centre the label instead of
     just padding the box downwards. */
  #page-settings button[onclick^="forceAppUpdate"] {
    display: inline-flex;
    align-items: center;
    min-height: 36px;
  }

  /* ======================= Settings = feed =======================
     The tenth and last page. The top cards go two by two; every section runs
     edge to edge with a 10px band, frames off. Card shadow was already off. */
  #page-settings.active { display: flex; flex-direction: column; }
  #page-settings > .s-feed { display: contents; }

  #page-settings .s-stats {
    display: grid !important;
    grid-template-columns: 1fr 1fr;
    grid-auto-rows: 1fr !important;
    gap: 8px;
    margin: 0 0 10px !important;
  }
  #page-settings .s-stats > .card {
    margin: 0 !important;
    grid-column: auto !important;
    width: auto !important;
    padding: 12px 14px !important;
  }
  #page-settings .s-stats > .card > div:first-child > div:first-child {
    flex-wrap: nowrap !important;
    gap: 4px;
  }
  #page-settings .s-stats > .card > div:first-child > div:first-child > span:first-child {
    font-size: 10px !important;
    letter-spacing: 0.4px !important;
  }
  #page-settings .s-stats .badge {
    display: none !important;
  }
  #settingsVaultCount {
    font-size: 20px !important;
  }

  /* The four cards were already the same size — what looked uneven was what is
     inside them. At 169px wide "Clear Cache & Update App" and "FACTORY RESET
     DATA" each wrapped to two lines and Data Backup's description to three, so
     one card read as cramped next to another with space to spare. Trimming the
     type stops the wraps; space-between then distributes what is left the same
     way in all four. Nothing is renamed — the words are the owner's. */
  #page-settings .s-stats > .card {
    justify-content: space-between !important;
  }
  #page-settings .s-stats > .card .onb-btn,
  #page-settings .s-stats > .card button {
    font-size: 9.5px !important;
    letter-spacing: 0 !important;
    padding: 7px 4px !important;
    line-height: 1.25 !important;
  }
  #page-settings .s-stats > .card p,
  #page-settings .s-stats > .card div[style*="font-size:10.5px"],
  #page-settings .s-stats > .card div[style*="font-size:11px"] {
    font-size: 9.5px !important;
    line-height: 1.3 !important;
  }

  #page-settings .s-feed > .card,
  #page-settings > .mobile-only.card {
    margin-left: calc(-1 * var(--pad-page)) !important;
    margin-right: calc(-1 * var(--pad-page)) !important;
    margin-bottom: 0 !important;
    width: auto !important;
    background: none !important;
    border: none !important;
    border-radius: 0 !important;
    box-shadow: none !important;
    padding: 14px var(--pad-page) !important;
    border-top: 10px solid var(--feed-sep) !important;
    margin-top: 10px !important;
    padding-top: 10px !important;
  }
  /* The Active Month card opens the page, so no band above it. */
  #page-settings > .mobile-only.card:first-of-type {
    border-top: none !important;
    margin-top: 0 !important;
  }

  /* Action-button glow off. */
  #page-settings .onb-btn,
  #page-settings .onb-btn:hover { box-shadow: none !important; }

  /* ---- requested phone edits ----
     The Active Month card and the Display Mode card are dropped on the phone:
     month switching already lives on Today's grid picker, and the light/dark
     switch moves into the header bar (below). Desktop keeps both — hidden here,
     not deleted. */
  #settingsActiveMonthCard,
  #settingsThemeCard,
  #settingsSocialCard { display: none !important; }

  /* Emoji stripped from the stat-card buttons; the Vault "Live Firebase & Local
     Storage" line and the "Custom Workspace" tag are dropped too. */
  #page-settings .s-emoji,
  #page-settings .s-vault-note,
  #page-settings .s-custom-workspace { display: none !important; }

  /* The small tag box in each stat card's top-right corner: a tight 3px radius. */
  #page-settings .s-stats .badge { border-radius: 3px !important; }

  /* The light/dark switch rides in the header bar, but only on Settings, and as
     an SVG icon rather than the desktop emoji. */
  body:has(#page-settings.active) #headerThemeModeBtn { display: flex !important; }
  #headerThemeModeBtn .tm-ico-emoji { display: none; }
  #headerThemeModeBtn .tm-ico-svg {
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }

  /* A foot of clearance so the last section never sits under the FAB. */
  #page-settings.active { padding-bottom: 96px; }

  /* ---- the two settings sheets ----
     Docked correctly since 9.3 and nothing is clipped inside them; only the
     controls are small. The delete buttons are the reason this is not cosmetic:
     73x26, one per category row, and they are the destructive control — the
     same pairing 10.4 fixed on the money rows. Addressed by handler because
     renderManageCategoriesList writes them without a class. */
  #manageCategoriesModal button[onclick^="deleteCategoryConfirm"],
  #manageCategoriesModal button[onclick^="closeManageCategoriesModal"] {
    min-height: 36px;
  }
  /* Both sheets' ✕ measured 20x30 and 24x34. */
  #editProfileModal button[onclick^="closeEditProfileModal"],
  #manageCategoriesModal > .card > div:first-child > button {
    min-width: 36px; min-height: 36px;
  }
}

/* ==========================================================================
   THE FAB SITS ON TOP OF OPEN MODALS                            (Phase 10.9)

   Found in the 10.9 screenshots rather than in any measurement: the FAB is
   z-index 9000 and every modal overlay is 120-150, so it floats above them.
   On the docked category sheet it covers the third row's Delete button
   outright, and on Edit Profile it hovers over the form beside Save.

   No check catches this. 11.1 only looks for content outside the viewport, and
   an occlusion check (11.6) would still have to be told to open the modals
   first — this is the same class of bug as Attack's preset under the FAB in
   10.7, which was also found by looking.

   Not scoped to the phone tier, because nothing about it is: the FAB opens a
   second modal on top of the first anywhere it is pressed. #fabModal is 10000
   and covers the button itself, so its own dialog is unaffected. The desktop
   baseline captures pages with no modal open, so this changes no pixel there.
   ========================================================================== */

body.modal-open .fab-container { display: none !important; }

/* ==========================================================================
   THE FAB — phone size and clearance                          (requested)

   The button is position:fixed in its own right (.fab-btn, bottom:58px), so its
   offset is a fixed number that ignores the notch inset the bottom bar pads
   itself by. On a device with a home-indicator the bar grows by that inset and
   the FAB, pinned low, ends up sitting on the bar's right tab.

   Phone only, and the offset is expressed against the bar so it tracks it: bar
   height + safe inset + a 16px gap, which lifts the button clear of the bar on
   every device. The button is a little smaller too, as asked. Desktop keeps the
   inline 60px at .fab-btn's own bottom, untouched.
   ========================================================================== */
@media (max-width: 640px) {
  #mainFabBtn {
    position: relative !important;
    right: auto !important;
    bottom: auto !important;
    width: 52px !important;
    height: 52px !important;
    font-size: 22px !important;
  }
  .fab-container {
    bottom: calc(var(--bottom-nav-h) + var(--safe-bottom) + 20px) !important;
    right: 20px !important;
  }

  /* Foot clearance for the raised FAB. Money and Insights already reserve room
     under their last section; Hall of Fame, Journal and Problems did not, so the
     FAB overhung their last card at the end of the scroll. Challenges joined
     them when it became its own page — the FAB sat on the last trophy card. */
  #page-leaderboard.active,
  #page-journal.active,
  #page-problems.active,
  #page-challenges.active { padding-bottom: 84px; }
}

/* ==========================================================================
   INSIGHTS PAGE — phone interior                               (Phase 10.10)

   The last page, and the plan's section for it is the one that ages worst.
   Measured at 390px: nothing is clipped, there are no horizontal scrollers,
   nothing is covered at max scroll, and the radar draws 277px with its labels
   clear - so "smallest useful 260px, labels chhote karo" describes a problem
   this page does not have.

   The plan's headline item was lazy chart rendering ("optional nahi hai").
   Measured over five runs each, a full goPage() render costs:

       today 38-46ms   habits 68-151ms   money 16-33ms   insights 30-37ms

   Insights is the *third* cheapest of the four - 392 elements, seven inline
   SVGs, 87 nodes between them, no Chart.js on this page at all. Lazy rendering
   would add an observer and a render-once flag to save nothing measurable, so
   it is deliberately not done. If this page ever gains a canvas chart, measure
   again before assuming that still holds.

   What was actually wrong: two dead bands under the charts, controls at 29px,
   and two bugs that are not layout at all - see app.js (the reflection box was
   rendering "[object Object]", and the savings ring's label had escaped its
   own svg).
   ========================================================================== */

@media (max-width: 640px) {

  /* Both containers are height:220px inline while .svgchart svg is
     width:100%;height:auto over a 560x190 viewBox - so at 317px wide the chart
     draws 107px and 113px of the card is empty. Twice on this page, 226px of a
     5240px scroll spent on nothing. Same shape as #moneyDayChart in 10.4, but
     the height is set rather than reserved, so it takes height:auto and an
     !important to beat the inline declaration. */
  #page-insights #habitWeekChart,
  #page-insights #habitTrendChart { height: auto !important; }

  /* The four reflection tag pills measured 29px and "Export PDF Report" 35px.
     .onb-btn here is laid out by inline padding, so min-height lifts the box
     without fighting anything. "Generate Fresh Audit" is already 36. */
  #page-insights .onb-btn { min-height: 36px; }

  /* The savings ring is a 100x100 svg in a flex row; it was being squeezed to
     60px wide, which letterboxes a square viewBox and shrinks the ring inside
     its own box. It has nothing to gain from shrinking - the text beside it
     wraps perfectly well. */
  #moneyPulsePanel > div:first-child > svg { flex-shrink: 0; }

  /* ---- the four KPI cards stand two by two ----

     They were four rows of one, 575px of a 5134px page spent on four figures.
     Tasks and Money both put their stat deck 2x2 and this is the same deck.

     Both declarations on the card are needed, for the reason the other two
     decks record: the 1024px tier gives every .s3 grid-column: span 12 and
     width: 100%, and clearing only one of them leaves the cards stacked. */
  #page-insights .i-stats {
    display: grid !important;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
    margin-bottom: 10px !important;
  }
  #page-insights .i-stats > .card {
    margin: 0 !important;
    grid-column: auto !important;
    width: auto !important;
    padding: 12px 14px !important;
  }

  /* ---- every section runs edge to edge, no frame ----

     The fifth page to get the feed grammar. It was 14 framed cards, each with
     an 18px radius and 22px of its own padding sitting inside the page's 14px
     gutter -- a box inside a box, eleven times down a 5134px scroll. The frames
     come off and a 10px band separates one section from the next, the same rule
     Money carries.

     display:contents on the .i-feed wrappers dissolves the bento rows so each
     card becomes a child of the page and takes the band itself; the paired rows
     (the two charts, the two pulses) were already one-under-another on the
     phone, so nothing moves -- only the frames between them change to bands.

     !important throughout: padding, background, border and radius are inline
     style attributes on every one of these cards. */
  #page-insights .i-feed { display: contents; }
  #page-insights .i-feed > .card {
    margin-left: calc(-1 * var(--pad-page)) !important;
    margin-right: calc(-1 * var(--pad-page)) !important;
    margin-bottom: 0 !important;
    width: auto !important;
    background: none !important;
    border: none !important;
    border-radius: 0 !important;
    box-shadow: none !important;
    padding: 14px var(--pad-page) !important;
    border-top: 10px solid var(--feed-sep) !important;
    margin-top: 10px !important;
    padding-top: 10px !important;
  }

  /* The Executive Report was a card inside a card: reportCardContent carried its
     own 24px-padded, 14px-radius frame. With the outer card now a bare section,
     the inner frame would be the only box left on the page -- so it comes off
     too and the report reads as part of the feed. */
  #page-insights #reportCardContent {
    padding: 0 !important;
    margin-top: 12px !important;
    background: none !important;
    border: none !important;
    border-radius: 0 !important;
    backdrop-filter: none !important;
  }

  /* ---- the glow comes off ----

     The same cyan glow Tasks and Money shed, in three shapes here: the
     "Generate Fresh Audit" button (0 4px 16px), the grade badge (0 0 20px), and
     the three balance-bar fills, each with a 0 0 10px glow in its own colour
     written inline by renderInsights. The report card's own 0 12px 40px drop is
     already gone with its frame in step two.

     .bl-track keeps its shadow -- that one is inset, the groove the fill sits in,
     not a glow. */
  #page-insights .onb-btn,
  #page-insights .onb-btn:hover,
  #page-insights #rcGradeContainer,
  #page-insights .bl-fill {
    box-shadow: none !important;
  }

  /* ---- the KPI chips get a 10px corner, and lose their prose ----
     The four top cards keep their top-right chip -- rounded to 10px as asked --
     and drop the sentence under the figure, the same way Money's stat cards drop
     theirs on the phone. Label and number stay; only the description line goes. */
  #page-insights .i-stats .badge { border-radius: 3px !important; }  /* match the Habits chips */
  #page-insights .i-stats .card > div:nth-child(2) > div:last-child { display: none !important; }

  /* ---- every other card drops its top-right chip ----
     "Live AI Feed", "Ratio", "6-Axis Scan", "0 Cases", "0 Entries", "This
     Month" -- the decorative corner chips that named a card a second time. Only
     the four stat cards keep theirs. The report's inline result badge is not a
     header chip and stays. */
  #page-insights .i-feed .card > div:first-child .badge { display: none !important; }

  /* ---- the pattern list reads as lines, not boxes ----
     Each insight was its own 12px-radius box; a stack of five identical frames
     inside a framed card. The frames come off and a hairline divides one from
     the next, the same line the card header already draws under its title. */
  #page-insights #insightList { gap: 0 !important; }
  #page-insights #insightList > div {
    background: none !important;
    border: none !important;
    border-radius: 0 !important;
    border-top: 1px solid var(--surface-border) !important;
    padding: 14px 0 !important;
  }
  #page-insights #insightList > div:first-child { border-top: none !important; padding-top: 4px !important; }

  /* ---- the audit metrics stand two by two ----
     Habit Mastery, Task Velocity, Top Domain, Focus Bottleneck were one column
     of four on the phone. Two by two, like the deck up top -- the same size, not
     bigger. */
  #page-insights #reportCardContent > div:has(#rcHabitStat) {
    grid-template-columns: 1fr 1fr !important;
  }

  /* ---- the Generate button drops down beside Export ----
     On the phone the header keeps only the report's title; the audit button
     moves to the foot of the card, to the right of "Export PDF Report". The
     header copy is hidden and the foot copy shown -- desktop keeps the original
     single button in the header. */
  #page-insights .i-gen-top { display: none !important; }
  #page-insights .i-gen-bottom { display: inline-flex !important; }
  #page-insights #reportCardContent > div:last-child { flex-wrap: nowrap !important; gap: 10px !important; margin-top: 18px !important; }

  /* The two report-foot buttons, reworked into a matched pair: a filled primary
     and a ghost secondary, both a comfortable 42px tall with a 12px corner.
     Small-caps stay (the app's button voice) but the tracking is dropped so the
     labels sit on one line in the half-width each button gets. */
  #page-insights #reportCardContent > div:last-child > .onb-btn {
    flex: 1 1 0;
    min-width: 0;
    padding: 0 10px !important;
    /* 44, not 42: that is the touch target the rest of the phone shell is held
       to, and check.js asserts it on the nav. These two were a shade under. */
    min-height: 44px !important;
    font-size: 11px !important;
    font-weight: 800 !important;
    border-radius: 12px !important;
    letter-spacing: 0 !important;
    line-height: 1.2;
    white-space: normal;
    /* Flex on both buttons, centred on both axes: without align-items the label
       of the taller (wrapping) button rode the top edge instead of the middle. */
    display: inline-flex !important;
    align-items: center;
    justify-content: center;
    text-align: center;
  }
  /* Primary — the accent gradient the Tasks "Add Task" button wears. */
  #page-insights #reportCardContent > div:last-child > .i-gen-bottom {
    background: linear-gradient(135deg, var(--accent), var(--accent-end, #00E5FF)) !important;
    color: var(--on-accent, #000) !important;
    border: none !important;
  }
  /* Secondary — a ghost in the same accent, so the pair reads as one control in
     both themes instead of a bright button beside a grey slab. */
  #page-insights #reportCardContent > div:last-child > .onb-btn.secondary {
    background: transparent !important;
    color: var(--accent) !important;
    border: 1px solid rgba(var(--accent-rgb), 0.5) !important;
  }

  /* ---- the last section clears the FAB ----
     Strategic Weekly Reflection is the foot of the page; without room under it
     the textarea's corner sits beneath the 60px FAB. Same 76px the Money history
     was given. */
  #page-insights .bento:last-child > .card { padding-bottom: 76px !important; }

  /* ---- the radar grid shows in light mode ----
     The rings are drawn with a hardcoded rgba(255,255,255,0.07) -- white on
     white, invisible once the theme flips. A dark hairline in its place; the
     data polygon (.radar-poly) keeps its accent stroke. */
  /* The report's foot buttons drop their emoji on the phone, at the user's ask;
     the glyph is wrapped in a span so the desktop button keeps it and stays
     pixel-identical. */
  #page-insights .i-btn-emoji { display: none !important; }

  body.light-theme #page-insights #systemRadarChart polygon:not(.radar-poly),
  body.light-theme #page-insights #systemRadarChart line {
    stroke: rgba(0, 0, 0, 0.18) !important;
  }

  /* The grade's light-mode colour and its exclusion from the watermark fade both
     live at the top of this file now, outside any media query: the fault was
     never phone-specific and the desktop report had it too. */

  /* ---- the report's two foot buttons ----
     The ghost button was accent-on-white: --accent stays #00E5FF in the light
     theme, so "Export PDF Report" was cyan text on a white card at a measured
     1.54:1 -- against the 4.5:1 the WCAG asks for body text, and it read as
     washed-out rather than as a control. It gets a darker ink of the same hue,
     a tinted surface so it looks like a button rather than floating text, and a
     border with enough weight to bound it. Dark mode keeps what it had, where
     the neon on near-black was never the problem. */
  body.light-theme #page-insights #reportCardContent > div:last-child > .onb-btn.secondary {
    color: #00727F !important;
    background: rgba(0, 114, 127, 0.07) !important;
    border: 1px solid rgba(0, 114, 127, 0.35) !important;
  }
  /* The filled button's label sits on the accent gradient. Since 13 Sept 2026
     light mode's accent is the theme's deep shade, so the readable pair there
     is white — the same --on-accent the markup asks for. Stated here so the
     two are set side by side and one cannot be changed without seeing the other. */
  body.light-theme #page-insights #reportCardContent > div:last-child > .i-gen-bottom {
    color: var(--on-accent, #04202B) !important;
  }
}

/* ================================================================
   MOBILE LONG-PRESS DELETE — visible delete buttons are hidden on the
   phone across Habits, Tasks, Money, Journal and Problems; press and hold the
   row/card instead (app.js). Desktop is untouched — every rule here lives
   inside this file's phone media query, and every selector it touches keeps
   its existing desktop-only rule elsewhere unchanged.
   ================================================================ */
@media (max-width: 640px) {
  /* Feedback while a press is held: a soft red ring grows in, the visual cue
     that something is armed before the dialog appears. */
  [data-lp-call] { position: relative; -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; }
  [data-lp-call].lp-armed::after {
    content: '';
    position: absolute; inset: 0;
    border-radius: inherit;
    background: rgba(255, 77, 109, 0.12);
    border: 1.5px solid rgba(255, 77, 109, 0.55);
    animation: lpPressGrow 550ms ease-out forwards;
    pointer-events: none;
    z-index: 3;
  }
  /* Grows by scale, from a base that is already visible.

     It used to fade in from opacity:0, which is the one shape this project's
     CSS guard rejects: an animation that is running but not advancing holds its
     `from` value, so a stalled frame clock left the ring invisible for the whole
     press — no feedback at all, on the gesture that is about to delete
     something. Scaling from .97 keeps the "grows in" read and cannot hide it. */
  @keyframes lpPressGrow { from { transform: scale(0.97); } to { transform: scale(1); } }
  @media (prefers-reduced-motion: reduce) {
    [data-lp-call].lp-armed::after { animation: none; }
  }

  /* ---- Habits: the "Edit Habit Details" table's Delete column ---- */
  #page-habits .del-x-btn,
  #page-habits #habitDetailTable th:last-child,
  #page-habits #habitDetailTable td:last-child { display: none !important; }
  #page-habits .hb-del-h { display: none !important; }

  /* ---- Tasks: the ✕ on each row (the star toggle stays) ---- */
  #page-tasks .task-del-btn { display: none !important; }

  /* ---- Money: transaction ledger + recurring subscriptions ---- */
  #page-money .tx-del,
  #page-money .rec-del { display: none !important; }

  /* ---- Journal: purpose pillars + the log history ---- */
  #page-journal .purpose-del-btn,
  #page-journal .je-del-btn { display: none !important; }

  /* ---- Problems: case file history ---- */
  #page-problems .case-del { display: none !important; }

  /* A small "Manage Categories" trigger in the habits log header — the delete
     modal it opens already has its own styled confirm; this just makes it
     reachable on the phone, where it previously lived only in the desktop
     analytics card. */
  #page-habits .hb-catmgr-btn {
    display: inline-flex !important;
    align-items: center; justify-content: center;
    width: 30px; height: 30px;
    background: var(--surface-hover);
    border: 1px solid var(--card-brd);
    border-radius: 8px;
    color: var(--text-dim);
    font-size: 13px;
    cursor: pointer;
  }

  /* Restart / Delete for an active challenge, shown only in the phone's
     separate 75-day arena card — the desktop bento keeps its own pair. */
  .sac-mobile-actions {
    display: flex !important;
    gap: 8px;
    margin-top: 10px;
  }
  .sac-mobile-actions button {
    flex: 1;
    display: inline-flex; align-items: center; justify-content: center; gap: 6px;
    min-height: 38px;
    border-radius: 9px;
    font-size: 12px; font-weight: 800;
    cursor: pointer;
  }
  .sac-restart-btn {
    background: rgba(0, 229, 255, 0.1);
    border: 1px solid rgba(0, 229, 255, 0.3);
    color: var(--cyan);
  }
  .sac-delete-btn {
    background: rgba(255, 62, 160, 0.1);
    border: 1px solid rgba(255, 62, 160, 0.3);
    color: var(--danger);
  }
}

/* ==========================================================================
   PUBLISHED CHALLENGE CARDS — phone
   ==========================================================================
   The five cards written into index.html all carry a short name that fits on
   one line ("75 Hard", "Detox"), and the phone lifts the Active chip out of
   the flow to sit absolutely at the card's top right. A published challenge
   names itself, so "30 Day Cold Shower" wraps to two lines and the second one
   ran underneath that chip.

   A gutter the width of the chip, on the title of a published card only. The
   built-in cards are untouched, and so is every desktop card — there the chip
   is still a flex item in the row and takes the space it needs.
   ========================================================================== */
@media (max-width: 640px) {
  #page-habits #challengeBentoGrid > [data-lp-custom] .cc-title {
    padding-right: 54px;
  }
}


/* --- Ripple & Press Feedback --- */
* { -webkit-tap-highlight-color: transparent; }

.lp-press {
  position: relative;
  overflow: hidden;
  transition: transform 90ms ease-out;
}
.lp-press[style*="position:absolute"],
.lp-press[style*="position: absolute"] {
  position: absolute;
}
.lp-press:active {
  transform: scale(0.97);
}
.card.lp-press:active,
.challenge-card.lp-press:active {
  transform: scale(0.988);
}

/* The mount point for a ripple whose pressed element cannot clip one itself —
   a table row. Positioning and clipping only; the press-scale is left on the
   row, which is the thing that was pressed. See the pointerdown handler. */
.lp-ripple-host { position: relative; overflow: hidden; }

.lp-ripple {
  position: absolute; border-radius: 50%; pointer-events: none;
  background: currentColor; opacity: 0.18;
  transform: scale(0);
  animation: lpRipple 380ms cubic-bezier(0.2, 0.6, 0.35, 1) forwards;
}
@keyframes lpRipple { to { transform: scale(1); opacity: 0; } }

@media (prefers-reduced-motion: reduce) {
  .lp-ripple { display: none; }
}
/* ------------------------------- */

@media (max-width: 640px) {
  .separate-arena-card .c-top-res-btn { top: 52px !important; }
  .hide-on-mobile { display: none !important; }
  #page-problems .section-title[style*="--magenta"] { color: var(--accent) !important; }
  #page-problems .badge#problemHealthBadge { background: rgba(var(--accent-rgb), 0.12) !important; color: var(--accent) !important; border-color: rgba(var(--accent-rgb), 0.3) !important; }
  #page-problems .badge[style*="255,62,160"] { background: rgba(var(--accent-rgb), 0.15) !important; color: var(--accent) !important; border-color: rgba(var(--accent-rgb), 0.35) !important; }
  #page-problems .p-diag-wrap .num { background: rgba(var(--accent-rgb), 0.15) !important; color: var(--accent) !important; }
  
  .ep-grid-container {
    display: grid !important;
    grid-template-columns: 1fr 1fr;
    gap: 12px !important;
  }
}

/* ============================================================
   Desktop Enhancements & Light/Dark Theme Polishes
   ============================================================ */

/* 1. Global & Light/Dark Theme Polishes */
#page-insights #systemRadarChart line {
  stroke: rgba(255, 255, 255, 0.18) !important;
}
#page-insights #systemRadarChart polygon:not(.radar-poly) {
  stroke: rgba(255, 255, 255, 0.12) !important;
}
body.light-theme #page-insights #systemRadarChart polygon:not(.radar-poly),
body.light-theme #page-insights #systemRadarChart line {
  stroke: rgba(0, 0, 0, 0.18) !important;
}

.ep-grid-container {
  display: grid !important;
  grid-template-columns: 1fr 1fr;
  gap: 12px !important;
}

.badge-filter-btn {
  border: 1px solid rgba(255, 255, 255, 0.14) !important;
}
body.light-theme .badge-filter-btn {
  border: 1px solid rgba(0, 0, 0, 0.12) !important;
}
.badge-filter-btn.active {
  border-color: rgba(0, 229, 255, 0.4) !important;
}

#journalSearchInput,
#journalFilterType {
  border: 1px solid rgba(255, 255, 255, 0.14) !important;
}
body.light-theme #journalSearchInput,
body.light-theme #journalFilterType {
  border: 1px solid rgba(0, 0, 0, 0.12) !important;
}

#settingsSocialCard {
  display: none !important;
}

#page-money .tx-pill-icon {
  display: none !important;
}

#page-money #recurList .add-btn.money,
#page-money #recurList .add-btn.money:hover {
  background: linear-gradient(135deg, #00707F, #0B4EA2) !important;
  color: #fff !important;
  box-shadow: none !important;
  border: none !important;
  filter: none !important;
}

/* 2. Journal Page Dark Mode Borders */
#journalTemplateSelector {
  border: 1px solid rgba(255, 255, 255, 0.14) !important;
  background: var(--surface-bg) !important;
}
body.light-theme #journalTemplateSelector {
  border: 1px solid rgba(0, 0, 0, 0.1) !important;
}

#newPurposeInput,
.j-goal-add-inner {
  border: 1px solid rgba(255, 255, 255, 0.14) !important;
}
body.light-theme #newPurposeInput,
body.light-theme .j-goal-add-inner {
  border: 1px solid rgba(0, 0, 0, 0.1) !important;
}

.focus-goal {
  border: 1px solid rgba(255, 255, 255, 0.12) !important;
}
body.light-theme .focus-goal {
  border: 1px solid rgba(0, 0, 0, 0.08) !important;
}

/* 3. Desktop Tier (>640px) */
@media (min-width: 641px) {
  /* Money Page: Reorder deck (Transaction log & Subscriptions) above charts */
  #page-money.active {
    display: flex;
    flex-direction: column;
  }
  #page-money > .m-stats { order: 1; }
  #page-money > .m-deck { order: 2; }
  #page-money > .m-charts { order: 3; }

  /* Money Page: Transaction Log Segmented Filter */
  #page-money .tx-filter-row {
    display: inline-flex;
    background: var(--surface-bg);
    border: 1px solid var(--card-brd);
    border-radius: 10px;
    padding: 3px;
    gap: 4px;
  }
  #page-money .tx-filter-pill {
    background: transparent !important;
    border: none !important;
    color: var(--text-dim) !important;
    font-weight: 700 !important;
    font-size: 12px !important;
    padding: 5px 14px !important;
    border-radius: 7px !important;
    cursor: pointer;
    transition: all 0.2s;
  }
  #page-money .tx-filter-pill.active {
    background: var(--accent) !important;
    color: var(--on-accent, #000) !important;
    font-weight: 800 !important;
  }

  /* Habits Page: Add Habit / Add Category text — black in dark, white in light (--on-accent) */
  #page-habits .add-btn.habit,
  .cat-add-box .add-btn.habit {
    color: var(--on-accent, #000000) !important;
    text-shadow: none !important;
  }

  /* Floating Action Button: plus icon — black in dark, white in light (--on-accent) */
  #mainFabBtn,
  #mainFabBtn .lp-ic,
  .fab-container #mainFabBtn {
    color: var(--on-accent, #000000) !important;
  }

  /* Header Topbar Height Uniformity (36px) on Desktop */
  .topbar2 .tb-left .logo,
  .topbar2 .xp-bar-container,
  .topbar2 #pageGuideBtn,
  .topbar2 #headerThemeModeBtn,
  .topbar2 #globalMonthSelect,
  .topbar2 #topbarCloudUserBtn {
    height: 36px !important;
    box-sizing: border-box !important;
    display: inline-flex !important;
    align-items: center !important;
  }
  .topbar2 #pageGuideBtn,
  .topbar2 #headerThemeModeBtn {
    width: 36px !important;
  }
  .topbar2 #globalMonthSelect {
    border-radius: 10px !important;
  }
}

/* Card section titles: Keep icon and title text adjacent (12-15px gap max) */
.card h3.section-title {
  justify-content: flex-start !important;
  gap: 12px !important;
}

/* Unified red bordered delete buttons for history & item lists */
.case-del,
.task-del-btn,
.tx-del,
.rec-del,
.purpose-del-btn,
.je-del-btn {
  background: rgba(255, 77, 109, 0.1) !important;
  border: 1px solid rgba(255, 77, 109, 0.25) !important;
  color: var(--danger) !important;
  width: 26px !important;
  height: 26px !important;
  border-radius: 7px !important;
  display: inline-flex !important;
  align-items: center !important;
  justify-content: center !important;
  cursor: pointer !important;
  opacity: 0.85 !important;
  transition: all 0.2s !important;
  padding: 0 !important;
  flex-shrink: 0 !important;
}
.case-del:hover,
.task-del-btn:hover,
.tx-del:hover,
.rec-del:hover,
.purpose-del-btn:hover,
.je-del-btn:hover {
  opacity: 1 !important;
  background: rgba(255, 77, 109, 0.22) !important;
  border-color: rgba(255, 77, 109, 0.45) !important;
  color: var(--danger) !important;
}

/* Hall of Fame: Sleek segmented tabs */
.lb-seg {
  display: inline-flex !important;
  background: rgba(255, 255, 255, 0.03) !important;
  border: 1px solid rgba(255, 255, 255, 0.12) !important;
  border-radius: 10px !important;
  padding: 5px !important;
  gap: 6px !important;
}
.lb-tab-btn {
  background: transparent !important;
  border: none !important;
  color: var(--text-dim) !important;
  font-weight: 700 !important;
  font-size: 15px !important;
  padding: 10px 22px !important;
  border-radius: 7px !important;
  cursor: pointer !important;
  transition: all 0.2s !important;
  box-shadow: none !important;
}
.lb-tab-btn.active {
  background: var(--cyan) !important;
  color: var(--on-accent, #000) !important;
  border: none !important;
  font-weight: 800 !important;
  box-shadow: none !important;
}

/* Badge filter segmented control overrides */
#badgeFilterControls.segmented-control {
  background: rgba(255, 255, 255, 0.03) !important;
  border: 1px solid rgba(255, 255, 255, 0.12) !important;
}
#badgeFilterControls.segmented-control .badge-filter-btn {
  background: transparent !important;
  color: var(--text-dim) !important;
  border: none !important;
  border-radius: 7px !important;
  padding: 6px 14px !important;
  font-size: 11.5px !important;
  font-weight: 700 !important;
}
#badgeFilterControls.segmented-control .badge-filter-btn.active {
  background: var(--accent) !important;
  color: var(--on-accent, #000) !important;
  font-weight: 800 !important;
}

body.light-theme .lb-seg,
body.light-theme #badgeFilterControls.segmented-control {
  background: rgba(0, 0, 0, 0.03) !important;
  border: 1px solid rgba(0, 0, 0, 0.08) !important;
}

/* Settings Page: Invite Friends referral row (Desktop same-line layout: Link 60% | Copy ~18-20% | Share ~18-20%) */
@media (min-width: 641px) {
  .settings-referral-row {
    display: flex !important;
    flex-direction: row !important;
    flex-wrap: nowrap !important;
    align-items: center !important;
    gap: 12px !important;
    width: 100% !important;
    max-width: 100% !important;
    box-sizing: border-box !important;
    overflow: hidden !important;
  }
  .settings-referral-row .settings-referral-link-box {
    flex: 0 0 calc(60% - 8px) !important;
    width: calc(60% - 8px) !important;
    max-width: calc(60% - 8px) !important;
    min-width: 0 !important;
    box-sizing: border-box !important;
  }
  .settings-referral-row .settings-referral-copy,
  .settings-referral-row .settings-referral-share {
    flex: 0 0 calc(20% - 8px) !important;
    width: calc(20% - 8px) !important;
    max-width: calc(20% - 8px) !important;
    min-width: 0 !important;
    white-space: nowrap !important;
    height: 44px !important;
    justify-content: center !important;
    text-align: center !important;
    box-sizing: border-box !important;
    padding: 10px 8px !important;
  }
}

/* Settings Page: Mobile layout (Link 100% on top, Copy 50% on bottom-left, Share 50% on bottom-right) */
@media (max-width: 640px) {
  .settings-referral-row {
    display: flex !important;
    flex-wrap: wrap !important;
    gap: 10px !important;
    width: 100% !important;
    max-width: 100% !important;
    box-sizing: border-box !important;
  }
  .settings-referral-row .settings-referral-link-box {
    width: 100% !important;
    flex: 0 0 100% !important;
    min-width: 0 !important;
    box-sizing: border-box !important;
  }
  .settings-referral-row .settings-referral-copy,
  .settings-referral-row .settings-referral-share {
    flex: 0 0 calc(50% - 5px) !important;
    width: calc(50% - 5px) !important;
    max-width: calc(50% - 5px) !important;
    min-width: 0 !important;
    justify-content: center !important;
    text-align: center !important;
    height: 44px !important;
    box-sizing: border-box !important;
  }
}

/* ============================================================
   CHALLENGES PAGE (EXECUTIVE DASHBOARD & PROTOCOL ARENA)
   ============================================================ */

/* Strict Mode Toggle Switch */
.lp-switch {
  position: relative;
  display: inline-block;
  width: 44px;
  height: 24px;
  flex-shrink: 0;
}
.lp-switch input {
  opacity: 0;
  width: 0;
  height: 0;
}
.lp-slider {
  position: absolute;
  cursor: pointer;
  inset: 0;
  background: var(--card-brd);
  border: 1px solid var(--inner-box-border);
  transition: .22s cubic-bezier(.16,1,.3,1);
  border-radius: 24px;
}
.lp-slider:before {
  position: absolute;
  content: "";
  height: 18px;
  width: 18px;
  left: 2px;
  bottom: 2px;
  background: #ffffff;
  transition: .22s cubic-bezier(.16,1,.3,1);
  border-radius: 50%;
  box-shadow: 0 1px 4px rgba(0,0,0,0.3);
}
.lp-switch input:checked + .lp-slider {
  background: var(--accent);
  border-color: var(--accent);
}
.lp-switch input:checked + .lp-slider:before {
  transform: translateX(20px);
  background: #000000;
}

/* Category Filter Chips */
.chal-filter-chip {
  padding: 6px 14px;
  border-radius: 99px;
  border: 1px solid var(--card-brd);
  background: var(--inner-box-bg);
  color: var(--text-dim);
  font-size: 12px;
  font-weight: 700;
  font-family: 'Inter', sans-serif;
  cursor: pointer;
  transition: all .2s cubic-bezier(.16,1,.3,1);
  white-space: nowrap;
}
.chal-filter-chip:hover {
  color: var(--text);
  border-color: var(--surface-border);
}
.chal-filter-chip.active {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent, #000000) !important;
  box-shadow: 0 2px 10px rgba(var(--accent-rgb), 0.25);
}

/* Segmented Control for Active Challenges */
.chal-seg-tabs {
  display: flex;
  gap: 8px;
  align-items: center;
  overflow-x: auto;
  scrollbar-width: none;
}
.chal-seg-tabs::-webkit-scrollbar {
  display: none;
}

/* Trophy Grid 5-up */
.chal-trophy-grid {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 16px;
}

@media (max-width: 1024px) {
  .chal-trophy-grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

@media (max-width: 640px) {
  .chal-trophy-grid {
    grid-template-columns: 1fr;
    gap: 12px;
  }
  .chal-kpi-deck {
    display: flex !important;
    flex-direction: column !important;
    gap: 10px !important;
  }
  .chal-filter-chips {
    width: 100% !important;
    overflow-x: auto !important;
    padding-bottom: 4px !important;
    scrollbar-width: none !important;
  }
  .chal-filter-chips::-webkit-scrollbar {
    display: none !important;
  }
  .chal-filter-chip {
    min-height: 44px !important;
    display: inline-flex !important;
    align-items: center !important;
    padding: 0 16px !important;
  }
  .chal-seg-btn {
    min-height: 44px !important;
    padding: 0 16px !important;
  }
  #page-challenges .separate-arena-card {
    border-radius: 0 !important;
    border-left: none !important;
    border-right: none !important;
  }
}

/* Winter Arc used to run the full width of the deck here. That is gone on
   purpose: every card in the catalogue is now the same size, and the row shape
   comes from syncChallengeGridLayout()'s column count alone — five to a line at
   most, a short last row simply leaving cells empty. The phone keeps its own
   full-width rule for the 5n card, in its own tier. */

@media (min-width: 641px) {
  #page-challenges .c-join-m {
    display: none !important;
  }
  #page-challenges .c-join-d {
    display: inline !important;
  }
  #page-challenges .c-btn {
    background: linear-gradient(135deg, #00707F, #0B4EA2) !important;
    color: #fff !important;
  }
}

/* ==========================================================================
   CHALLENGES PAGE — phone                                     (mobile only)
   ==========================================================================
   The page in the shape the rest of the app already uses on a phone: a stack
   of full-bleed sections, each closed by the 10px band, and everything inside
   them lined up on var(--pad-page) so nothing is indented twice.

   Desktop is untouched — every rule below lives in the 640px tier.
   ========================================================================== */
@media (max-width: 640px) {

  /* ---- 1. Status deck: two boxes on one line, shaped like the Habits four ----

     The phone shows two of the deck's four. Conquered Vault (3) and Execution
     Rate (4) are hidden here — the owner asked for two on one line, and these
     are the two that were dropped. Hidden, not removed: desktop shows all four
     and every KPI keeps being written, so either can be swapped in by changing
     which nth-child this rule names.

     grid-column and width both have to be cleared, for the same reason they do
     on the Habits deck: the 1024px tier gives every .s4 a 12-column span and
     width:100%, which in a two-column grid still claims a whole row. That is
     what kept these two stacked after the deck became a grid. */
  #page-challenges .chal-kpi-deck > .chal-kpi-card:nth-child(3),
  #page-challenges .chal-kpi-deck > .chal-kpi-card:nth-child(4) {
    display: none !important;
  }
  #page-challenges .chal-kpi-deck {
    display: grid !important;
    grid-template-columns: 1fr 1fr !important;
    gap: 8px !important;
    width: auto !important;
    margin: 0 calc(-1 * var(--pad-page)) 15px !important;
    padding: 0 var(--pad-page) 15px !important;
    border-bottom: 10px solid var(--feed-sep) !important;
  }
  /* The card's own shape — padding, radius, the stacked icon tile and the black
     fill — is the shared stat-card block at the end of this file, which Journal,
     Problems and Hall of Fame now use too. Only the grid placement is here. */
  #page-challenges .chal-kpi-deck > .chal-kpi-card {
    grid-column: auto !important;
    width: auto !important;
    margin: 0 !important;
  }
  /* The sub-lines are written with white-space:nowrap and an ellipsis, which at
     half the width cut "1 running transformation protocols" to "1 running
     transformation …". Half a screen is enough to wrap onto a second line. */
  #page-challenges #chalKpiActiveSub,
  #page-challenges #chalKpiStreakSub {
    white-space: normal !important;
    overflow: visible !important;
    text-overflow: clip !important;
    font-size: 10.5px !important;
    line-height: 1.3 !important;
  }

  /* ---- 2. Active Protocol Arena ---- */
  #page-challenges #chalActiveSection {
    margin: 0 calc(-1 * var(--pad-page)) 15px !important;
    padding: 0 var(--pad-page) 15px !important;
    border-bottom: 10px solid var(--feed-sep) !important;
  }
  /* The card inside is already frameless on the phone — that is the
     `#separateChallengeArena > .card` rule further up. What was left was its
     horizontal padding: the card sat on the page gutter and then indented its
     own contents by another 14px, so the title row, the checklist box, the
     Restart/Delete/Club buttons and the day matrix all started 28px in while
     the section heading above them started at 14. Zero it and every one of
     them lands on var(--pad-page), like the rest of the page. */
  #page-challenges .separate-arena-card {
    padding: 12px 0 !important;
    margin-bottom: 0 !important;
  }
  /* Two running protocols show one arena at a time behind the segmented tabs,
     so this normally matches nothing — it is here so that if they ever render
     stacked, each one is closed by the same band. */
  #page-challenges #separateChallengeArena > .card:not(:last-child) {
    border-bottom: 10px solid var(--feed-sep) !important;
    margin-bottom: 15px !important;
    padding-bottom: 15px !important;
  }
  /* Black on the accent fill in dark; white in light, where the accent is the
     theme's deep shade (--on-accent, 13 Sept 2026). */
  #page-challenges #chalEmptyState .add-btn.habit {
    color: var(--on-accent, #000) !important;
  }

  /* ---- 3. Life Transformation Challenges: no outer frame, full width ---- */
  #page-challenges .hb-challenges {
    background: transparent !important;
    border: none !important;
    border-radius: 0 !important;
    border-bottom: 10px solid var(--feed-sep) !important;
    margin: 0 calc(-1 * var(--pad-page)) 15px !important;
    padding: 0 var(--pad-page) 15px !important;
  }
  /* The "Command Center" chip goes for the same reason it went on Habits: it
     names the section it sits in, next to a heading that already names it —
     and at this width it was pushing the heading onto two lines. */
  #page-challenges .hb-cmd-badge { display: none !important; }
  /* Its header had a rule under it, which read as the frame the section no
     longer has. */
  #page-challenges .hb-challenges > div:first-child {
    border-bottom: none !important;
    margin-bottom: 10px !important;
    padding-bottom: 0 !important;
  }

  /* ---- 4. The deck's row pattern, at any number of cards ----

     Five to a group: two pairs and a full-width fifth. So a card is full width
     when it is the 5th of its group, or when it is the last card and would
     otherwise sit alone at the start of a row — position 1 or 3 of the group.

     Six cards: 1-2, 3-4, 5, then 6 alone. Seven: 6 and 7 pair up. Eight: 6-7,
     then 8 alone. Nine: 6-7, 8-9. Ten: 10 is the 5n card. Then it repeats.
     (`:nth-child(odd):last-child` was the old test and is wrong from seven on —
     7 is odd and last, but it has a partner.) */
  #page-challenges #challengeBentoGrid > .challenge-card:nth-child(5n),
  #page-challenges #challengeBentoGrid > .challenge-card:nth-child(5n + 1):last-child,
  #page-challenges #challengeBentoGrid > .challenge-card:nth-child(5n + 3):last-child {
    grid-column: 1 / -1 !important;
  }
  /* A full-width card that is not running puts its Join button beside the text
     rather than under it, the way Winter Arc already does. */
  #page-challenges #challengeBentoGrid > .challenge-card:nth-child(5n):not(.is-active),
  #page-challenges #challengeBentoGrid > .challenge-card:nth-child(5n + 1):last-child:not(.is-active),
  #page-challenges #challengeBentoGrid > .challenge-card:nth-child(5n + 3):last-child:not(.is-active) {
    flex-direction: row !important;
    align-items: flex-end !important;
    min-height: auto !important;
  }
  #page-challenges #challengeBentoGrid > .challenge-card:nth-child(5n):not(.is-active) > div:nth-child(2),
  #page-challenges #challengeBentoGrid > .challenge-card:nth-child(5n + 1):last-child:not(.is-active) > div:nth-child(2),
  #page-challenges #challengeBentoGrid > .challenge-card:nth-child(5n + 3):last-child:not(.is-active) > div:nth-child(2) {
    flex: 1 1 auto;
    min-width: 0;
    margin-right: 20px;
  }
  #page-challenges #challengeBentoGrid > .challenge-card:nth-child(5n):not(.is-active) > div:nth-child(2) > div:first-child,
  #page-challenges #challengeBentoGrid > .challenge-card:nth-child(5n + 1):last-child:not(.is-active) > div:nth-child(2) > div:first-child,
  #page-challenges #challengeBentoGrid > .challenge-card:nth-child(5n + 3):last-child:not(.is-active) > div:nth-child(2) > div:first-child {
    margin-bottom: 10px !important;
  }
  #page-challenges #challengeBentoGrid > .challenge-card:nth-child(5n):not(.is-active) > [id^="action-btn-"],
  #page-challenges #challengeBentoGrid > .challenge-card:nth-child(5n + 1):last-child:not(.is-active) > [id^="action-btn-"],
  #page-challenges #challengeBentoGrid > .challenge-card:nth-child(5n + 3):last-child:not(.is-active) > [id^="action-btn-"] {
    flex: 0 0 auto;
  }
  #page-challenges #challengeBentoGrid > .challenge-card:nth-child(5n):not(.is-active) [id^="action-btn-"] .c-btn,
  #page-challenges #challengeBentoGrid > .challenge-card:nth-child(5n + 1):last-child:not(.is-active) [id^="action-btn-"] .c-btn,
  #page-challenges #challengeBentoGrid > .challenge-card:nth-child(5n + 3):last-child:not(.is-active) [id^="action-btn-"] .c-btn {
    margin-top: 0 !important;
    padding: 11px 24px !important;
    font-size: 13px !important;
  }

  /* ---- 5. Trophy shelf: only the protocols actually taken on ----

     renderChallengeTrophies() marks a card .is-locked when it has never been
     started and is not running, so what is left is exactly what was joined or
     conquered. The whole section goes with them when that leaves nothing —
     a heading and a subtitle over an empty grid is not a shelf. */
  /* The band belongs to the section above the shelf, so when the shelf goes
     (rule below, both tiers now) the band has to go with it — otherwise the
     page ends on a separator with nothing under it. */
  #page-challenges.chal-no-trophies .hb-challenges {
    border-bottom: none !important;
    padding-bottom: 0 !important;
    margin-bottom: 0 !important;
  }
}

/* ==========================================================================
   THE PHONE'S STAT CARD, ONE SHAPE                            (mobile only)
   ==========================================================================
   Four decks used to be four designs at 170px wide: Challenges stacked its
   icon over the label, while Journal, Problems and Hall of Fame kept the
   desktop's icon-beside-text row, which at half a screen left the number about
   90px to live in. Asked for directly — make those three read like Challenges.

   So one shape for all four: the icon tile on top at 34px, the text under it
   with the full width to itself, 14px of padding inside a 10px corner, and in
   dark mode a black fill so only the card's own coloured border draws it,
   which is what the phone's #000 page ground asks for.

   The two markups differ by one node. Challenges' cards are [tile, text];
   the other three carry a watermark glyph first, so theirs are [watermark,
   tile, text] — the watermark is position:absolute, so it is out of flow and
   the column only ever lays out the two that matter. That is why the tile and
   the text are addressed from both ends: :first-child/:last-child covers the
   pair, nth-child(2)/nth-child(3) covers the trio.

   Desktop keeps all four decks exactly as they were — every rule is in the
   640px tier, and the light theme keeps its own card fills.
   ========================================================================== */
@media (max-width: 640px) {
  #page-challenges .chal-kpi-deck > .chal-kpi-card,
  #page-journal .j-stats > .card,
  #page-problems .p-stats > .card,
  #page-leaderboard .lb-stats > .card {
    padding: 14px !important;
    border-radius: 10px !important;
    flex-direction: column !important;
    align-items: flex-start !important;
    justify-content: flex-start !important;
    gap: 10px !important;
  }

  /* The icon tile, down from 42-46px: at half the width these cards were
     otherwise as tall as a whole section underneath them. */
  #page-challenges .chal-kpi-deck > .chal-kpi-card > div:first-child,
  #page-journal .j-stats > .card > div:nth-child(2),
  #page-problems .p-stats > .card > div:nth-child(2),
  #page-leaderboard .lb-stats > .card > div:nth-child(2) {
    width: 34px !important;
    height: 34px !important;
    border-radius: 9px !important;
    font-size: 17px !important;
    flex: 0 0 auto !important;
  }

  /* The text block takes the row it now has. flex:1 is inline on some of these
     and in a column that stretches them vertically instead of horizontally. */
  #page-challenges .chal-kpi-deck > .chal-kpi-card > div:last-child,
  #page-journal .j-stats > .card > div:nth-child(3),
  #page-problems .p-stats > .card > div:nth-child(3),
  #page-leaderboard .lb-stats > .card > div:nth-child(3) {
    width: 100% !important;
    min-width: 0 !important;
    flex: 0 0 auto !important;
  }

  /* The watermark glyph, which the Challenges cards never had — it is the last
     difference between them, and against a black fill it reads louder than it
     did against the old gradient. Hidden, not removed; desktop keeps it. */
  #page-journal .j-stats > .card > div:first-child,
  #page-problems .p-stats > .card > div:first-child,
  #page-leaderboard .lb-stats > .card > div:first-child {
    display: none !important;
  }

  /* One size for the number on all four, so the decks read as one component
     rather than four that happen to sit in the same place. */
  #page-challenges #chalKpiActive,
  #page-challenges #chalKpiStreak,
  #page-journal .j-stats > .card > div:nth-child(3) > div:nth-child(2),
  #page-problems .p-stats > .card > div:nth-child(3) > div:nth-child(2),
  #page-leaderboard .lb-stats > .card > div:nth-child(3) > div:nth-child(2) {
    font-size: 21px !important;
    line-height: 1.2 !important;
  }

  /* Black in dark mode. The fills are inline gradients on three of these decks,
     hence !important; the border stays, because on a black ground it is the
     only thing left drawing the card. Light theme is untouched — it has its own
     card colour and a white page behind it. */
  body:not(.light-theme) #page-challenges .chal-kpi-deck > .chal-kpi-card,
  body:not(.light-theme) #page-journal .j-stats > .card,
  body:not(.light-theme) #page-problems .p-stats > .card,
  body:not(.light-theme) #page-leaderboard .lb-stats > .card {
    background: #000000 !important;
  }
}

/* ==========================================================================
   ONE SEGMENTED CONTROL                                       (mobile only)
   ==========================================================================
   The Money log's filter — a track with a 1px edge, 4px of inset padding, and
   the chosen half filled solid accent with black on it — is the one the owner
   picked as the house control. Three other pairs on the phone were each doing
   their own thing, and are brought onto it here:

   - Hall of Fame's Achievement / Community switcher. Its track already
     matched; its chosen half did not. A global rule paints .lb-tab-btn.active
     with a 14% accent tint and a bordered edge, while the phone tier had
     separately set the label to black — 14% of accent is close to the track it
     sits on, so "Achievement" was black text on near-black. Solid fill fixes
     the control and the contrast in one go.

   - Settings' Export / Restore, and Copy Link / Share. Neither pair is a
     filter: nothing is "selected", and the accent half is the primary action
     rather than the current state. They read as a segmented pair anyway,
     because that is the shape the owner asked for, and the two halves keep the
     fonts and weights they already had.

   Desktop is untouched: the Hall of Fame rules live in the 640px tier, and the
   two Settings wrappers are display:flex only here — .settings-referral-actions
   is display:contents in its inline style, so above this width its buttons stay
   direct flex children of .settings-referral-row exactly as before.
   ========================================================================== */
@media (max-width: 640px) {
  /* ---- the track ---- */
  #page-leaderboard .lb-seg,
  #page-settings .settings-backup-actions,
  #page-settings .settings-referral-actions {
    display: flex !important;
    gap: 4px !important;
    padding: 4px !important;
    border-radius: 10px !important;
    background: var(--surface-bg) !important;
    border: 1px solid var(--card-brd) !important;
    box-sizing: border-box !important;
  }

  /* ---- the halves ---- */
  #page-leaderboard .lb-seg > .lb-tab-btn,
  #page-settings .settings-backup-actions > .onb-btn,
  #page-settings .settings-referral-actions > .onb-btn {
    flex: 1 1 0 !important;
    min-width: 0 !important;
    border: none !important;
    border-radius: 7px !important;
    box-shadow: none !important;
    background: transparent !important;
    color: var(--text-dim) !important;
  }

  /* The chosen half, and — on the two Settings pairs — the primary action.
     Black on accent both times: var(--accent) does not flip with the theme, so
     black is the readable side in dark and in light. */
  #page-leaderboard .lb-seg > .lb-tab-btn.active,
  #page-settings .settings-backup-actions > .onb-btn:not(.secondary),
  #page-settings .settings-referral-actions > .onb-btn:not(.secondary) {
    background: var(--accent) !important;
    color: var(--on-accent, #000) !important;
  }

  /* ---- what each pair keeps ----
     Only the geometry is shared. The owner asked for these two to keep the
     type they already had, so nothing here touches font-size or weight. */
  #page-settings .settings-backup-actions > .onb-btn {
    min-height: 34px !important;
    padding: 6px !important;
  }
  #page-settings .settings-referral-actions {
    flex: 0 0 100% !important;
    width: 100% !important;
  }
  #page-settings .settings-referral-actions > .onb-btn {
    height: 40px !important;
  }
  /* The 50%-each widths the old two-up row set, now that the track does the
     splitting. */
  #page-settings .settings-referral-row .settings-referral-copy,
  #page-settings .settings-referral-row .settings-referral-share {
    flex: 1 1 0 !important;
    width: auto !important;
    max-width: none !important;
  }
}

/* ==========================================================================
   TROPHY SHELF — both tiers                                    (requested)
   ==========================================================================
   A shelf is what you have won, not a catalogue of what exists. The phone has
   shown only the protocols actually taken on since 8 Sept; the desktop asked
   for the same. renderChallengeTrophies() marks a card .is-locked when it has
   never been started and is not running, and sets .chal-no-trophies on the page
   when that leaves nothing — a heading and a subtitle over an empty grid is not
   a shelf.
   ========================================================================== */
#page-challenges .chal-trophy-card.is-locked { display: none !important; }
#page-challenges.chal-no-trophies #chalTrophySection { display: none !important; }

/* ==========================================================================
   THE TAB STRIP — names only, and none of them can hide       (requested)
   ==========================================================================
   Two changes to the same row, and the second is why the first matters.

   The icons come off. Every tab already carries its name, and thirteen glyphs
   were 288px of a 1273px strip — the whole overflow and then some. Measured:
   1561px of tabs with them, 1273px without, against a 1273px strip at 1280
   wide. So on a full-width desktop the row now fits exactly, where before it
   ran off the right with no scrollbar to say so (scrollbar-width is none).

   Below that width it wraps instead of scrolling. Scrolling was the real
   complaint — make the window smaller and tabs disappeared off the end with
   nothing to indicate they were there, and a strip already scrolled to reach
   Settings had hidden Challenges off the left. A wrapped row is taller; a
   scrolled one is a row that lies about how many tabs there are.

   The labels come up from --text-dim to a brighter slate, as asked: on the
   glass strip #94A3B8 sat close to the background. Dark only — the light theme
   is dark text on a pale bar and "whiter" there means less readable.
   ========================================================================== */
.top-tabs .tab-icon { display: none !important; }
.top-tabs {
  flex-wrap: wrap;
  overflow-x: visible;
  row-gap: 0;
}
body:not(.light-theme) .top-tab {
  color: #CBD5E1;
}
/* Social and Resources are <a>, not .top-tab, and they carry the old colour in
   an inline style plus onmouseover/onmouseout handlers that write it back — so
   these two need !important, and their hover needs restating for the same
   reason. Without it the row was eleven tabs at one brightness and two at
   another. */
body:not(.light-theme) .top-tabs > a { color: #CBD5E1 !important; }
body:not(.light-theme) .top-tabs > a:hover { color: var(--text) !important; }
/* The chosen tab keeps the accent it already had; this only stops the rule
   above from outranking it. */
body:not(.light-theme) .top-tab.active { color: var(--accent); }

/* ==========================================================================
   HALL OF FAME SWITCHER — desktop                              (requested)
   ==========================================================================
   Wearing the Tasks filter's clothes, which is the control the owner picked:
   an --inner-box-bg track with a hairline and 4px of inset, 7px pills, and the
   chosen one a solid accent fill with black on it. What was here instead was a
   14% accent tint with a bordered edge and accent-coloured text — close enough
   to the track behind it to read as barely selected.

   !important throughout: the track carries its colours in an inline style, and
   the rules these replace are themselves !important further up the file. The
   phone tier sets the same shape from its own block, so both tiers agree.
   ========================================================================== */
#page-leaderboard .lb-seg {
  background: rgba(255, 255, 255, 0.03) !important;
  border: 1px solid rgba(255, 255, 255, 0.12) !important;
  border-radius: 12px !important;
  padding: 6px !important;
  gap: 8px !important;
}
.lb-tab-btn {
  padding: 12px 28px !important;
  font-size: 16px !important;
  font-weight: 700 !important;
  border: none !important;
  border-radius: 8px !important;
  background: transparent !important;
  color: var(--text-dim) !important;
  box-shadow: none !important;
  transform: none !important;
}
.lb-tab-btn.active {
  background: var(--cyan) !important;
  color: var(--on-accent, #000) !important;
  font-weight: 800 !important;
  border: none !important;
  box-shadow: none !important;
}
/* The live dot and the coming-soon chip sat on a tinted pill and now sit on a
   solid accent one, where their own accent colouring disappears. */
.lb-tab-btn.active .lb-live-dot,
.lb-tab-btn.active .lb-coming-soon-badge {
  color: var(--on-accent, #000) !important;
  background: rgba(0, 0, 0, 0.14) !important;
  border-color: rgba(0, 0, 0, 0.2) !important;
}

/* Desktop override for badge filter segmented control */
#badgeFilterControls.segmented-control {
  background: rgba(255, 255, 255, 0.03) !important;
  border: 1px solid rgba(255, 255, 255, 0.12) !important;
}
#badgeFilterControls.segmented-control .badge-filter-btn {
  background: transparent !important;
  color: var(--text-dim) !important;
  border: none !important;
  border-radius: 7px !important;
  padding: 8px 18px !important;
  font-size: 13px !important;
  font-weight: 700 !important;
}
#badgeFilterControls.segmented-control .badge-filter-btn.active {
  background: var(--accent) !important;
  color: var(--on-accent, #000) !important;
  font-weight: 800 !important;
}

body.light-theme #page-leaderboard .lb-seg,
body.light-theme #badgeFilterControls.segmented-control {
  background: rgba(0, 0, 0, 0.03) !important;
  border: 1px solid rgba(0, 0, 0, 0.08) !important;
}

/* ==========================================================================
   THE TOAST                                                    (requested)
   ==========================================================================
   Was: a solid var(--accent) slab with #fff text, an 8px corner, no entrance
   and no width limit, pinned 20px off the bottom on every screen. White on
   cyan is the contrast trap this codebase has hit before — --accent does not
   flip with the theme, so what sits on it has to be black — and 20px off the
   bottom of a phone is underneath the nav bar and the FAB.

   Now it is built like everything else here: dark glass, one hairline, a real
   shadow, and an accent rail down the left edge that carries the colour the
   old slab was trying to carry with its whole body. It rises into place and
   drops back out rather than blinking.

   Both tiers, both themes, one place — showToast() only builds the node and
   runs the clock.
   ========================================================================== */
.lp-toast {
  position: fixed;
  left: 50%;
  bottom: 24px;
  transform: translate(-50%, 14px);
  z-index: 10000;

  display: none;
  align-items: center;
  gap: 12px;

  /* width before max-width, and both are load-bearing. A fixed box sizes
     shrink-to-fit, and with only a max-width a long message collapsed to about
     half the screen and stacked six lines in the middle of it. max-content asks
     for the whole string on one line and the max-width is what folds it. */
  width: max-content;
  max-width: min(440px, calc(100vw - 32px));
  padding: 14px 20px;
  box-sizing: border-box;

  background: rgba(10, 15, 26, 0.85);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 99px;
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.05);

  font-family: 'Inter', sans-serif;
  font-size: 13.5px;
  font-weight: 600;
  line-height: 1.45;
  color: var(--text);
  /* A message longer than the box wraps instead of running off the side, and
     the old single-line slab is why that mattered. */
  text-align: center;
  justify-content: center;
  /* break-word, not anywhere: `anywhere` also shrinks the box's own min-content
     size, which is the other half of why it collapsed. */
  overflow-wrap: break-word;

  opacity: 0;
  transition: opacity .24s ease, transform .24s cubic-bezier(.16, 1, .3, 1);
  pointer-events: none;
}
.lp-toast.is-up {
  opacity: 1;
  transform: translate(-50%, 0);
}
.lp-toast .lp-toast-msg { display: block; }

/* The shadow is tuned for a dark ground; on the light theme it reads as a
   smudge, and the glass card there is white on near-white without an edge. */
body.light-theme .lp-toast {
  background: rgba(255, 255, 255, 0.9);
  border: 1px solid rgba(0, 0, 0, 0.08);
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.12), inset 0 1px 0 rgba(255, 255, 255, 0.6);
  color: #0F172A;
}

@media (max-width: 640px) {
  /* Clear of the bottom nav and the notch inset, expressed against the same
     tokens the FAB uses so it tracks them rather than guessing a number. */
  .lp-toast {
    bottom: calc(var(--bottom-nav-h) + var(--safe-bottom) + 16px);
    max-width: calc(100vw - 28px);
    font-size: 13px;
    padding: 12px 16px 12px 13px;
    border-radius: 12px;
  }
  /* And the FAB steps up over it rather than being covered by it, the way a
     snackbar and a floating action button have always shared this corner.
     showToast() measures the toast and writes --lp-toast-h, because how far up
     depends on how many lines the message wrapped to. */
  body.lp-toast-up .fab-container {
    transform: translateY(calc(-1 * (var(--lp-toast-h, 0px) + 12px)));
  }
  .fab-container {
    transition: transform .24s cubic-bezier(.16, 1, .3, 1);
  }
}

@media (prefers-reduced-motion: reduce) {
  .lp-toast { transition: opacity .2s ease; transform: translate(-50%, 0); }
}

/* ==========================================================================
   CIRCULAR WHEEL — the detail hub                        (10 Sept 2026)

   The wheel used to answer a hover with a tooltip beside the cursor. It now
   answers in the middle of itself, which is the one part of the wheel that is
   the same size on a phone as it is worth reading. Both tiers, asked for.

   Sized in container units rather than pixels: the hub is a fixed percentage
   of the wheel, the wheel is a percentage of the card, and the card changes
   with the window. One px value would be right at exactly one width. Each
   font-size therefore ships twice — a px fallback first for a browser without
   container queries, then the cqw that supersedes it.
   ========================================================================== */
.ct-wheel-wrap {
  position: relative;
  width: 100%;
  max-width: 1100px;
}

.ct-hub {
  position: absolute;
  transform: translate(-50%, -50%);   /* left/top are the disc centre */
  aspect-ratio: 1;
  border-radius: 50%;
  /* The last line of defence. Nothing below should reach the curve, but if a
     translation or a very long habit name ever does, it is cut at the circle
     rather than laid across the rings. */
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  container-type: inline-size;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  -webkit-user-select: none;
  user-select: none;
}
.ct-hub:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* A square drawn inside a circle has a side of d/√2, about 70.7%. Staying
   under that is what keeps a long name off the curve instead of behind it. */
.ct-hub-in {
  width: 70%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  gap: 2cqw;
  overflow: hidden;
}

.ct-hub-month {
  font-family: 'Space Grotesk', sans-serif;
  font-weight: 800;
  color: var(--cyan);
  line-height: 1.1;
  font-size: 14px;
  font-size: max(13px, 9cqw);
}
.ct-hub-hint {
  color: var(--text-faint);
  font-weight: 700;
  line-height: 1.2;
  font-size: 10px;
  font-size: max(10px, 6cqw);
}

.ct-hub-date {
  color: var(--text-dim);
  font-weight: 800;
  letter-spacing: 0.08em;
  line-height: 1.1;
  white-space: nowrap;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: 9px;
  font-size: max(10px, 5.6cqw);
}

/* Two lines and then an ellipsis. "No Social Media" fits on one at this size
   and "Deep Work 90min" does not, so the clamp is doing real work rather than
   guarding a case that never happens. */
.ct-hub-habit {
  color: var(--text);
  font-weight: 800;
  line-height: 1.15;
  max-width: 100%;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
  overflow-wrap: anywhere;
  font-size: 13px;
  font-size: max(12.5px, 8.6cqw);
}

.ct-hub-status {
  display: inline-flex;
  align-items: center;
  gap: 0.45em;
  font-weight: 800;
  line-height: 1;
  white-space: nowrap;
  padding: 0.3em 0.75em;
  border-radius: 999px;
  border: 1px solid currentColor;
  font-size: 9px;
  font-size: max(10px, 5.8cqw);
}
.ct-hub-status .dot {
  width: 0.55em;
  height: 0.55em;
  border-radius: 50%;
  background: currentColor;
  flex: 0 0 auto;
}
/* The legend colours, so the word in the hub and the colour of the cell that
   was pressed are the same statement. */
.ct-hub-status.is-done    { color: var(--legend-completed); }
.ct-hub-status.is-missed  { color: var(--legend-missed); }
.ct-hub-status.is-pending { color: var(--text-faint); }

.ct-hub-foot {
  color: var(--text-faint);
  font-weight: 700;
  line-height: 1.2;
  max-width: 100%;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
  font-size: 9px;
  font-size: max(9.5px, 5.4cqw);
}
.ct-hub-foot b { color: var(--text-dim); }

/* Which cell the hub is talking about. Stroke only — a fill change would
   argue with the completed/missed colour the cell is there to show. */
.circular-segment.is-picked {
  stroke: var(--accent);
  stroke-width: 2.5;
}
.circular-segment:hover {
  opacity: 0.75;
}
@media (hover: none) {
  .circular-segment:hover { opacity: 1; }
}

/* ---- the phone's name column ---------------------------------------

   The desktop column, at the phone's scale. left/top/width/height are
   written by the render, in percentages, so the box lands exactly on the
   band the rings occupy — the wrapper is the SVG's own box and
   preserveAspectRatio leaves no letterboxing, so one scale factor maps both
   axes and the rows sit level with their rings at every width.

   HTML rather than SVG <text> for the ellipsis and the tap target, the same
   reasoning that put the hub in HTML. */
.ct-names {
  position: absolute;
  /* cqw below resolves against this box, not the viewport. */
  container-type: inline-size;
  /* The box is only as tall as the rings, and its right edge is the twelve
     o'clock radius, so nothing here can reach the hub or a segment. */
  pointer-events: none;
}
.ct-name-row {
  display: block;
  /* A button sizes to its content even as a block — form controls resolve
     width:auto to their intrinsic width, not to the containing block. Without
     this the rules stopped at the end of each name, at twelve different
     lengths, instead of running to the wheel. The column already ends exactly
     on the twelve o'clock radius, so 100% is what touches the wheel. */
  width: 100%;
  /* One ring, one row. --ct-rows is maxHabits, not the habit count, so a
     short month still lands on ring boundaries. */
  height: calc(100% / var(--ct-rows, 12));
  padding: 0 3px 0 2px;
  border: 0;
  /* No rule here: the SVG under this draws it, at the same one viewBox unit
     as the wheel's own lines. A border would also have cost 1px of a 7.28px
     row, and the last row, ruled top and bottom, would have clipped every
     descender it had. */
  background-color: transparent;
  color: var(--ct-text-habit);
  font-family: inherit;
  font-weight: 500;
  /* Sized off the column, never off px: a px floor would be a promise to
     overflow the row on a narrower phone.

     The number is what the row can hold, not what looks comfortable. This
     font draws ascender to descender over 1.274em, so on a 7.28px row the
     most it can be is 5.71px — at 6.25px it was losing 0.47px off the top of
     every capital and 0.22px off every descender to overflow:hidden, on all
     twelve rows. 3.3cqw of a 172.6px column is 5.70px. */
  font-size: 3.3cqw;
  /* The line box is the row, so half-leading centres the glyphs in it. At
     line-height 1 the box was the font size and sat high: the ink cleared the
     row overall but still crossed the top edge by 0.2px. Unitless, so it
     tracks the font, which tracks the column. */
  line-height: 1.27;
  text-align: left;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: pointer;
  pointer-events: auto;
  -webkit-tap-highlight-color: transparent;
  transition: background 0.18s;
}
/* background-color only — background alone would drop the painted rules.

   The text keeps its own colour. --accent is cyan and does not flip with the
   theme, so accent text on the light tint measured about 1.3:1 — the same
   one-mode contrast trap as text on an accent fill. Weight carries the
   selection instead, and it reads in both themes. */
.ct-name-row.is-on {
  background-color: rgba(var(--accent-rgb), 0.16);
  font-weight: 700;
}

/* Showing one ring by dimming the other eleven. An outline was the first
   idea and it does not survive contact with a 7.28px band — at that height
   a 1px stroke is the ring. Contrast reads at any size. */
.ct-wheel-wrap.has-ring .circular-segment { opacity: 0.22; }
.ct-wheel-wrap.has-ring .circular-segment.is-ring { opacity: 1; }
@media (hover: none) {
  .ct-wheel-wrap.has-ring .circular-segment:hover { opacity: 0.22; }
  .ct-wheel-wrap.has-ring .circular-segment.is-ring:hover { opacity: 1; }
}
