/* ============================================================
   FocusFlow — components.css
   Shared component styling (cards, buttons, inputs, chips,
   navigation, header, widgets).
   ============================================================ */

/* ---------- Update banner ---------- */
/* Populated and shown by scripts/pwa.js when a new SW version is
   waiting to activate. */
#update-banner {
  position: fixed; top: 0; left: 0; right: 0; z-index: 9999;
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; padding: 12px 16px;
  background: var(--accent-strong, #6366f1);
  color: var(--on-accent, #1E1F22); font-size: 14px; font-family: inherit;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}
#update-banner button {
  flex-shrink: 0; padding: 6px 14px;
  border: 2px solid color-mix(in srgb, var(--on-accent, #1E1F22) 55%, transparent); border-radius: 6px;
  background: transparent; color: var(--on-accent, #1E1F22); font-size: 13px;
  font-weight: 500; cursor: pointer; white-space: nowrap;
}
#update-banner button:hover { background: color-mix(in srgb, var(--on-accent, #1E1F22) 10%, transparent); }

/* ---------- Card ---------- */
.card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-5);
  box-shadow: var(--shadow-sm);
  transition: transform var(--dur-base) var(--ease-out),
              box-shadow var(--dur-base) var(--ease-out);
  /* User text can be a pasted URL or one long unbroken word. A flex child
     can shrink, but a single word cannot break — so without this, one task
     titled with a 60-character string scrolls the entire page sideways.
     `anywhere` (not `break-word`) because it also lets the flex/grid child
     compute a smaller min-content width. Inherited by everything inside. */
  overflow-wrap: anywhere;
}
.card--alt { background: var(--color-surface-alt); }
.card--tinted {
  background: color-mix(in srgb, var(--accent) 16%, var(--color-surface));
  border-color: color-mix(in srgb, var(--accent) 30%, var(--color-border));
}

/* ADHD Clarity mode: bold left border for fast scanning */
[data-theme="clarity"] .card {
  border-left: 5px solid var(--accent);
}

@media (hover: hover) {
  .card--interactive:hover {
    transform: translateY(-2px);
    box-shadow: var(--shadow-md);
  }
}

.card__title {
  font-family: var(--font-display);
  font-size: var(--text-lg);
  font-weight: 600;
  color: var(--color-text-dark);
  margin: 0;
}
.card__subtitle {
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  margin-top: 2px;
}
.card__header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: 44px;
  padding: 0 var(--space-5);
  border-radius: var(--radius-full);
  font-weight: 600;
  font-size: var(--text-sm);
  color: var(--color-text-dark);
  background: var(--color-surface-alt);
  transition: transform var(--dur-fast) var(--ease-out),
              filter var(--dur-fast) var(--ease-out),
              background var(--dur-base) var(--ease-out);
  -webkit-tap-highlight-color: transparent;
}
.btn:active { transform: scale(0.97); }
@media (hover: hover) { .btn:hover { filter: brightness(0.97); } }

/* Pastel: pale fills get a fine outline in a slightly deeper shade of the
   same colour, so they stay soft but never vanish into a white card or a
   pale track. Inset shadow, not border, so nothing shifts by a pixel. The
   attribute selector catches every progress bar / heatmap cell, which are
   styled inline with var(--accent-mark). */
[data-theme="pastel"] .btn--primary,
[data-theme="pastel"] .fab,
[data-theme="pastel"] [style*="--accent-mark"],
[data-theme="blush"] .btn--primary,
[data-theme="blush"] .fab,
[data-theme="blush"] [style*="--accent-mark"],
[data-theme="neutral"] .btn--primary,
[data-theme="neutral"] .fab,
[data-theme="neutral"] [style*="--accent-mark"] {
  box-shadow: inset 0 0 0 1px var(--accent-edge);
}
[data-theme="pastel"] .fab,
[data-theme="blush"] .fab,
[data-theme="neutral"] .fab { box-shadow: inset 0 0 0 1px var(--accent-edge), var(--shadow-lg); }

/* Ink on an accent fill is --on-accent, never #fff: --accent-strong is a
   pale pastel in every theme and white measured 1.6–3.1:1 on it. */
.btn--primary {
  background: var(--accent-strong);
  color: var(--on-accent);
}
/* ---------- Free demo (only ever shown when html.is-demo) ----------
   The paid app never gets the class, so none of this applies to buyers. */
html.is-demo .demo-hide { display: none !important; }
.demo-bar {
  display: flex; align-items: center; justify-content: center; flex-wrap: wrap;
  gap: var(--space-2) var(--space-3);
  padding: var(--space-2) var(--space-4);
  background: color-mix(in srgb, var(--accent) 45%, var(--color-surface));
  color: var(--color-text-dark);
  font-size: var(--text-sm); font-weight: 500; text-align: center;
  border-bottom: 1px solid var(--color-border);
}
.demo-bar__buy {
  display: inline-flex; align-items: center; min-height: 32px;
  padding: 0 var(--space-3);
  border-radius: var(--radius-full);
  background: var(--accent-strong);
  box-shadow: inset 0 0 0 1px var(--accent-edge, transparent);
  color: var(--on-accent);
  font-weight: 600; text-decoration: none;
}
.demo-card {
  border: 2px solid var(--accent-strong);
  background: color-mix(in srgb, var(--accent) 22%, var(--color-surface));
}
.demo-splash-note { margin-top: var(--space-3); font-size: var(--text-sm); color: var(--color-text-muted); }

/* ---------- Colour-coded dashboard widgets ----------
   Each widget wears its own colour from the active theme (a soft tint plus a
   dot by its title), so the dashboard shows the whole palette in every
   theme instead of a wall of identical cards — and neighbours differ.
   The top card and the row under it use slots that are colourful in EVERY
   theme (Neutral's greys sit lower down, Blush's top row mixes pinks and greens).
   Tint strength is --widget-tint (24% light themes, 12% dark): small muted
   text measured 4.57:1+ on every colour in every theme. Low-stimulation
   mode keeps the plain cards. */
.card[data-widget="topThree"] { --w: var(--color-lavender); --w-deep: var(--color-lavender-deep, var(--color-lavender)); }
.card[data-widget="nextUp"] { --w: var(--color-sky); --w-deep: var(--color-sky-deep, var(--color-sky)); }
.card[data-widget="energy"] { --w: var(--color-mauve); --w-deep: var(--color-mauve-deep, var(--color-mauve)); }
.card[data-widget="progress"] { --w: var(--color-sage); --w-deep: var(--color-sage-deep, var(--color-sage)); }
.card[data-widget="deadlines"] { --w: var(--color-peach); --w-deep: var(--color-peach-deep, var(--color-peach)); }
.card[data-widget="schedule"] { --w: var(--color-butter); --w-deep: var(--color-butter-deep, var(--color-butter)); }
.card[data-widget="meals"] { --w: var(--color-apricot); --w-deep: var(--color-apricot-deep, var(--color-apricot)); }
.card[data-widget="habits"] { --w: var(--color-lavender); --w-deep: var(--color-lavender-deep, var(--color-lavender)); }
.card[data-widget="waiting"] { --w: var(--color-sky); --w-deep: var(--color-sky-deep, var(--color-sky)); }
.card[data-widget="meds"] { --w: var(--color-mauve); --w-deep: var(--color-mauve-deep, var(--color-mauve)); }
.card[data-widget="finance"] { --w: var(--color-sage); --w-deep: var(--color-sage-deep, var(--color-sage)); }
.card[data-widget="goals"] { --w: var(--color-peach); --w-deep: var(--color-peach-deep, var(--color-peach)); }
.card[data-widget="braindump"] { --w: var(--color-butter); --w-deep: var(--color-butter-deep, var(--color-butter)); }
.card[data-widget="motivation"] { --w: var(--color-apricot); --w-deep: var(--color-apricot-deep, var(--color-apricot)); }
html:not([data-lowstim="on"]) .card[data-widget] {
  background: color-mix(in srgb, var(--w) var(--widget-tint), var(--color-surface));
  border-color: color-mix(in srgb, var(--w-deep) 45%, var(--color-border));
  /* A band across the top in the card's deeper shade: the colour reads at a
     glance (and in a listing screenshot) even where the tint is soft. */
  box-shadow: inset 0 4px 0 var(--w-deep), var(--shadow-sm);
}
.card[data-widget] .card__title { display: flex; align-items: center; gap: var(--space-2); }
html:not([data-lowstim="on"]) .card[data-widget] .card__title::before {
  content: "";
  flex-shrink: 0;
  width: 10px; height: 10px;
  border-radius: 50%;
  background: var(--w-deep);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--color-text-dark) 18%, transparent);
}

/* ---------- Dashboard stat strip ----------
   Three tinted tiles under the greeting, coloured like the widgets (same
   --widget-tint and top band) from slots that are colourful in every theme. */
.stat-strip { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-3); }
.stat-tile {
  --w: var(--color-sky); --w-deep: var(--color-sky-deep, var(--color-sky));
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  min-height: 72px;
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-lg);
  border: 1px solid var(--color-border);
  background: var(--color-surface);
  box-shadow: var(--shadow-sm);
  color: var(--color-text-dark);
  font: inherit; text-align: left; cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: transform var(--dur-fast) var(--ease-out);
}
.stat-tile:active { transform: scale(0.98); }
.stat-tile[data-slot="sage"] { --w: var(--color-sage); --w-deep: var(--color-sage-deep, var(--color-sage)); }
.stat-tile[data-slot="mauve"] { --w: var(--color-mauve); --w-deep: var(--color-mauve-deep, var(--color-mauve)); }
.stat-tile[data-slot="lavender"] { --w: var(--color-lavender); --w-deep: var(--color-lavender-deep, var(--color-lavender)); }
html:not([data-lowstim="on"]) .stat-tile {
  background: color-mix(in srgb, var(--w) var(--widget-tint), var(--color-surface));
  border-color: color-mix(in srgb, var(--w-deep) 45%, var(--color-border));
  box-shadow: inset 0 4px 0 var(--w-deep), var(--shadow-sm);
}
.stat-tile__value {
  font-family: var(--font-display);
  font-size: var(--text-2xl); font-weight: 800; line-height: 1.15;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.stat-tile__label { font-size: var(--text-xs); color: var(--color-text-muted); line-height: 1.3; }
@media (max-width: 420px) {
  .stat-strip { gap: var(--space-2); }
  .stat-tile { padding: var(--space-3); min-height: 68px; }
  .stat-tile__value { font-size: var(--text-xl); }
}

/* ---------- Theme picker (Settings) ----------
   The preview strip has its own data-theme, so its chips and background
   are that theme's real colours. */
.theme-picker {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: var(--space-3);
}
.theme-option {
  display: flex; flex-direction: column; align-items: stretch;
  text-align: left;
  padding: var(--space-2) var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  border: 1.5px solid var(--color-border);
  background: var(--color-surface);
  color: var(--color-text-dark);
  cursor: pointer;
  font: inherit;
  -webkit-tap-highlight-color: transparent;
  transition: border-color var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.theme-option--active {
  border-color: var(--color-text-dark);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--color-text-dark) 18%, transparent);
}
.theme-option__preview {
  display: flex; flex-wrap: wrap; gap: 6px;
  padding: 10px;
  border-radius: var(--radius-sm);
  background: var(--color-base);
  border: 1px solid var(--color-border);
}
.theme-option__swatch {
  width: 18px; height: 18px;
  border-radius: 50%;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--color-text-dark) 14%, transparent);
}
.theme-option__name {
  margin-top: var(--space-2);
  padding: 0 var(--space-1);
  font-size: var(--text-sm); font-weight: 600;
}
.theme-option--active .theme-option__name::after { content: " ✓"; }
.theme-option__hint {
  padding: 0 var(--space-1);
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}

/* Small labels sitting ON a pastel tint (meal cells, planner blocks).
   --color-text clears 4.5:1 on every tint in every theme; muted grey
   doesn't (2.2–3.5:1 in dark). */
.tint-label { color: var(--color-text); }
.btn--accent {
  /* Per-theme tint strength: at a fixed 55% Dark's light text measured 3.6:1. */
  background: color-mix(in srgb, var(--accent) var(--tint-block), var(--color-surface));
  color: var(--color-text-dark);
}
.btn--ghost { background: transparent; }
.btn--ghost:hover { background: var(--color-surface-alt); }
.btn--sm { min-height: 36px; padding: 0 var(--space-4); font-size: var(--text-xs); }
.btn--block { width: 100%; }
.btn--icon {
  width: 44px; height: 44px;
  min-height: 44px; padding: 0;
  border-radius: var(--radius-full);
}
/* Compact icon button for header corners (smaller, lighter footprint) */
.btn--icon-sm { width: 34px; height: 34px; min-height: 34px; }
.btn--icon-sm .nav-icon { width: 18px; height: 18px; }

/* ---------- Inputs ---------- */
.input, .textarea, .select {
  width: 100%;
  min-height: 44px;
  padding: var(--space-3) var(--space-4);
  background: var(--color-surface);
  border: 1.5px solid var(--color-border);
  border-radius: var(--radius-md);
  color: var(--color-text-dark);
  transition: border-color var(--dur-base) var(--ease-out),
              box-shadow var(--dur-base) var(--ease-out);
}
.textarea { min-height: 100px; resize: vertical; line-height: 1.6; }
.input::placeholder, .textarea::placeholder { color: var(--color-text-muted); }
.input:focus, .textarea:focus, .select:focus {
  outline: none;
  border-color: var(--accent-mark);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 35%, transparent);
}
.field { margin-bottom: var(--space-4); }
.field__label {
  display: block;
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--color-text-dark);
  margin-bottom: var(--space-2);
}

/* ---------- Chips ---------- */
.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 36px;
  padding: 0 var(--space-4);
  border-radius: var(--radius-full);
  background: var(--color-surface-alt);
  border: 1.5px solid transparent;
  font-size: var(--text-sm);
  font-weight: 500;
  /* Chips WRAP rather than run past the edge — a long dopamine-menu label
     ("📸 Take 5 photos of beautiful things") measured 342px inside a 375px
     phone and pushed the page sideways. */
  max-width: 100%;
  white-space: normal;
  text-align: center;
  transition: all var(--dur-fast) var(--ease-out);
  -webkit-tap-highlight-color: transparent;
}
.chip:active { transform: scale(0.96); }
.chip--active {
  background: color-mix(in srgb, var(--accent) 40%, var(--color-surface));
  border-color: var(--accent-mark);
  color: var(--color-text-dark);
}
/* Chip rows WRAP — they never scroll sideways.
   These rows used to be `overflow-x: auto` with the scrollbar hidden, so on
   a phone the Tasks filter row put 677px of chips off-screen behind no
   affordance at all, and the currency picker hid "Custom". A filter you
   can't see is a filter that doesn't exist. */
.chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  padding-bottom: var(--space-2);
}

/* ---------- Badge ---------- */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 10px;
  border-radius: var(--radius-full);
  font-size: var(--text-xs);
  font-weight: 600;
  background: var(--color-surface-alt);
}
/* Status badges carry meaning — "Overdue", "Start today", "chase it" — so
   they have to clear AA as small text. They used to mix 30% of the semantic
   colour into the surface and pair it with a hardcoded mid-tone text colour,
   which measured 3.1–3.6:1 in EVERY theme and didn't re-theme at all. The
   pairs are now per-theme tokens, each measured. */
.badge--danger  { background: var(--badge-danger-bg);  color: var(--badge-danger-fg); }
.badge--warning { background: var(--badge-warning-bg); color: var(--badge-warning-fg); }
.badge--success { background: var(--badge-success-bg); color: var(--badge-success-fg); }

/* ---------- Page header ---------- */
.page-header {
  position: sticky;
  top: 0;
  z-index: 30;
  display: flex;
  align-items: center;
  justify-content: space-between;
  /* Wrap: on a 375px screen a long title plus two action buttons otherwise
     pushes the whole page sideways. */
  flex-wrap: wrap;
  gap: var(--space-3);
  min-height: var(--header-h);
  padding: var(--space-3) var(--space-4);
  background: color-mix(in srgb, var(--color-base) 82%, transparent);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid color-mix(in srgb, var(--color-border) 60%, transparent);
}
.page-header__titles { min-width: 0; }
.page-header__title {
  font-family: var(--font-display);
  font-size: var(--text-2xl);
  font-weight: 600;
  color: var(--color-text-dark);
  line-height: 1.1;
}
.page-header__subtitle {
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}
/* Header actions wrap onto their own line rather than overflowing: "+ Room"
   plus "⏱ 10-min reset" ran 42px past the edge of a 375px screen. */
.page-header__actions { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; justify-content: flex-end; }

/* ---------- Navigation colours ----------
   Themes can give the menus their own colour with --nav-* variables (Blush &
   Sage and Neutral do: a solid bar in one of their button shades, so it's
   obvious which theme you're in). Every var() below falls back to the
   original look, so themes that don't set them are unchanged. */

/* ---------- Bottom navigation (mobile) ---------- */
.bottom-nav {
  position: fixed;
  bottom: 0; left: 0; right: 0;
  z-index: 50;
  display: flex;
  justify-content: space-around;
  align-items: stretch;
  height: calc(var(--bottom-nav-h) + env(safe-area-inset-bottom, 0px));
  padding-bottom: env(safe-area-inset-bottom, 0px);
  background: var(--nav-bg, color-mix(in srgb, var(--color-surface) 88%, transparent));
  -webkit-backdrop-filter: blur(16px);
  backdrop-filter: blur(16px);
  border-top: 1px solid var(--nav-border, var(--color-border));
}
.bottom-nav__item {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  min-width: 44px;
  color: var(--nav-label, var(--color-text-muted));
  font-size: 10px;
  font-weight: 600;
  transition: color var(--dur-base) var(--ease-out);
}
/* Default icon size — without this, icons outside a nav context (e.g. the
   More page list) render at the SVG's intrinsic size and look huge. */
.nav-icon { width: 24px; height: 24px; flex-shrink: 0; }

.bottom-nav__item .nav-icon { width: 24px; height: 24px; }
.bottom-nav__item--active { color: var(--nav-active-ink, var(--accent-text)); }
/* On a coloured bar the current page sits in a light pill. */
[data-theme="blush"] .bottom-nav__item--active,
[data-theme="neutral"] .bottom-nav__item--active {
  background: var(--nav-active-bg);
  border-radius: var(--radius-md);
  margin: 6px 3px;
}

/* ---------- Side rail (tablet) / Sidebar (desktop) ---------- */
.side-nav {
  display: none;
  flex-direction: column;
  flex-shrink: 0;
  width: var(--side-rail-w);
  padding: var(--space-4) var(--space-2);
  gap: var(--space-1);
  background: var(--nav-bg, color-mix(in srgb, var(--color-surface) 70%, transparent));
  border-right: 1px solid var(--nav-border, var(--color-border));
  position: sticky;
  top: 0;
  height: 100dvh;
  overflow-y: auto;
  scrollbar-width: thin;
}
.side-nav__brand {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2);
  margin-bottom: var(--space-3);
  font-family: var(--font-display);
  font-weight: 600;
  color: var(--nav-ink, var(--color-text-dark));
  white-space: nowrap;
  overflow: hidden;
}
.side-nav__brand .brand-word { display: none; }
.side-nav__item {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3);
  border-radius: var(--radius-md);
  color: var(--nav-ink, var(--color-text));
  font-weight: 500;
  font-size: var(--text-sm);
  white-space: nowrap;
  overflow: hidden;
  transition: background var(--dur-base) var(--ease-out), color var(--dur-base) var(--ease-out);
}
.side-nav__item .nav-icon { width: 22px; height: 22px; flex-shrink: 0; }
.side-nav__item .nav-label { display: none; }
.side-nav__item:hover { background: var(--nav-hover, var(--color-surface-alt)); }
.side-nav__item--active {
  background: var(--nav-active-bg, color-mix(in srgb, var(--accent) 35%, var(--color-surface)));
  color: var(--nav-active-ink, var(--color-text-dark));
  font-weight: 600;
}
.side-nav__group-label {
  display: none;
  font-size: var(--text-xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--nav-label, var(--color-text-muted));
  padding: var(--space-3) var(--space-3) var(--space-1);
}

/* Below 1024px (phones AND tablets in portrait, incl. iPad portrait) we
   show the labeled bottom nav. The icon-only side rail was unreadable on
   iPad portrait — only the full labeled sidebar earns its keep. */
@media (min-width: 1024px) {
  .bottom-nav { display: none; }
  .side-nav { display: flex; width: var(--sidebar-w); }
  .side-nav__brand .brand-word { display: inline; }
  .side-nav__item .nav-label { display: inline; }
  .side-nav__group-label { display: block; }
}

/* ---------- FAB ---------- */
.fab {
  position: fixed;
  right: var(--space-4);
  bottom: calc(var(--bottom-nav-h) + env(safe-area-inset-bottom, 0px) + var(--space-4));
  z-index: 45;
  width: 56px; height: 56px;
  border-radius: var(--radius-full);
  background: var(--accent-strong);
  color: var(--on-accent);
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: var(--shadow-lg);
  transition: transform var(--dur-fast) var(--ease-out);
}
.fab:active { transform: scale(0.93); }
.fab .nav-icon { width: 28px; height: 28px; }
/* Only lift the FAB above the bottom-of-screen safe area once the bottom
   nav is gone — i.e. when the sidebar takes over at ≥1024px. */
@media (min-width: 1024px) {
  .fab { bottom: var(--space-6); right: var(--space-6); }
}

/* ---------- Grids ---------- */
.widget-stack { display: flex; flex-direction: column; gap: var(--space-4); }
@media (min-width: 768px) {
  .widget-stack {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    align-items: start;
  }
}
@media (min-width: 1024px) {
  .widget-stack { grid-template-columns: repeat(3, 1fr); }
}
.widget--wide { grid-column: 1 / -1; }

/* ---------- Quick capture bar ---------- */
.quick-capture {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  background: var(--color-surface);
  border: 1.5px solid var(--color-border);
  border-radius: var(--radius-full);
  padding: var(--space-2) var(--space-2) var(--space-2) var(--space-4);
  box-shadow: var(--shadow-sm);
}
.quick-capture__input {
  flex: 1;
  border: none;
  background: none;
  min-height: 40px;
  outline: none;
}
.quick-capture__dest {
  display: flex;
  gap: var(--space-2);
  margin-top: var(--space-3);
}

/* ---------- Progress ring ---------- */
.progress-ring { display: block; margin: 0 auto; }
.progress-ring__bg { stroke: var(--color-border); }
.progress-ring__fill {
  stroke: var(--accent-mark);
  stroke-linecap: round;
  transition: stroke-dashoffset var(--dur-slow) var(--ease-out), stroke var(--dur-slow) var(--ease-out);
  transform: rotate(-90deg);
  transform-origin: center;
}

/* ---------- Energy / mood selector ---------- */
.scale-row { display: flex; gap: var(--space-2); }
.scale-dot {
  flex: 1;
  aspect-ratio: 1;
  max-width: 52px;
  border-radius: var(--radius-full);
  background: var(--color-surface-alt);
  border: 1.5px solid var(--color-border);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--text-lg);
  transition: all var(--dur-fast) var(--ease-out);
}
.scale-dot--selected {
  background: color-mix(in srgb, var(--accent) 45%, var(--color-surface));
  border-color: var(--accent-mark);
  transform: scale(1.12);
  /* A soft ring sits outside the tinted background so the selection reads
     clearly without feeling harsh. */
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-text-dark) 15%, transparent);
}

/* ---------- Completion circle (tasks/habits) ---------- */
.check-circle {
  width: 26px; height: 26px;
  border-radius: var(--radius-full);
  border: 2px solid var(--color-text-muted);
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all var(--dur-base) var(--ease-out);
}
.check-circle--done {
  background: var(--color-success);
  border-color: var(--color-success);
  /* Dark ink, not white: a white tick on the soft success green measured
     2.3–2.8:1 in every light theme. Dark ink is 5:1+ in all five. */
  color: var(--on-accent);
}

/* ---------- Empty state ---------- */
.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: var(--space-6) var(--space-4);
  gap: var(--space-3);
}
.empty-state__art { width: 96px; height: 96px; opacity: 0.9; }
.empty-state__title {
  font-family: var(--font-display);
  font-size: var(--text-lg);
  color: var(--color-text-dark);
}
.empty-state__text { font-size: var(--text-sm); color: var(--color-text-muted); max-width: 32ch; }

/* ---------- Toast ---------- */
.toast-region {
  position: fixed;
  top: calc(env(safe-area-inset-top, 0px) + var(--space-3));
  left: 50%;
  transform: translateX(-50%);
  z-index: 100;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  width: max-content;
  max-width: 92vw;
  pointer-events: none;
}
@media (min-width: 768px) {
  .toast-region { left: auto; right: var(--space-5); transform: none; }
}
.toast {
  pointer-events: auto;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  background: var(--color-text-dark);
  /* Surface colour, not #fff: --color-text-dark is LIGHT in the dark theme,
     where white text on it was unreadable. */
  color: var(--color-surface);
  border-radius: var(--radius-full);
  box-shadow: var(--shadow-lg);
  font-size: var(--text-sm);
  font-weight: 500;
}

/* ---------- Modal / bottom sheet ---------- */
.modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: 90;
  background: rgba(20, 20, 24, 0.4);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
  display: flex;
  align-items: flex-end;
  justify-content: center;
  overscroll-behavior: contain;
}
/* While a sheet is open the page behind can't scroll and, crucially, a
   downward pull can't trigger the browser's pull-to-refresh — which is what
   used to happen when people tried to drag a sheet closed. */
html.modal-open,
html.modal-open body { overflow: hidden; overscroll-behavior-y: none; }
.modal {
  width: 100%;
  max-width: 560px;
  max-height: 92dvh;
  overflow-y: auto;
  overscroll-behavior: contain;
  overflow-wrap: anywhere;   /* same long-string guard as .card */
  background: var(--color-surface);
  border-radius: var(--radius-xl) var(--radius-xl) 0 0;
  /* No top padding: the header supplies it, so it can stick to the very top
     of the sheet while the content scrolls underneath. */
  padding: 0 var(--space-5);
  padding-bottom: calc(var(--space-5) + env(safe-area-inset-bottom, 0px));
  box-shadow: var(--shadow-lg);
  will-change: transform;
}
/* The grab zone: handle + title + close. touch-action:none hands the whole
   gesture to Modal.js's drag-to-dismiss instead of the browser. */
.modal__header {
  position: sticky;
  top: 0;
  z-index: 2;
  margin: 0 calc(-1 * var(--space-5)) var(--space-3);
  padding: var(--space-3) var(--space-5) var(--space-2);
  background: var(--color-surface);
  border-radius: var(--radius-xl) var(--radius-xl) 0 0;
  touch-action: none;
  cursor: grab;
}
.modal__handle {
  width: 44px; height: 5px;
  border-radius: var(--radius-full);
  /* Was --color-border (#EBEBEB on white) — too faint to read as something
     you can grab. */
  background: color-mix(in srgb, var(--color-text-muted) 55%, var(--color-surface));
  margin: 0 auto var(--space-2);
}
.modal__titlebar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  min-height: 44px;
}
.modal__title { margin: 0; flex: 1; min-width: 0; }
.modal__close {
  flex-shrink: 0;
  width: 44px; height: 44px;           /* full touch target */
  margin-right: calc(-1 * var(--space-2));
  display: flex; align-items: center; justify-content: center;
  border: none;
  border-radius: var(--radius-full);
  background: var(--color-surface-alt);
  color: var(--color-text-dark);
  font-size: 18px; line-height: 1;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
@media (hover: hover) { .modal__close:hover { filter: brightness(0.95); } }
.modal__close:active { transform: scale(0.94); }
@media (min-width: 768px) {
  .modal-backdrop { align-items: center; }
  .modal { border-radius: var(--radius-xl); }
  .modal__header { cursor: default; }
  .modal__handle { display: none; }   /* nothing to drag on a centred dialog */
}

/* ---------- Misc layout helpers ---------- */
.row { display: flex; align-items: center; gap: var(--space-3); }
.row--between { justify-content: space-between; }
.col { display: flex; flex-direction: column; }
/* gap-1/4/5 and mt-1/5/6 were used in markup but never defined, so those
   gaps silently collapsed to zero. */
.gap-1 { gap: var(--space-1); }
.gap-2 { gap: var(--space-2); }
.gap-3 { gap: var(--space-3); }
.gap-4 { gap: var(--space-4); }
.gap-5 { gap: var(--space-5); }
.mt-1 { margin-top: var(--space-1); }
.mt-2 { margin-top: var(--space-2); }
.mt-3 { margin-top: var(--space-3); }
.mt-4 { margin-top: var(--space-4); }
.mt-5 { margin-top: var(--space-5); }
.mt-6 { margin-top: var(--space-6); }
.flex-1 { flex: 1; min-width: 0; }
.center { text-align: center; }

/* Disabled controls should look it — the widget reorder arrows rely on
   this to show when a widget is already at the top or bottom. */
.btn:disabled, .btn[disabled] { opacity: .4; cursor: default; }
.btn:disabled:active { transform: none; }

/* ---------- Focus session bar ----------
   A session now survives navigation, so it needs to be visible from
   anywhere — otherwise you wander off and never come back to it. */
.session-bar {
  position: fixed;
  left: var(--space-3);
  right: var(--space-3);
  bottom: calc(var(--bottom-nav-h) + env(safe-area-inset-bottom, 0px) + var(--space-2));
  z-index: 48;
  display: flex;
  align-items: stretch;
  gap: 0;
  max-width: 480px;
  margin: 0 auto;
  overflow: hidden;
  border-radius: var(--radius-full);
  background: var(--color-text-dark);
  color: var(--color-surface);   /* not #fff — the bar is light in the dark theme */
  box-shadow: var(--shadow-lg);
}
.session-bar__fill {
  position: absolute;
  inset: 0 auto 0 0;
  width: 0;
  background: color-mix(in srgb, var(--accent-strong) 55%, transparent);
  transition: width 1s linear;
  pointer-events: none;
}
.session-bar__main {
  position: relative;
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  color: inherit;
  font-size: var(--text-sm);
  font-weight: 600;
  text-align: left;
}
.session-bar__label {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.session-bar__clock {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1, "zero" 0;
  flex-shrink: 0;
}
.session-bar__end {
  position: relative;
  width: 44px;
  flex-shrink: 0;
  color: inherit;
  opacity: .7;
}
.session-bar__end:hover { opacity: 1; }
.session-bar[data-mode="break"] .session-bar__fill {
  background: color-mix(in srgb, var(--color-sage) 65%, transparent);
}
/* The FAB sits above the bar rather than on top of it. */
.session-bar:not(.hidden) ~ .fab {
  bottom: calc(var(--bottom-nav-h) + env(safe-area-inset-bottom, 0px) + var(--space-4) + 56px);
}
@media (min-width: 1024px) {
  .session-bar {
    left: auto;
    right: var(--space-6);
    bottom: var(--space-6);
    width: 320px;
  }
  .session-bar:not(.hidden) ~ .fab { bottom: calc(var(--space-6) + 72px); }
}

/* ---------- Low-stimulation mode ----------
   For sensory-overload days, and for the people who find the pastel
   wash itself distracting. Mutes colour, removes the ambient gradient,
   hides decorative emoji and drops the dashboard to a single column. */
[data-lowstim="on"] body::before { display: none; }
[data-lowstim="on"] #app { filter: saturate(0.45); }
[data-lowstim="on"] .card--tinted {
  background: var(--color-surface-alt);
  border-color: var(--color-border);
}
[data-lowstim="on"] .card { box-shadow: none; }
[data-lowstim="on"] .widget-stack {
  display: flex;
  flex-direction: column;
}
[data-lowstim="on"] .widget--wide { grid-column: auto; }
/* Decorative-only glyphs are marked aria-hidden; content emoji aren't. */
[data-lowstim="on"] .card [aria-hidden="true"]:not(svg):not(.modal__handle):not(.theme-option__preview),
[data-lowstim="on"] .empty-state > div:first-child {
  display: none;
}
[data-lowstim="on"] .fab { box-shadow: var(--shadow-sm); }

/* ---------- Readable typeface ----------
   Not a novelty font: a wider, heavier stack with more room between
   letters, words and lines, which is what actually helps. */
[data-fontface="readable"] {
  --font-body: Verdana, "DejaVu Sans", Tahoma, Geneva, sans-serif;
  --font-display: Verdana, "DejaVu Sans", Tahoma, Geneva, sans-serif;
  --font-mono: Verdana, "DejaVu Sans", Tahoma, Geneva, sans-serif;
  --font-tnum: Verdana, "DejaVu Sans", Tahoma, Geneva, sans-serif;
}
[data-fontface="readable"] body {
  line-height: 1.85;
  letter-spacing: 0.02em;
  word-spacing: 0.08em;
}
[data-fontface="readable"] h1,
[data-fontface="readable"] h2,
[data-fontface="readable"] h3,
[data-fontface="readable"] h4 {
  letter-spacing: 0;
  line-height: 1.4;
}
[data-fontface="readable"] .text-xs { font-size: 0.8125rem; }

/* Offline indicator dot in header */
.offline-dot {
  width: 8px; height: 8px;
  border-radius: var(--radius-full);
  background: var(--color-warning);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-warning) 30%, transparent);
}

/* ============================================================
   Print
   People print planners. There were no print rules at all, so
   Ctrl+P produced the sidebar, the bottom nav, the FAB and a
   stack of blank pages — the app shell is built from 100dvh
   containers, which fight pagination.
   Everything below strips the app chrome and lets the content
   flow as an ordinary document.
   ============================================================ */
@media print {
  /* App chrome — none of it belongs on paper. */
  .side-nav,
  .bottom-nav,
  .fab,
  .session-bar,
  .skip-link,
  .toast-region,
  .install-banner,
  .modal-backdrop,
  #update-banner,
  #ff-db-blocked,
  #splash,
  .page-header__actions,
  .quick-capture,
  .chip-row,
  .btn {
    display: none !important;
  }

  /* Release the fixed-height shell so pages break naturally instead of
     printing one screenful and a run of blanks. */
  .demo-bar { display: none !important; }
  html, body, #app, .app-shell, .app-main, .app-content {
    height: auto !important;
    min-height: 0 !important;
    max-height: none !important;
    overflow: visible !important;
    display: block !important;
    padding: 0 !important;
    margin: 0 !important;
    max-width: none !important;
  }
  body::before { display: none !important; }

  body {
    background: #fff !important;
    color: #000 !important;
    font-size: 11pt;
  }

  /* Cards become plain sections — no shadows, no tints, no wasted ink,
     and never split across a page break mid-card. */
  .card {
    background: #fff !important;
    border: 1px solid #bbb !important;
    box-shadow: none !important;
    border-radius: 6px;
    padding: 10pt !important;
    margin-bottom: 8pt;
    break-inside: avoid;
    page-break-inside: avoid;
  }
  .card--tinted, .card--alt { background: #fff !important; }

  .page-header {
    position: static !important;
    backdrop-filter: none !important;
    -webkit-backdrop-filter: none !important;
    background: none !important;
    border-bottom: 2px solid #000 !important;
    padding: 0 0 6pt 0 !important;
    margin-bottom: 10pt;
  }
  .page-header__title { color: #000 !important; }

  .widget-stack { display: block !important; }
  .badge { border: 1px solid #999; background: #fff !important; color: #000 !important; }
  .text-muted { color: #444 !important; }
  .check-circle { border-color: #000 !important; }
  h1, h2, h3, h4 { color: #000 !important; break-after: avoid; page-break-after: avoid; }

  @page { margin: 14mm; }
}
