/* ── Fonts (self-hosted). Tactile: Fraunces + Hanken Grotesk. Glass: Inter Tight.
   Paper: Newsreader + IBM Plex Sans. ── */
@font-face {
  font-family: 'Fraunces';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('fonts/fraunces-roman.woff2') format('woff2');
}
@font-face {
  font-family: 'Fraunces';
  font-style: italic;
  font-weight: 400 600;
  font-display: swap;
  src: url('fonts/fraunces-italic.woff2') format('woff2');
}
@font-face {
  font-family: 'Hanken Grotesk';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('fonts/hanken-grotesk.woff2') format('woff2');
}
/* Glass theme */
@font-face {
  font-family: 'Inter Tight';
  font-style: normal;
  font-weight: 200 700;
  font-display: swap;
  src: url('fonts/inter-tight.woff2') format('woff2');
}
/* Paper theme */
@font-face {
  font-family: 'Newsreader';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url('fonts/newsreader-roman.woff2') format('woff2');
}
@font-face {
  font-family: 'Newsreader';
  font-style: italic;
  font-weight: 400 600;
  font-display: swap;
  src: url('fonts/newsreader-italic.woff2') format('woff2');
}
@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url('fonts/ibm-plex-sans.woff2') format('woff2');
}

:root {
  --bg: #0f1117;
  --surface: rgba(26,29,39,0.78);
  --surface2: rgba(34,38,58,0.70);
  --accent: #6c8ef5;
  --accent2: #f5a623;
  --green: #4caf82;
  --red: #e06c75;
  --text: #e8eaf6;
  --muted: #7b7f96;
  /* ── Material tokens (surface depth, hairlines, type, grain) ──
     --ink/--ink-soft are declared on body (next to its base rule), not here. */
  --line: rgba(160,168,220,0.16);
  --surface-solid: #1e2030;
  --surface-hi:    #262a40;
  --surface-lo:    #171823;
  --font-display: 'Fraunces', Georgia, 'Times New Roman', serif;
  --font-body:    'Hanken Grotesk', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --radius: 16px;
  --grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.5'/%3E%3C/svg%3E");
  --shadow: 0 0 22px rgba(108,142,245,0.22), 0 4px 18px rgba(0,0,0,0.55);
  --scrim: rgba(0,0,0,0.45);
  --card-bg-photo: rgba(15,17,23,0.75);
  --cal-card-bg-photo: rgba(15,17,23,0.55);
  --cal-day-bg-photo: rgba(34,38,58,0.22);
  --mini-card-bg-photo: rgba(34,38,58,0.70);
  --strip-bg-photo: rgba(15,17,23,0.70);

  /* ── Type scale ───────────────────────────────────────────────
     xs   0.70rem  ~11px  — tiny labels, calendar micro-text
     sm   0.78rem  ~12px  — secondary meta, badges, timestamps
     base 0.88rem  ~14px  — body copy, list items, inputs
     md   1.00rem  ~16px  — card headings, nav labels
     lg   1.15rem  ~18px  — modal titles
     xl   1.50rem  ~24px  — weather temperature display (scaled down from 3rem)
     ──────────────────────────────────────────────────────────── */
  --text-xs:   0.70rem;
  --text-sm:   0.78rem;
  --text-base: 0.88rem;
  --text-md:   1.00rem;
  --text-lg:   1.15rem;
  --text-xl:   1.50rem;

  /* Day/Agenda typography consolidation — semantic names aliased into the
     ladder above (not a second scale) so the two stay in sync: bumping
     --text-md still moves --fs-title. Use these for new markup instead of
     ad hoc font-size values. */
  --fs-title: var(--text-md);   /* row/section headline: event title, "Bills Due Today" */
  --fs-item:  var(--text-base); /* regular row content: bill/task line, event time */
  --fs-meta:  var(--text-sm);   /* secondary annotation: assignee badge, placeholder text */

  /* Calendar period slide (swipe left/right). Keep CAL_SLIDE_MS in
     family-hub.js in sync — it times the ghost teardown. */
  --cal-slide: 260ms;

  /* Weights */
  --weight-normal:  400;
  --weight-medium:  500;
  --weight-semi:    600;
  --weight-bold:    700;

  /* Line heights */
  --leading-tight:  1.3;
  --leading-normal: 1.5;
  --leading-loose:  1.65;
}

/* ════════════════════════════════════════════════════════════════
   👁  ACCESSIBLE MODE — High Contrast · Okabe-Ito colorblind-safe palette
   Uses sky-blue accent, orange highlight, vermillion (not red), bluish-green
   (not pure green) — all distinguishable by deuteranopes and protanopes.
   ════════════════════════════════════════════════════════════════ */
body.colorblind-mode {
  --bg:                 #0d1117;
  --surface:            rgba(18, 24, 38, 0.90);
  --surface2:           rgba(26, 34, 54, 0.85);
  --accent:             #56B4E9;        /* Okabe sky-blue — primary interactive */
  --accent2:            #E69F00;        /* Okabe orange — secondary highlight */
  --green:              #009E73;        /* Okabe bluish-green — safe for deuteranopia */
  --red:                #D55E00;        /* Okabe vermillion — not pure red */
  --text:               #F5F5F5;
  --muted:              #8899bb;
  --line:          rgba(120,140,180,0.18);
  --surface-solid: #161c28;
  --surface-hi:    #1e2636;
  --surface-lo:    #10141e;
  --shadow:             0 0 22px rgba(86,180,233,0.22), 0 4px 18px rgba(0,0,0,0.55);
  --scrim:              rgba(0,0,0,0.45);
  --card-bg-photo:      rgba(13,17,23,0.80);
  --cal-card-bg-photo:  rgba(13,17,23,0.55);
  --cal-day-bg-photo:   rgba(18,24,38,0.28);
  --mini-card-bg-photo: rgba(18,24,38,0.75);
  --strip-bg-photo:     rgba(13,17,23,0.75);
}

/* Today/current-week highlights follow --accent (sky blue here) through the
   material tokens (--today-bg, --today-ring), so they need no override. */

/* ── Light mode overrides ── */
body.light-mode {
  --bg: #f0f2f8;
  --surface: rgba(255,255,255,0.82);
  --surface2: rgba(228,231,240,0.75);
  --accent: #4a6cf7;
  --accent2: #e08c10;
  --green: #2e9e68;
  --red: #d0404a;
  --text: #1a1d2e;
  --muted: #6b7080;
  /* warm cream paper stock */
  --line:          rgba(70,55,30,0.14);
  --surface-solid: #fbf8f1;
  --surface-hi:    #fffdf8;
  --surface-lo:    #efe7d7;
  --ink-soft:      #7c766a;
  --shadow: 0 4px 24px rgba(0,0,0,0.18), 0 2px 8px rgba(0,0,0,0.10);
  --scrim: rgba(255,255,255,0.50);
  --card-bg-photo: rgba(255,255,255,0.82);
  --cal-card-bg-photo: rgba(255,255,255,0.55);
  --cal-day-bg-photo: rgba(240,242,248,0.28);
  --mini-card-bg-photo: rgba(255,255,255,0.80);
  --strip-bg-photo: rgba(255,255,255,0.78);
}

* { box-sizing: border-box; margin: 0; padding: 0; }

/* iOS Safari's elastic bounce/rubber-band is a document-level effect, not
   tied to whether body actually overflows: body's own `overflow: hidden`
   below doesn't stop it. `overscroll-behavior: none` on both html and body
   is what actually pins the page down so a drag can't pan/slide the whole
   viewport; it doesn't affect scrolling inside a genuinely scrollable
   descendant (.modal, .sidebar, #weekView, …), only the document root. */
html {
  height: 100%;
  overflow: hidden;
  overscroll-behavior: none;
}

body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  font-size: var(--text-base);
  line-height: var(--leading-normal);
  height: 100dvh;
  overflow: hidden;
  overscroll-behavior: none;
  display: flex;
  flex-direction: column;
  padding: 10px 14px;
  box-sizing: border-box;
  /* A finger dragged across the wall panel kept selecting text. Nothing on
     the page is selectable except fields you type into (rule below). */
  -webkit-user-select: none;
  user-select: none;
}
/* Aliases of the theme's text colors. They live on body, not :root, because
   the themes set --text/--muted on body: a var() resolves where it's declared,
   so on :root these froze to the dark theme's values and light mode got
   near-white text. Each theme's own override (e.g. light mode's
   --ink-soft) still wins on specificity. */
body {
  --ink: var(--text);
  --ink-soft: var(--muted);

  /* ── Material tokens ──────────────────────────────────────────
     Component CSS reads these; a Theme (Tactile = these defaults,
     body.theme-glass, body.theme-paper) only sets them, so every
     component follows the theme without per-theme selectors. They
     live on body (not :root) because they reference theme colors.
     Colors (light/dark/accessible) stay on their own axis. */
  --display-weight: var(--weight-semi);
  --card-bg:
    radial-gradient(120% 80% at 50% -10%, color-mix(in srgb, var(--accent) 13%, transparent), transparent 60%),
    linear-gradient(180deg, var(--surface-hi) 0%, var(--surface-solid) 18%, var(--surface-solid) 82%, var(--surface-lo) 100%);
  --cal-card-bg: var(--card-bg);
  --card-border: var(--line);
  --card-shadow:
    0 1px 1px rgba(0,0,0,.28),
    0 3px 7px rgba(0,0,0,.20),
    0 14px 30px rgba(0,0,0,.28),
    inset 0 1px 0 var(--surface-hi),
    inset 0 -1px 0 var(--surface-lo);
  --card-blur: none;
  --cal-card-blur: none;
  --grain-opacity: 0.05;
  --grain-blend: soft-light;
  /* controls: view tabs, period arrows, forecast toggle, .btn */
  --control-radius: 11px;
  --tab-radius: 9px;
  --control-bg: linear-gradient(180deg, var(--surface-hi), var(--surface-solid));
  --control-border: var(--line);
  --control-shadow: 0 1px 2px rgba(0,0,0,.28);
  --control-active-bg: var(--accent);
  --control-active-ink: #fff;
  --control-active-border: var(--accent);
  --control-active-shadow: 0 3px 10px color-mix(in srgb, var(--accent) 30%, transparent);
  /* recessed fills: hourly tiles, 6-month strip cards */
  --well-bg: color-mix(in srgb, var(--surface-lo) 55%, var(--surface-solid));
  --well-shadow: inset 0 1px 2px rgba(0,0,0,.30);
  /* calendar day columns (Week) and cells (Month) */
  --col-bg: var(--well-bg);
  --col-border: color-mix(in srgb, var(--text) 14%, transparent);
  --col-radius: 10px;
  --col-gap: 4px;
  --col-blur: none;
  --today-bg: color-mix(in srgb, var(--accent) 16%, var(--col-bg));
  --week-bg: color-mix(in srgb, var(--accent) 7%, var(--col-bg));
  --today-ring: 0 0 0 1.5px color-mix(in srgb, var(--accent) 60%, transparent);
  --event-radius: 8px;
  --chip-radius: 999px;
  /* modals: always opaque (a translucent modal goes illegible over a busy
     photo), so Glass frosts with blur instead of a thin wash */
  --modal-bg: linear-gradient(180deg, var(--surface-hi) 0%, var(--surface-solid) 18%, var(--surface-solid) 82%, var(--surface-lo) 100%);
  --modal-border: var(--line);
  --modal-shadow:
    0 8px 48px rgba(0,0,0,.6),
    0 2px 12px rgba(0,0,0,.4),
    inset 0 1px 0 var(--surface-hi);
  --modal-blur: none;
  --modal-scrim: rgba(0,0,0,.7);
  --modal-scrim-blur: none;
}
/* Tactile in light colors: warm shadows, multiply grain */
body.light-mode {
  --card-shadow:
    0 1px 1px rgba(70,55,30,.08),
    0 3px 6px rgba(70,55,30,.08),
    0 14px 30px rgba(70,55,30,.10),
    inset 0 1px 0 var(--surface-hi),
    inset 0 -1px 0 var(--surface-lo);
  --grain-opacity: 0.06;
  --grain-blend: multiply;
  --control-shadow: 0 1px 2px rgba(70,55,30,.12);
  --well-shadow: inset 0 1px 2px rgba(70,55,30,.08);
  /* blue mixed into cream reads grey; today is the bright sheet + accent ring */
  --today-bg: var(--surface-hi);
  --week-bg: color-mix(in srgb, var(--surface-hi) 55%, var(--col-bg));
  --modal-shadow:
    0 8px 48px rgba(70,55,30,.18),
    0 2px 12px rgba(70,55,30,.10),
    inset 0 1px 0 var(--surface-hi);
}
input, textarea, [contenteditable]:not([contenteditable="false"]) {
  -webkit-user-select: text;
  user-select: text;
}

/* ── Background photo ── */
#bgPhoto {
  position: fixed;
  inset: 0;
  z-index: -1;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  transition: opacity 1.2s ease;
  opacity: 0;
}
#bgPhoto.visible { opacity: 1; }
/* scrim so UI stays readable — color controlled by mode variables */
#bgPhoto::after {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--scrim);
}

/* When a photo is active, make cards semi-transparent (material tokens, so a
   Theme declared later in the file can still choose its own surfaces) */
body.has-bg {
  --card-bg: var(--card-bg-photo);
  --card-blur: blur(6px);
  --cal-card-bg: var(--cal-card-bg-photo);
  /* day cells and week columns let the photo show through */
  --col-bg: var(--cal-day-bg-photo);
  --col-blur: blur(4px);
}
/* During a photo crossfade, drop the per-frame backdrop blur — recomputing it
   for every card while the photo fades stalls the main thread and drops clicks.
   Cards keep their semi-transparent background; blur returns once the fade ends. */
body.bg-fading .card {
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
body.has-bg .day-hour-row {
  background: transparent;
}
/* Day numbers and event pills stay fully opaque */
.cal-day-num,
.cal-event {
  opacity: 1 !important;
  position: relative;
  z-index: 1;
}

/* ── Header ── */
/* ── App title inside calendar card header ── */
.cal-app-title {
  font-size: 1.6rem;
  font-weight: var(--weight-bold);
  letter-spacing: -0.5px;
  line-height: 1.2;
  white-space: nowrap;
  display: flex;
  align-items: center;
  gap: 6px;
}
.cal-app-title .cal-app-title-text span { color: var(--accent); }
.cal-app-emoji {
  /* Now a real <button> (was an <em>) so it's focusable/keyboard-operable —
     reset the browser's default button chrome back to how the plain emoji
     used to look on desktop. Mobile re-adds visible chrome below. */
  font-size: 1.6rem;
  line-height: 1;
  font-style: normal;
  flex-shrink: 0;
  font-family: inherit;
  border: none;
  background: none;
  padding: 0;
  color: inherit;
}
/* ── Calendar Palettes ── */
/* A palette assigns every calendar color, so hand pickers step aside. */
.cal-palette-on input.lc-color { display: none; }
.cal-palette-on .cal-color-wrap,
.cal-palette-on .cal-color-wrap input[type=color] { cursor: default; }
.palette-dot {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  flex-shrink: 0;
  display: inline-block;
  border: 1px solid var(--line);
}
#calPalettePreview { margin-top: 8px; }
.palette-preview-row {
  display: flex;
  align-items: center;
  gap: 4px;
  margin-top: 4px;
}
.palette-preview-row > span:first-child {
  width: 56px;
  font-size: var(--text-xs);
  color: var(--muted);
}

/* ── Control card (top of sidebar) ── */
#controlCard {
  overflow: visible;
}
.ctrl-header {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 14px;
  cursor: pointer;
  user-select: none;
}
html:not(.touch-input) .ctrl-header:hover { background: rgba(255,255,255,.04); }
.ctrl-chevron {
  font-size: .65rem;
  color: var(--muted);
  transition: transform .2s;
  flex-shrink: 0;
}
.status-pill {
  flex: 1;
  text-align: center;
  padding: 4px 12px;
  border-radius: 20px;
  font-size: var(--text-xs);
  font-weight: var(--weight-bold);
  letter-spacing: .08em;
  cursor: pointer;
  transition: opacity .15s, transform .1s;
}
html:not(.touch-input) .status-pill:hover { opacity: .85; transform: scale(1.03); }
.status-connected {
  /* Healthy is the normal state, so it stays quiet: a green dot and muted
     text. Only the red STATUS state below is meant to catch the eye. */
  background: none;
  color: var(--muted);
  font-weight: var(--weight-semi);
  letter-spacing: 0;
}
.status-connected::before {
  content: '';
  display: inline-block;
  width: .55em; height: .55em;
  margin-right: .45em;
  border-radius: 50%;
  background: var(--green);
  vertical-align: .08em;
}
.status-fix {
  background: #7d2e2e;
  color: #f0a8a8;
  box-shadow: 0 0 8px rgba(125,46,46,.5);
  animation: pulse-red 2s ease-in-out infinite;
}
@keyframes pulse-red {
  0%, 100% { box-shadow: 0 0 8px rgba(125,46,46,.5); }
  50%       { box-shadow: 0 0 16px rgba(200,60,60,.8); }
}
@keyframes konamiRainbow {
  0%   { filter: hue-rotate(0deg) saturate(2) brightness(1.1); }
  25%  { filter: hue-rotate(90deg) saturate(2.5) brightness(1.2); }
  50%  { filter: hue-rotate(180deg) saturate(2) brightness(1.1); }
  75%  { filter: hue-rotate(270deg) saturate(2.5) brightness(1.2); }
  100% { filter: hue-rotate(360deg) saturate(1) brightness(1); }
}
@keyframes konamiText {
  0%   { opacity: 0; transform: translate(-50%,-50%) scale(0.5); }
  15%  { opacity: 1; transform: translate(-50%,-50%) scale(1.1); }
  80%  { opacity: 1; transform: translate(-50%,-50%) scale(1); }
  100% { opacity: 0; transform: translate(-50%,-50%) scale(0.9); }
}
body.konami-mode {
  animation: konamiRainbow 3s ease forwards;
}
@keyframes gandalfShake {
  0%,100% { transform: translateX(0); }
  10%,50%,90% { transform: translateX(-10px) rotate(-1deg); }
  30%,70% { transform: translateX(10px) rotate(1deg); }
}
.gandalf-shake {
  animation: gandalfShake 0.9s ease-in-out;
}
.ctrl-body {
  border-top: 1px solid rgba(255,255,255,.06);
  padding: 10px 14px 12px;
}
.ctrl-section-label {
  font-size: var(--text-xs);
  font-weight: var(--weight-bold);
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: .07em;
  margin-bottom: 8px;
}
.ctrl-actions {
  margin-top: 10px;
  display: flex;
  justify-content: flex-end;
}
.ctrl-signout-btn {
  background: var(--surface2);
  color: var(--muted);
}
.ctrl-status-row {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 6px 0;
  border-bottom: 1px solid rgba(255,255,255,.04);
  cursor: default;
}
.ctrl-status-row:last-child { border-bottom: none; }
.ctrl-status-dot {
  width: 9px; height: 9px;
  border-radius: 50%;
  flex-shrink: 0;
  margin-top: 3px;
}
.ctrl-status-dot.ok   { background: #4caf82; box-shadow: 0 0 5px rgba(76,175,130,.6); }
.ctrl-status-dot.fail { background: var(--red); box-shadow: 0 0 5px color-mix(in srgb, var(--red) 60%, transparent); }
.ctrl-status-text {
  display: flex;
  flex-direction: column;
  gap: 1px;
  flex: 1;
  min-width: 0;
}
.ctrl-status-label {
  font-size: var(--text-xs);
  font-weight: var(--weight-bold);
  color: var(--text);
}
.ctrl-status-desc {
  font-size: .7rem;
  color: var(--muted);
  line-height: 1.3;
}

/* ── Budget total summary card ── */
#budgetTotalCard {
  padding: 8px 16px;
  border-bottom: 1px solid color-mix(in srgb, var(--red) 20%, transparent);
  background: color-mix(in srgb, var(--red) 6%, transparent);
}
.budget-total-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
}
.budget-total-label {
  font-size: var(--text-xs);
  color: var(--muted);
  font-weight: var(--weight-medium);
}
.budget-total-amount {
  font-size: var(--text-base);
  font-weight: var(--weight-bold);
  color: var(--red);
  white-space: nowrap;
}
.btn {
  /* a plain .btn (no variant) is a theme control; it used to fall through
     to the browser's default white button in every theme */
  padding: 7px 15px; border: 1px solid var(--control-border); border-radius: 8px;
  background: var(--control-bg); color: var(--text);
  box-shadow: var(--control-shadow);
  cursor: pointer; font-size: var(--text-base); font-weight: var(--weight-semi);
  font-family: inherit;
  transition: opacity .15s;
}
html:not(.touch-input) .btn:hover { opacity: .85; }
.btn-primary { background: var(--accent); color: #fff; border-color: transparent; }
.btn-danger  { background: var(--red);    color: #fff; border-color: transparent; }
.btn-sm      { padding: 3px 8px; font-size: var(--text-xs); }

/* ── Config banner ── */
#configBanner {
  flex-shrink: 0;
  background: var(--surface2);
  border: 1px solid var(--accent2);
  border-radius: var(--radius);
  padding: 16px 20px;
  margin-bottom: 24px;
  font-size: var(--text-base);
  display: none;
}
#configBanner strong { color: var(--accent2); }
#configBanner code {
  background: rgba(255,255,255,.08);
  border-radius: 4px;
  padding: 2px 6px;
  font-size: var(--text-sm);
}

/* ── Main layout ── */
.layout {
  /* Also the width of the Day/Agenda beside column. 300px up to a 1500px-wide
     screen, then a fifth of the width (384px at 1920), capped at 420px: a
     fixed 300px left sidebar text cramped on big screens while the calendar,
     which needs it least in Day/Agenda, took all the extra width. */
  --sidebar-w: clamp(300px, 20vw, 420px);
  display: grid;
  grid-template-columns: 1fr var(--sidebar-w);
  gap: 14px;
  align-items: stretch;   /* both columns fill the grid row height */
  flex: 1;                /* grow to fill remaining body height */
  min-height: 0;          /* allow shrinking below content size */
}
.cal-column {
  display: flex;
  flex-direction: column;
  min-height: 0;
}
.sidebar {
  display: flex;
  flex-direction: column;
  gap: 10px;
  overflow-y: auto;
  min-height: 0;
}
/* Sidebar cards must not shrink — let the sidebar scroll instead */
.sidebar > * { flex-shrink: 0; }

/* ── Bottom widget strip (inside .cal-column, below #yearStrip) ── */
/* auto-fit spreads cards evenly; align-items: stretch makes all cards same height */
#bottomStrip {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 14px;
  align-items: stretch;
}
#bottomStrip > .card {
  display: flex;
  flex-direction: column;
}
#bottomStrip > .card > .card-body {
  flex: 1;
}

/* ── Beside column (Day/Agenda widgets; applyWidgetPlacement in family-hub.js) ──
   Shares the calendar's row inside .cal-column. Same rules as .sidebar: it
   scrolls, and its cards keep flex-shrink: 0 so they don't compress. */
.cal-main-row {
  flex: 1;
  display: flex;
  gap: 14px;
  min-height: 0;
}
.cal-main-row > .cal-card { min-width: 0; }
.beside-strip {
  flex: 0 0 var(--sidebar-w);
  display: flex;
  flex-direction: column;
  gap: 10px;
  overflow-y: auto;
  overscroll-behavior: contain;
  min-height: 0;
  min-width: 0;
}
.beside-strip > * { flex-shrink: 0; }
.cal-main-row.beside-right > .beside-strip { order: 1; }

/* ── Responsive ── */

/* Narrow desktop: sidebar shrinks to 260px */
@media (max-width: 1100px) {
  .layout { --sidebar-w: 260px; }
}

/* Tablet: sidebar drops below calendar, so yearStrip goes full width */
@media (max-width: 860px) {
  .layout { grid-template-columns: 1fr; gap: 16px; }
  .sidebar { flex-direction: row; flex-wrap: wrap; }
  .sidebar > .card { flex: 1 1 280px; }
  .mini-grid { grid-template-columns: repeat(3, 1fr); }
  .cal-day { min-height: 58px; }
  /* Single-column layout: bottom strip uses one full-width column */
  #bottomStrip { grid-template-columns: 1fr; }
}

/* ── Portrait / Landscape orientation layout ── */
/* Applied by JS (applyLayoutMode) via body.portrait-mode for forced portrait,
   or auto-detected from @media (orientation: portrait) when layoutMode === 'auto' */
body.portrait-mode .layout,
body:not(.force-landscape).auto-portrait .layout {
  grid-template-columns: 1fr;
  gap: 16px;
}
body.portrait-mode .sidebar,
body:not(.force-landscape).auto-portrait .sidebar {
  flex-direction: row;
  flex-wrap: wrap;
  overflow-y: visible;
}
body.portrait-mode .sidebar > .card,
body:not(.force-landscape).auto-portrait .sidebar > .card {
  flex: 1 1 260px;
}
body.portrait-mode .mini-grid,
body:not(.force-landscape).auto-portrait .mini-grid {
  grid-template-columns: repeat(3, 1fr);
}
body.portrait-mode #bottomStrip,
body:not(.force-landscape).auto-portrait #bottomStrip {
  grid-template-columns: 1fr;
}

/* ── Card ── */
.card {
  background: var(--surface);
  border-radius: var(--radius);
  border: 1px solid color-mix(in srgb, var(--accent) 38%, transparent);
  box-shadow: var(--shadow);
  overflow: hidden;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}
/* Calendar card fills remaining cal-column height */
.cal-card {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 0;
}
.cal-card .card-body {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 0;
  padding-bottom: 0;
  overflow: hidden;
}
.card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 14px 8px;
  border-bottom: 1px solid rgba(255,255,255,.06);
  overflow: hidden;
}
.card-header h2 {
  font-size: var(--text-base);
  font-weight: var(--weight-semi);
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 1;
  min-width: 0;
}
.card-header > div {
  min-width: 0;
  flex-shrink: 1;
  flex-wrap: wrap;
}
/* A title block (h2 + subtitle) stretches like a bare h2 does. Without this,
   the injected ⠿ drag handle made three header children and space-between
   centered the title (Budget). */
.card-header > .card-title-wrap { flex: 1; }
.card-body { padding: 12px 14px; }
/* ↺ in the headers of Word of the Day, Dad Joke, Quote, News, Stocks and
   Home (was the same inline style repeated six times) */
.card-refresh-btn {
  background: none; border: none;
  color: var(--muted); cursor: pointer;
  font-size: 1.6rem; line-height: 1;
}

/* ── Calendar Swipe / Period Slide ──
   #calViews holds the four period views and is what the finger drags. The
   outgoing period is cloned into a .cal-view-ghost pinned over the wrapper,
   so the ghost and the freshly rendered view can travel past each other in
   one transition. Overflow clipping comes from .cal-card .card-body. */
.cal-views {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  position: relative;
  /* We own horizontal panning; vertical scrolling stays with the browser. */
  touch-action: pan-y pinch-zoom;
  transition: transform var(--cal-slide) cubic-bezier(.22,.61,.36,1),
              opacity   var(--cal-slide) ease;
}
/* Finger down: the wrapper tracks the touch, so no easing in the way. */
.cal-views.cal-dragging { transition: none; }
/* Child combinator, not a bare .cal-view-ghost: .cal-grid also sets
   position (relative, for its multi-day overlay) and would otherwise win on
   source order at equal specificity, dropping the ghost back into flow. */
.cal-views > .cal-view-ghost {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 3;
}
.cal-view-ghost,
.cal-views.cal-sliding > * {
  transition: transform var(--cal-slide) cubic-bezier(.22,.61,.36,1),
              opacity   var(--cal-slide) ease;
}
/* The week grid scrolls horizontally on narrow screens. That gesture is its
   own until the grid is at the edge the swipe heads for; then setupCalSwipe()
   pages the week (calScrollerAtEdge). No sideways overscroll, so iOS doesn't
   rubber-band the grid under the page slide. */
#weekView { touch-action: pan-x pan-y pinch-zoom; overscroll-behavior-x: none; }

@media (prefers-reduced-motion: reduce) {
  .cal-views, .cal-view-ghost, .cal-views.cal-sliding > * { transition: none; }
}

/* ── Month Calendar ── */
.cal-month-nav {
  display: flex;
  align-items: center;
  gap: 10px;
}
.cal-month-nav button {
  background: var(--control-bg);
  border: 1px solid var(--control-border);
  box-shadow: var(--control-shadow);
  color: var(--text);
  width: 44px; height: 38px;
  border-radius: var(--control-radius);
  cursor: pointer;
  font-size: 1.5rem;
  line-height: 1;
  display: flex; align-items: center; justify-content: center;
  transition: background .15s, transform .1s;
  flex-shrink: 0;
}
html:not(.touch-input) .cal-month-nav button:hover  { background: var(--accent); }
.cal-month-nav button:active { transform: scale(0.93); }
.cal-title-zone {
  display: flex;
  align-items: center;
  gap: 10px;
}
#backToTodayBtn {
  width: auto !important;
  padding: 5px 13px !important;
  font-size: var(--text-xs) !important;
  font-weight: var(--weight-bold);
  color: #fff !important;
  border: none !important;
  background: #e05c2a !important;
  border-radius: 20px !important;
  letter-spacing: .05em;
  text-transform: uppercase;
  cursor: pointer;
  box-shadow: 0 2px 8px rgba(224,92,42,.45);
  transition: background .15s, transform .1s;
  white-space: nowrap;
}
#backToTodayBtn:hover {
  background: #c44d20 !important;
  transform: scale(1.05);
}
#calMonthLabel { font-size: 1.6rem; font-weight: var(--weight-bold); white-space: nowrap; letter-spacing: -0.5px; min-width: 140px; text-align: center; }

/* ── Calendar card header: 3-zone layout ── */
.cal-card-header {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 10px;
  overflow: visible; /* override card-header overflow:hidden so tabs/buttons are never clipped */
}
.cal-card-header .cal-title-zone {
  justify-self: start;
  min-width: 0;
  overflow: hidden;
}
.cal-card-header .cal-month-nav {
  justify-content: center;
}
.cal-card-header .cal-view-tabs {
  justify-self: end;
}
/* One row always. It used to wrap (via .card-header > div) once a beside
   column narrowed the card, dropping the view tabs under 💡. The title gives
   way first instead. */
.cal-card-header .cal-header-actions,
.cal-card-header .cal-month-nav { flex-wrap: nowrap; min-width: max-content; }
.cal-card-header .cal-title-zone { max-width: 100%; }
.cal-card-header .cal-app-title { min-width: 0; }
.cal-card-header .cal-app-title-text { min-width: 0; overflow: hidden; }
/* Set by syncAppTitleFit() when the text doesn't fit: hidden rather than
   truncated. visibility keeps the box so it can be measured to come back. */
.cal-card-header .cal-app-title.title-clipped .cal-app-title-text { visibility: hidden; }
/* Settings as its own top-level header nav item — mobile-only (desktop keeps
   the sidebar control card's ⚙️ button). Hidden by default so it drops out
   of the box tree entirely, leaving the 3-column desktop grid above with
   exactly the 3 visible children it expects (title-zone/month-nav/actions);
   the mobile media query below turns it back on and pins it to row 1. */
.cal-header-settings-btn { display: none; }
.cal-view-select { display: none; }

/* Mobile: 1-row header — 🏠/👁, the period label, a Month/Day/Agenda
   select and ⚙️. (It used to be 2 rows with the view tabs on row 2, which
   cost the Month grid most of a week row's height on a phone.)
   Must be after the base .cal-card-header rule (which sets display:grid) so
   the lower specificity flex override wins via source order. Row 2 is
   centered rather than right-aligned so the tabs don't read as stranded in
   the row's left-hand dead space; padding/gap trimmed to reclaim a bit of
   vertical space for the calendar body below.
   Like every phone override, this block has to come after the base rules
   it overrides (see "Phone overrides (generic)" at the end of the file). */
@media (max-width: 640px), (max-height: 500px) and (orientation: landscape) and (pointer: coarse) {
  .cal-card-header {
    display: flex;
    flex-wrap: nowrap;
    gap: 4px;
    align-items: center;
    padding: 8px 14px 6px;
  }
  .cal-card-header .cal-title-zone { flex: 0 0 auto; }
  .cal-card-header .cal-month-nav  { flex: 1; justify-content: center; min-width: 0; flex-wrap: nowrap; }
  /* !important: the element carries an inline display:flex */
  .cal-card-header .cal-header-actions { display: none !important; }
  #calMonthLabel { overflow: hidden; text-overflow: ellipsis; font-size: var(--text-md); }
  .cal-card-header .cal-title-zone { gap: 6px; }
  /* Refocus as a compact round icon: the word "REFOCUS" alone took a third
     of the one-row header's width on a phone. */
  #backToTodayBtn {
    font-size: 0 !important;
    padding: 0 !important;
    width: 32px !important;
    height: 32px;
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }
  #backToTodayBtn::before { content: '\21BA'; font-size: 1.15rem; line-height: 1; }
  .cal-app-title-text { display: none; }
  .cal-view-select {
    display: block;
    flex: 0 0 auto;
    border: 1px solid var(--line);
    background: var(--surface-solid);
    color: var(--text);
    border-radius: 8px;
    padding: 5px 8px;
    font: inherit;
    font-size: var(--text-sm);
    font-weight: var(--weight-semi);
  }

  /* Month pills wrap to two lines instead of clipping after a few letters.
     Qualified with .cal-day so it beats the unguarded .cal-event rule further down. */
  .cal-day .cal-event {
    white-space: normal;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    line-height: 1.2;
    overflow-wrap: break-word;   /* only a word too long for the line breaks, */
    -webkit-hyphens: auto;       /* and then with a hyphen (needs <html lang>; */
    hyphens: auto;               /* iOS Safari reads only the prefixed one) */
    padding-left: 3px;
    padding-right: 3px;
  }
  /* Today's cell gets an accent ring. An outline, not a box-shadow, because
     the themes already put their own glows in box-shadow on this cell. */
  .cal-day.today { outline: 2px solid var(--accent); outline-offset: -2px; }
}

.cal-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  grid-template-rows: auto;   /* first row (DOW headers) sizes to content */
  grid-auto-rows: 1fr;        /* week rows share remaining height equally */
  gap: 2px;
  flex: 1;              /* grow to fill card body */
  min-height: 0;
  align-content: stretch;
  position: relative;   /* anchor for .cal-multiday-overlay */
}

/* ── Multi-day spanning event overlay ── */
.cal-multiday-overlay {
  position: absolute;
  inset: 0;
  pointer-events: none; /* bars opt back in individually */
  z-index: 2;
}
.cal-multiday-bar {
  position: absolute;
  border-radius: 3px;
  color: #fff;
  font-size: .68rem;
  font-weight: 500;
  line-height: 18px;
  padding: 0 5px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  pointer-events: auto;
  cursor: pointer;
  box-sizing: border-box;
}
.cal-multiday-bar:hover { filter: brightness(1.12); }
.cal-multiday-bar.cal-dnd-dragging { opacity: 0.3; pointer-events: none; }
.cal-multiday-bar.other-month { opacity: .35; }
.cal-md-spacer { flex-shrink: 0; }
.cal-dow {
  text-align: center;
  font-size: var(--text-sm);
  font-weight: var(--weight-bold);
  color: var(--muted);
  padding: 1px 0 2px;
  letter-spacing: .05em;
  text-transform: uppercase;
  display: flex; align-items: center; justify-content: center;
}
.cal-day {
  min-height: 0;  /* removed fixed height — grid rows handle sizing now */
  padding: 4px;
  /* Stronger than --line (a hairline divider elsewhere) so each day reads
     as its own cell rather than blending into the grid's background. */
  border: 1px solid var(--col-border);
  border-radius: var(--col-radius);
  background: var(--col-bg);
  font-size: var(--text-sm);
  cursor: default;
  position: relative;
  overflow: hidden;
}
/* Subtle + hint on hoverable day cells (month view) */
.cal-day.can-add::after {
  content: '+';
  position: absolute;
  top: 3px; right: 5px;
  font-size: .85rem;
  font-weight: 300;
  color: var(--accent);
  opacity: 0;
  transition: opacity .15s;
  pointer-events: none;
  line-height: 1;
}
.cal-day.can-add:hover::after { opacity: 0.55; }
.cal-day.can-add:hover { background: rgba(255,255,255,.10); }
.cal-day.other-month { opacity: .35; }

/* ── Current day-of-week header highlight (month view) ── */
.cal-dow.current-dow { color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, transparent); border-radius: 6px; }

.week-day-col.today,
.day-hour-row.current-hour {
  box-shadow: var(--today-ring);
}
.cal-day.current-week { background: var(--week-bg); }
.cal-day-num {
  font-size: var(--text-sm);
  font-weight: var(--weight-semi);
  margin-bottom: 3px;
  color: var(--muted);
  display: inline-flex;
  align-items: center; justify-content: center;
  width: 22px; height: 22px;
  border-radius: 50%;
}
.cal-day.today .cal-day-num {
  background: var(--accent);
  color: #fff;
  font-weight: var(--weight-bold);
}
/* Today's cell pops above the current-week row with a stronger tint + accent ring */
.cal-day.today {
  background: var(--today-bg);
  box-shadow: var(--today-ring);
}
.cal-event {
  font-size: var(--text-xs);
  padding: 1px 4px;
  border-radius: 3px;
  margin: 0 -4px 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  color: #fff;
}
.cal-event-holiday {
  font-style: italic;
  opacity: .9;
}

/* ── Calendar drag-and-drop rescheduling ── */
.cal-event[draggable="true"] { cursor: grab; }
.cal-event[draggable="true"]:active { cursor: grabbing; }
.cal-event.cal-dnd-dragging { opacity: 0.3; pointer-events: none; }
.cal-day.cal-dnd-over {
  outline: 2px dashed var(--accent);
  outline-offset: -2px;
  background: rgba(100,160,255,.08);
}

/* ── Agenda View ── */
#agendaView {
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  flex: 1;
  min-height: 0;
  padding: 2px 0;
  -webkit-overflow-scrolling: touch;
}
/* Cap the list's width (headers, events, empty state) and center it: full-
   width bars on a wide panel read as stretched. In rem so the cap grows with
   the text (704px on desktop, 880px under Wall display's 20px root). The
   scroll container itself stays full width, so the scrollbar keeps its edge. */
#agendaView > * {
  width: 100%;
  max-width: 44rem;
  align-self: center;
}
.agenda-day-header {
  font-size: var(--text-sm);
  font-weight: var(--weight-bold);
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: .06em;
  padding: 18px 10px 6px;
  border-bottom: 1px solid rgba(255,255,255,.06);
  flex-shrink: 0;
}
.agenda-day-header:first-child { padding-top: 6px; }
.agenda-day-header.agenda-today { color: var(--accent); }
/* Same color treatment as Month's .cal-event and Day's .day-event-pill: a
   solid fill of the event's own calendar color with white text, so one event
   reads the same in all three views. --event-color is set inline per event
   (dynamic, can't be a class); events with no color fall back to --muted.
   Margin-bottom (not gap on the parent) so fills don't visually merge. */
.agenda-event {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 8px 10px 8px 12px;
  --event-color: var(--muted);
  background: var(--event-color);
  color: #fff;
  border-radius: 8px;
  cursor: default;
  flex-shrink: 0;
  margin-bottom: 6px;
  transition: filter .12s;
}
.agenda-event:last-child { margin-bottom: 0; }
/* Bills in Agenda: the --red fill Month's bill pills and Day's "Bills Due
   Today" use, with the amount right-aligned */
.agenda-event.agenda-bill { --event-color: var(--red); align-items: center; }
.agenda-bill-amt { font-weight: var(--weight-semi); font-variant-numeric: tabular-nums; white-space: nowrap; }
.agenda-event.agenda-clickable { cursor: pointer; }
.agenda-event.agenda-clickable:hover  { filter: brightness(1.08); }
.agenda-event.agenda-clickable:active { filter: brightness(0.96); }
.agenda-info { flex: 1; min-width: 0; }
.agenda-time {
  font-size: var(--fs-meta);
  /* Matches .day-event-pill .evt-time: the fill's white text, dimmed */
  color: inherit;
  opacity: .85;
  margin-bottom: 3px;
  font-variant-numeric: tabular-nums;
}
.agenda-title {
  font-size: var(--fs-title);
  font-weight: var(--weight-semi);
  color: inherit;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  line-height: var(--leading-tight);
}
/* Member initials sized to read across the room (base badge is 14px) */
.agenda-title .evt-member-badge {
  width: 32px; height: 32px;
  font-size: 1rem;
  margin-left: 6px;
  box-shadow: inset 0 0 0 2px color-mix(in srgb, #fff 85%, transparent);
}
.agenda-title > span:first-child { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.agenda-empty {
  text-align: center;
  color: var(--muted);
  font-size: var(--text-sm);
  padding: 40px 0;
}

/* ── Weather ── */
#weatherSetup { display: flex; gap: 8px; flex-wrap: wrap; }
#weatherSetup input {
  flex: 1; min-width: 140px;
  background: var(--surface2);
  border: 1px solid rgba(255,255,255,.1);
  border-radius: 8px;
  color: var(--text);
  padding: 8px 12px;
  font-size: var(--text-base);
}
#weatherDisplay { display: none; }

.wx-current {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 8px;
}
.wx-icon-wrap { font-size: 2.8rem; line-height: 1; flex-shrink: 0; display: flex; }
.wx-icon { display: inline-block; }
/* Animated icon states */
.wx-anim-sunny .wx-icon  { animation: wx-spin 8s linear infinite; transform-origin: center; }
.wx-anim-rain .wx-icon   { animation: wx-bob 1.4s ease-in-out infinite; }
.wx-anim-storm .wx-icon  { animation: wx-shake 0.8s ease-in-out infinite; }
.wx-anim-snow .wx-icon   { animation: wx-bob 2.5s ease-in-out infinite; }
.wx-anim-clouds .wx-icon { animation: wx-drift 4s ease-in-out infinite; }
@keyframes wx-spin  { to { transform: rotate(360deg); } }
@keyframes wx-bob   { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-4px); } }
@keyframes wx-shake { 0%,100% { transform: translateX(0); } 25% { transform: translateX(-3px); } 75% { transform: translateX(3px); } }
@keyframes wx-drift { 0%,100% { transform: translateX(0); } 50% { transform: translateX(4px); } }

.wx-temp-row { display: flex; align-items: baseline; gap: 8px; }
.wx-temp { font-size: 2.2rem; font-weight: var(--weight-bold); line-height: 1; }
.wx-feels { font-size: var(--text-sm); color: var(--muted); }
.wx-desc { color: var(--muted); font-size: var(--text-sm); margin-top: 2px; text-transform: capitalize; }
.wx-details {
  display: flex; flex-wrap: wrap; gap: 5px;
  padding: 6px 0 2px;
}
.wx-detail-pill {
  font-size: var(--text-xs);
  color: var(--muted);
  background: var(--surface2);
  border-radius: 20px;
  padding: 2px 8px;
  white-space: nowrap;
}

.wx-today {
  display: flex;
  justify-content: space-evenly;
  flex-wrap: wrap;
  padding: 6px 0;
  border-top: 1px solid rgba(255,255,255,.06);
  border-bottom: 1px solid rgba(255,255,255,.06);
  margin-bottom: 8px;
}
.wx-today span { font-size: var(--text-sm); color: var(--muted); }
.wx-today strong { color: var(--text); }

/* ── Sun-path arc ── */
.wx-sun-arc { margin: 2px 0 10px; }
.wx-sun-svg {
  width: 100%;
  height: auto;
  display: block;
  overflow: visible;
}
.wx-sun-track {
  fill: none;
  stroke: rgba(255,255,255,.14);
  stroke-width: 2;
  stroke-dasharray: 3 4;
  stroke-linecap: round;
}
.wx-sun-progress {
  fill: none;
  stroke: rgba(255,210,74,.55);
  stroke-width: 2.5;
  stroke-linecap: round;
}
.wx-sun-glow {
  opacity: .35;
  filter: blur(3px);
  animation: wxSunPulse 3s ease-in-out infinite;
}
@keyframes wxSunPulse {
  0%, 100% { opacity: .25; }
  50%      { opacity: .5; }
}
.wx-sun-end {
  fill: var(--muted);
  font-size: 11px;
  font-weight: 600;
}
.wx-sun-readout {
  text-align: center;
  font-size: var(--text-sm);
  color: var(--muted);
  margin-top: 2px;
}
body.light-mode    .wx-sun-track    { stroke: rgba(0,0,0,.16); }

.wx-forecast {
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.wx-day {
  display: grid;
  grid-template-columns: 40px 20px 1fr auto auto auto;
  align-items: center;
  gap: 5px;
  font-size: var(--text-sm);
}
.wx-day-name { color: var(--muted); font-weight: var(--weight-semi); }
.wx-day-icon { font-size: 1.1rem; text-align: center; }
.wx-day-bar-wrap {
  height: 4px;
  border-radius: 2px;
  background: rgba(255,255,255,.08);
  overflow: hidden;
}
.wx-day-bar { height: 100%; background: var(--accent2); border-radius: 2px; }
.wx-day-lo { color: var(--muted); min-width: 34px; text-align: right; }
.wx-day-hi { color: var(--text); font-weight: var(--weight-semi); min-width: 34px; text-align: right; }
.wx-day-rain { color: #61afef; font-size: var(--text-sm); min-width: 36px; text-align: right; }

/* Text for screen readers only (an icon-only title still needs a name) */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ── Forecast mode toggle ── */
.wx-forecast-toggle {
  /* Same look as the calendar's .cal-view-tab, one size down */
  display: flex;
  flex-wrap: nowrap;
  gap: 3px;
  margin-bottom: 6px;
  width: fit-content;
  flex-shrink: 1;
}
.wx-forecast-toggle button {
  padding: 2px 6px;
  border: 1px solid var(--control-border); border-radius: var(--tab-radius);
  background: var(--control-bg); color: var(--text);
  box-shadow: var(--control-shadow);
  font-size: var(--text-xs); font-weight: var(--weight-semi);
  cursor: pointer;
  transition: all .15s;
  line-height: 1.6;
  white-space: nowrap;
}
.wx-forecast-toggle button.active {
  background: var(--control-active-bg);
  color: var(--control-active-ink);
  border-color: var(--control-active-border);
  box-shadow: var(--control-active-shadow);
}
/* The title is the cloud-sun icon alone (the word is .sr-only): "Weather"
   cost 94px on the wall panel, enough to push the forecast tabs onto a row
   of their own. If even the icon and tabs don't fit, the tabs still drop to
   their own row rather than squeezing (same pattern as the wall-display
   Assistant header). */
#weatherCard .card-header { flex-wrap: wrap; row-gap: 6px; }
#weatherCard .card-header h2 { min-width: max-content; flex: 0 0 auto; }
#weatherCard .card-header .wx-forecast-toggle { margin-left: auto; margin-bottom: 0; }
html:not(.touch-input) .wx-forecast-toggle button:not(.active):hover {
  border-color: var(--accent);
}

/* ── Condition-based card tint ── */
#weatherCard {
  --wx-tint: transparent;
  background: var(--surface) !important;
  position: relative;
}
#weatherCard::before {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--wx-tint);
  border-radius: inherit;
  pointer-events: none;
  transition: background 1.2s ease;
}
body.has-bg #weatherCard::before { background: transparent; }

/* ── Activity tags ── */
.wx-activity-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  padding: 4px 0 2px;
}
.wx-activity-tag {
  font-size: var(--text-xs);
  padding: 2px 8px;
  border-radius: 20px;
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  color: var(--text);
  white-space: nowrap;
}

/* ── Compass rose arrow in wind pill ── */
.wx-compass {
  display: inline-block;
  font-style: normal;
  transform: rotate(var(--deg));
  transform-origin: center;
  margin-right: 2px;
  font-size: .8em;
  opacity: .8;
}

/* ── Precipitation radar map ── */
.wx-radar-map {
  width: 100%;
  height: 220px;
  border-radius: 8px;
  overflow: hidden;
  flex-shrink: 0;
}
/* Leaflet attribution — keep it visible but compact */
.wx-radar-map .leaflet-control-attribution {
  font-size: 9px;
  background: rgba(0,0,0,.45);
  color: rgba(255,255,255,.6);
  padding: 1px 4px;
}
.wx-radar-map .leaflet-control-attribution a { color: rgba(255,255,255,.6); }
body.light-mode .wx-radar-map .leaflet-control-attribution {
  background: rgba(255,255,255,.7);
  color: rgba(0,0,0,.5);
}
body.light-mode .wx-radar-map .leaflet-control-attribution a { color: rgba(0,0,0,.5); }

/* ── Temperature curve ── */
.wx-curve-wrap {
  width: 100%;
  padding: 2px 0 4px;
}
.wx-curve-svg {
  width: 100%;
  height: auto;
  display: block;
  overflow: visible;
}
.wx-curve-line {
  fill: none;
  stroke: var(--accent);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.wx-curve-dot {
  fill: var(--accent);
  stroke: var(--surface);
  stroke-width: 1.5;
}
.wx-curve-label {
  fill: var(--text);
  font-size: 8px;
  font-weight: 600;
  text-anchor: middle;
  dominant-baseline: auto;
}
.wx-curve-time {
  fill: var(--muted);
  font-size: 7.5px;
  text-anchor: middle;
  dominant-baseline: hanging;
}
.wx-curve-dot-now {
  fill: var(--accent2);
  stroke: var(--surface);
}
.wx-curve-label-now {
  fill: var(--accent2);
  font-weight: 700;
}
body.light-mode .wx-curve-dot   { stroke: #fff; }

/* ── Hourly forecast strip ── */
.wx-hourly {
  display: flex;
  gap: 5px;
  overflow-x: auto;
  padding-bottom: 2px;
  scrollbar-width: none;
}
.wx-hourly::-webkit-scrollbar { display: none; }
.wx-hour {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  min-width: 46px;
  padding: 6px 4px;
  border-radius: calc(var(--tab-radius) + 1px);
  background: var(--well-bg);
  box-shadow: var(--well-shadow);
  flex-shrink: 0;
}
.wx-hour-time { color: var(--muted); font-size: .68rem; }
.wx-hour-icon { font-size: 1.1rem; line-height: 1.2; }
.wx-hour-temp { font-size: var(--text-sm); font-weight: var(--weight-semi); }
.wx-hour-rain { color: #61afef; font-size: .68rem; min-height: .9rem; }
.wx-hour-bar-wrap {
  width: 100%;
  height: 24px;
  display: flex;
  align-items: flex-end;
  justify-content: center;
}
.wx-hour-bar {
  width: 70%;
  min-height: 2px;
  background: linear-gradient(to top, #61afef, rgba(97,175,239,.3));
  border-radius: 2px 2px 0 0;
  transition: height .3s ease;
}

.wx-city-label { font-size: var(--text-sm); color: var(--muted); flex: 1; text-align: center; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#weatherChange { font-size: var(--text-sm); cursor: pointer; text-decoration: underline; background: none; border: none; color: var(--accent); }

/* ── Snarky / WTF Forecast ticker ── */
.wx-snarky {
  overflow: hidden;
  background: rgba(230,159,0,0.10);
  border-top: 1px solid rgba(230,159,0,0.25);
  margin-top: 8px;
  padding: 5px 0;
  white-space: nowrap;
}
body.light-mode .wx-snarky { background: rgba(230,159,0,0.08); }
.wx-snarky-track {
  display: inline-block;
  font-size: var(--text-xs);
  color: var(--text);
  white-space: nowrap;
  animation: wx-scroll linear 24s infinite;
}
.wx-snarky-loading {
  display: inline-block;
  font-size: var(--text-xs);
  color: var(--muted);
  font-style: italic;
  padding: 0 12px;
}

/* ── Weather ticker ── */
.wx-ticker {
  overflow: hidden;
  background: rgba(255,255,255,.04);
  border-top: 1px solid rgba(255,255,255,.06);
  border-radius: 0 0 6px 6px;
  margin-top: 8px;
  padding: 5px 0;
  white-space: nowrap;
}
.wx-ticker-track {
  display: inline-block;
  font-size: var(--text-xs);
  color: var(--muted);
  white-space: nowrap;
}
.wx-ticker-anim {
  animation: wx-scroll linear 18s infinite;
}
@keyframes wx-scroll {
  0%   { transform: translateX(0); }
  100% { transform: translateX(-50%); }
}

/* ── Location bar (city label + optional prev/next) ── */
.wx-loc-bar {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 8px;
  padding-top: 6px;
  border-top: 1px solid rgba(255,255,255,.06);
}
.wx-loc-btn {
  background: var(--surface2);
  border: none;
  color: var(--text);
  width: 28px; height: 28px;
  border-radius: 50%;
  cursor: pointer;
  font-size: 1.1rem;
  line-height: 1;
  display: flex; align-items: center; justify-content: center;
  transition: background .15s;
  flex-shrink: 0;
}
html:not(.touch-input) .wx-loc-btn:hover { background: var(--accent); color: #fff; }
.wx-loc-dots {
  display: flex;
  gap: 6px;
  align-items: center;
}
.wx-loc-dot {
  width: 8px; height: 8px;
  border-radius: 50%;
  border: none;
  background: var(--muted);
  opacity: 0.45;
  cursor: pointer;
  padding: 0;
  transition: opacity .15s, background .15s, transform .15s;
  flex-shrink: 0;
}
html:not(.touch-input) .wx-loc-dot:hover { opacity: 0.75; transform: scale(1.2); }
.wx-loc-dot.active {
  background: var(--accent);
  opacity: 1;
  transform: scale(1.3);
}

/* ── Calendar ── */
#calendarList { list-style: none; }
#calendarList li {
  padding: 10px 0;
  border-bottom: 1px solid rgba(255,255,255,.05);
  display: flex;
  align-items: flex-start;
  gap: 12px;
}
#calendarList li:last-child { border-bottom: none; }
.event-dot {
  width: 8px; height: 8px; border-radius: 50%;
  margin-top: 6px; flex-shrink: 0;
}
.event-title { font-size: var(--text-base); font-weight: var(--weight-medium); }
.event-time { font-size: var(--text-sm); color: var(--muted); margin-top: 2px; }
#calendarStatus { color: var(--muted); font-size: var(--text-base); }
.cal-add-row {
  display: flex; gap: 8px; margin-top: 12px; flex-wrap: wrap;
}
.cal-add-row input {
  flex: 1; min-width: 160px;
  background: var(--surface2);
  border: 1px solid rgba(255,255,255,.1);
  border-radius: 8px;
  color: var(--text);
  padding: 7px 12px;
  font-size: var(--text-base);
}
#calendarTabs {
  display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 12px;
}
/* ── Calendar toggle rows (sidebar calendar card) ── */
.cal-toggle-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 0;
  cursor: pointer;
  border-bottom: 1px solid rgba(255,255,255,.05);
}
.cal-toggle-row:last-child { border-bottom: none; }
.cal-toggle-row:hover { opacity: .85; }

.cal-toggle-swatch {
  width: 12px; height: 12px;
  border-radius: 50%;
  border: 2px solid;
  flex-shrink: 0;
  transition: background .15s;
}
.cal-toggle-name {
  flex: 1;
  font-size: var(--text-sm);
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.cal-toggle-check {
  accent-color: var(--accent);
  width: 15px; height: 15px;
  flex-shrink: 0;
  cursor: pointer;
}

/* ── View Switcher ── */
/* Individual pills carry their own surface + hairline border (chosen over a
   shared dark track) so an unselected segment stays visible against the
   page background in both themes, rather than a transparent tab blending
   into whatever's behind the whole control. */
.cal-view-tabs {
  display: flex;
  gap: 6px;
}
.cal-view-tab {
  padding: 4px 13px;
  border: 1px solid var(--control-border); border-radius: var(--tab-radius);
  background: var(--control-bg); color: var(--text);
  box-shadow: var(--control-shadow);
  cursor: pointer; font-size: var(--text-sm); font-weight: var(--weight-semi);
  transition: all .15s; white-space: nowrap;
}
.cal-view-tab.active {
  background: var(--control-active-bg); color: var(--control-active-ink);
  border-color: var(--control-active-border); box-shadow: var(--control-active-shadow);
}
html:not(.touch-input) .cal-view-tab:hover:not(.active) { border-color: var(--accent); }

/* ── Week View ── */
#weekView {
  flex-direction: column;  /* display:flex set by JS when active */
  flex: 1;
  min-height: 0;
}
.week-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  /* Rows shared by every day column (via subgrid on .week-day-col):
     header · all-day · timed events · to-dos + chores · bills · meals. A
     section one day lacks still takes the row's height there, so each band
     lines up across the week. Only the timed-events row grows. */
  grid-template-rows: auto auto 1fr auto auto auto;
  column-gap: var(--col-gap); row-gap: 0;
  flex: 1;
  min-height: 0;
  margin-top: 4px;
}
.week-day-col {
  display: grid;
  grid-row: 1 / -1;
  grid-template-rows: auto auto 1fr auto auto auto;   /* no-subgrid fallback */
  grid-template-rows: subgrid;
  /* minmax(0,…) so a nowrap line (a long to-do) ellipsizes instead of
     widening the track past the column and pushing the header off-centre */
  grid-template-columns: minmax(0, 1fr);
  min-height: 0;
  /* Stronger than --line (a hairline divider elsewhere) so each day reads
     as its own column rather than blending into the grid's background. */
  border: 1px solid var(--col-border);
  border-radius: var(--col-radius); overflow: hidden;
  background: var(--col-bg);
  backdrop-filter: var(--col-blur);
  -webkit-backdrop-filter: var(--col-blur);
}
.week-day-header {
  grid-row: 1;
  text-align: center;
  padding: 3px 4px 2px;
  border-bottom: 1px solid rgba(128,128,128,.12);
  flex-shrink: 0;
  position: relative;
}
/* Subtle + hint on hoverable week headers */
.week-day-header.can-add::after {
  content: '+';
  position: absolute;
  top: 3px; right: 6px;
  font-size: .85rem;
  font-weight: 300;
  color: var(--accent);
  opacity: 0;
  transition: opacity .15s;
  pointer-events: none;
  line-height: 1;
}
.week-day-header.can-add:hover::after { opacity: 0.55; }
.week-day-header.can-add:hover { background: rgba(255,255,255,.10); border-radius: 6px; }
.week-day-dow {
  font-size: var(--text-sm); font-weight: var(--weight-bold);
  letter-spacing: .05em; text-transform: uppercase;
  color: var(--muted);
}
.week-day-num {
  font-size: var(--text-md); font-weight: var(--weight-bold);
  color: var(--text);
  width: 26px; height: 26px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%;
  margin: 3px auto 0;
}
.week-day-col.today { background: var(--today-bg); }
.week-day-col.today .week-day-dow { color: var(--accent); }
.week-day-col.today .week-day-num { background: var(--accent); color: #fff; }
/* ── School Day Cycle badge (Week/Day view only) ── */
.school-day-badge {
  font-size: var(--text-xs); font-weight: var(--weight-bold);
  color: var(--accent2); text-align: center; margin-top: 2px;
}
.school-day-subjects {
  display: flex; flex-wrap: wrap; justify-content: center; gap: 3px;
  margin-top: 2px;
}
.school-day-subject-chip {
  display: inline-flex; align-items: center;
  font-size: var(--text-xs); color: var(--text);
  background: color-mix(in srgb, var(--accent2) 16%, var(--surface2));
  border-radius: 8px; padding: 1px 5px;
  /* A narrow column wraps the badge under the name instead of clipping it */
  max-width: 100%; box-sizing: border-box;
  flex-wrap: wrap; justify-content: center;
}
.week-tasks-strip {
  grid-row: 4;
  border-top: 1px solid color-mix(in srgb, var(--accent) 20%, transparent);
  padding: 4px 3px 5px;
  display: flex; flex-direction: column; gap: 2px;
  background: color-mix(in srgb, var(--accent) 6%, transparent);
}
.week-todo-item {
  font-size: var(--text-xs); color: var(--accent);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  padding: 1px 2px;
}
.week-day-allday {
  grid-row: 2;
  padding: 4px 3px 0;
  display: flex; flex-direction: column; gap: 2px;
}
.week-day-body {
  grid-row: 3; min-height: 0;
  padding: 4px 3px;
  display: flex; flex-direction: column; gap: 2px;
  overflow-y: auto;
}
/* Sized to match .agenda-event: title at --fs-title, time at --fs-meta,
   same padding and radius, so one event reads the same size in every list */
.week-event {
  font-size: var(--fs-title); font-weight: var(--weight-semi);
  line-height: var(--leading-tight);
  padding: 8px 10px 8px 12px;
  border-radius: 8px; color: #fff;
  overflow: hidden;
}
.week-event-time {
  font-size: var(--fs-meta); font-weight: normal; opacity: .85; display: block;
  margin-bottom: 3px; font-variant-numeric: tabular-nums;
}
.week-empty { font-size: var(--text-xs); color: var(--muted); text-align: center; margin-top: 10px; opacity: .5; }

/* ── Day View ── */
#dayView {
  flex-direction: column;  /* display:flex set by JS when active */
  flex: 1;
  min-height: 0;
  overflow: hidden;
}
.day-view-wrap { margin-top: 4px; flex: 1; display: flex; flex-direction: column; min-height: 0; overflow: hidden; }
.day-allday-row {
  display: flex; gap: 6px; align-items: flex-start;
  padding: 6px 0 10px;
  border-bottom: 1px solid rgba(128,128,128,.12);
  margin-bottom: 2px; min-height: 34px;
}
.day-time-label {
  font-size: var(--fs-meta); color: var(--muted);
  white-space: nowrap; padding-top: 3px;
  width: 56px; flex-shrink: 0;
  text-align: right; padding-right: 10px;
  font-variant-numeric: tabular-nums;
}
.day-allday-pills { flex: 1; display: flex; flex-wrap: wrap; gap: 3px; }
.day-timeline { flex: 1; overflow-y: auto; min-height: 0; }
.day-hour-row {
  display: grid; grid-template-columns: 56px 1fr;
  min-height: 54px;
  border-bottom: 1px solid rgba(128,128,128,.07);
}
.day-hour-row.current-hour {
  background: var(--today-bg);
  border-left: 2px solid var(--accent);
}
.day-hour-row.current-hour .day-hour-label { color: var(--accent); font-weight: var(--weight-bold); }
.day-hour-label {
  font-size: var(--fs-meta); color: var(--muted);
  text-align: right; padding: 4px 10px 0 0;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.day-hour-slot {
  border-left: 1px solid rgba(128,128,128,.1);
  padding: 3px 5px;
  display: flex; flex-direction: column; gap: 2px;
}
/* Sized to match .agenda-event (see .week-event above) */
.day-event-pill {
  font-size: var(--fs-title); line-height: var(--leading-tight);
  padding: 8px 10px 8px 12px;
  border-radius: 8px; color: #fff;
  display: flex; flex-direction: column;
}
.day-event-pill .evt-title { font-weight: var(--weight-semi); }
.day-event-pill .evt-time  { font-size: var(--fs-meta); opacity: .85; font-variant-numeric: tabular-nums; }
/* Time + member badges share one row (built by appendPillMeta()) — without
   this, badges appended straight into the column-flex pill above each land
   on their own row, stacking vertically instead of sitting beside the time. */
.day-event-pill .evt-meta {
  display: flex; align-items: center; gap: 4px; margin-top: 3px;
}
.day-event-pill .evt-meta .evt-member-badge { margin-left: 0; }

/* ── Event depth ── */
/* Every event fill (Month pills and bars, Day and Week pills, Agenda rows) gets a
   faint bezel (light top edge, dark bottom edge) plus a soft drop shadow.
   The bezel carries the depth in dark themes, the shadow in light ones.
   Mobile Month's member stripes ride in --member-stripes (applyMemberStripes())
   so an inline box-shadow doesn't wipe this out, or the other way round. */
.cal-event, .cal-multiday-bar, .day-event-pill, .week-event, .agenda-event {
  box-shadow:
    var(--member-stripes, 0 0 transparent),
    inset 0 1px 0 rgba(255,255,255,.30),
    inset 0 -1px 0 rgba(0,0,0,.20),
    0 1px 2px rgba(0,0,0,.16),
    0 3px 8px rgba(0,0,0,.10);
}

/* ── Meal Planning — Week strip ── */
.week-meals-strip {
  grid-row: 6;
  border-top: 1px solid rgba(128,128,128,.12);
  padding: 4px 4px 5px;
  display: flex; flex-direction: column; gap: 2px;
  flex-shrink: 0;   /* always holds its size at the bottom of the day card */
}
.week-meal-item {
  font-size: var(--text-base); font-weight: var(--weight-semi); color: var(--text);
  display: flex; align-items: center; gap: 2px;
  padding: 4px 6px; border-radius: 4px;
  min-height: 56px;   /* match the "+ plan meal" button size */
  background: color-mix(in srgb, var(--green) 18%, transparent); cursor: pointer;
  overflow: hidden;
}
.week-meal-item:hover { background: color-mix(in srgb, var(--green) 32%, transparent); }
.week-meal-item:hover .meal-remove-btn { opacity: 1; }

/* Remove (×) button — shared between week and day views */
.meal-remove-btn {
  background: none; border: none; cursor: pointer;
  color: var(--muted); font-size: .85rem; line-height: 1;
  padding: 0 2px; flex-shrink: 0;
  opacity: 0; transition: opacity .15s, color .15s;
}
.meal-remove-btn:hover { color: var(--red); }

/* Day view remove button — positioned in top-right corner of slot */
.day-meal-remove-btn {
  float: right; margin: -2px -3px 0 4px;
  font-size: 1rem; opacity: 0;
}
.day-meal-slot:hover .day-meal-remove-btn { opacity: 1; }
.week-add-meal-btn {
  font-size: var(--text-base); color: var(--accent);
  background: none; border: 1px dashed color-mix(in srgb, var(--accent) 30%, transparent);
  border-radius: 4px; cursor: pointer; padding: 3px 4px;
  width: 100%; text-align: center; transition: all .15s;
  min-height: 56px;   /* ~3× the natural button height */
  display: flex; align-items: center; justify-content: center;
}
.week-add-meal-btn:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); border-style: solid; }

/* ── Meal Planning — Day section ── */
.day-meals-section {
  padding: 8px 0 10px;
  border-bottom: 1px solid rgba(128,128,128,.12);
}
.day-meals-header {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 8px; font-size: var(--text-base); font-weight: var(--weight-semi);
}
.day-meals-grid {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px;
}
.day-meal-slot {
  background: var(--surface2); border-radius: 8px; padding: 8px 9px;
  cursor: pointer; border: 1px solid transparent; transition: border-color .15s;
}
.day-meal-slot:hover { border-color: var(--accent); }
.day-meal-slot.has-meal { border-color: color-mix(in srgb, var(--green) 50%, transparent); }
.day-meal-slot-label {
  font-size: var(--text-xs); color: var(--muted); margin-bottom: 4px;
  text-transform: uppercase; letter-spacing: .04em;
}
.day-meal-slot-name { font-size: var(--text-sm); font-weight: var(--weight-semi); line-height: var(--leading-tight); }
.day-meal-slot-link {
  font-size: var(--text-xs); color: var(--accent);
  text-decoration: none; display: inline-block; margin-top: 3px;
}
.day-meal-slot-link:hover { text-decoration: underline; }
.day-meal-empty { font-size: var(--text-sm); color: var(--muted); }

/* ── Day view inline meal chips (in timeline) ── */
.day-meal-chip {
  display: inline-flex; align-items: center; gap: 5px;
  background: var(--surface2); border-radius: 6px;
  padding: 3px 8px 3px 6px; margin-top: 2px;
  cursor: pointer; border: 1px solid transparent;
  font-size: var(--text-sm); transition: border-color .15s;
  max-width: 100%;
}
.day-meal-chip:hover { border-color: var(--accent); }
.day-meal-chip.has-meal { border-color: color-mix(in srgb, var(--green) 40%, transparent); }
.day-meal-chip-icon { font-size: .9rem; flex-shrink: 0; }
.day-meal-chip-text { font-weight: var(--weight-semi); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.day-meal-chip-label { color: var(--muted); }
.day-meal-chip-add { color: var(--accent); font-size: .9rem; margin-left: 2px; }
.day-meal-chip .meal-remove-btn { opacity: 0; margin-left: 2px; }
.day-meal-chip:hover .meal-remove-btn { opacity: 1; }

/* ── Meal Planning — Suggestion Modal ── */
.modal.meal-modal-inner { max-width: 640px; }
.meal-suggestion {
  background: var(--surface2); border-radius: 10px;
  padding: 14px 16px; margin-bottom: 10px;
  border: 2px solid transparent; transition: border-color .15s;
}
.meal-suggestion.accepted { border-color: var(--green); }
.meal-s-name {
  font-size: var(--text-md); font-weight: var(--weight-bold); margin-bottom: 2px;
}
.meal-s-meta {
  display: flex; gap: 10px; flex-wrap: wrap;
  font-size: var(--text-sm); color: var(--muted); margin-bottom: 6px;
}
.meal-s-desc { font-size: var(--text-base); margin-bottom: 6px; line-height: var(--leading-loose); }
.meal-s-healthy { font-size: var(--text-sm); color: var(--green); margin-bottom: 6px; }
.meal-s-ingredients { font-size: var(--text-sm); color: var(--muted); margin-bottom: 8px; }
.meal-s-actions { display: flex; gap: 6px; flex-wrap: wrap; }
.wx-view-option {
  display: flex; align-items: center; gap: 5px;
  font-size: var(--text-sm); cursor: pointer;
  padding: 4px 10px;
  border-radius: 20px;
  border: 1px solid rgba(128,128,128,.25);
  user-select: none;
}

/* ── Meal Planning — Detail Modal ── */
.modal.meal-detail-modal { max-width: 480px; }
.meal-detail-img-wrap {
  width: 100%; height: 200px;
  border-radius: 12px; overflow: hidden;
  background: var(--surface2);
  display: flex; align-items: center; justify-content: center;
  margin-bottom: 14px;
  position: relative;
}
.meal-detail-img-placeholder { font-size: 4rem; opacity: .5; }
#mealDetailImg { width: 100%; height: 100%; object-fit: cover; display: block; }
.meal-stock-label {
  position: absolute;
  bottom: 6px; right: 8px;
  font-size: .65rem;
  font-style: italic;
  color: #fff;
  background: rgba(0,0,0,.45);
  border-radius: 3px;
  padding: 1px 5px;
  pointer-events: none;
}
.meal-detail-meta {
  display: flex; gap: 12px; flex-wrap: wrap;
  font-size: var(--text-base); color: var(--muted); margin-bottom: 8px;
}
.meal-detail-desc { font-size: var(--text-base); line-height: var(--leading-loose); margin-bottom: 8px; }
.meal-detail-healthy { font-size: var(--text-sm); color: var(--green); margin-bottom: 6px; }
.meal-detail-ingredients { font-size: var(--text-sm); color: var(--muted); line-height: 1.5; }

/* Imported-recipe instructions — collapsed <details> so a long method doesn't
   push the Keep/Adjust/Delete buttons off a wall-panel screen. */
.meal-detail-steps { margin-top: 8px; }
.meal-detail-steps summary {
  font-size: var(--text-sm); font-weight: 600; color: var(--text);
  cursor: pointer; padding: 4px 0; list-style: none;
}
.meal-detail-steps summary::-webkit-details-marker { display: none; }
.meal-detail-steps summary::before { content: '▸ '; color: var(--muted); }
.meal-detail-steps details[open] > summary::before { content: '▾ '; }
.meal-detail-steps ol {
  margin: 6px 0 0; padding-left: 20px;
  max-height: 220px; overflow-y: auto;
  font-size: var(--text-sm); color: var(--muted); line-height: 1.55;
}
.meal-detail-steps li { margin-bottom: 6px; }

/* ── Recipe Box — import from URL ── */
.recipe-src-badge {
  display: inline-block; margin-left: 6px;
  font-size: .64rem; font-weight: 600; letter-spacing: .02em;
  color: var(--accent);
  border: 1px solid var(--line);
  border-radius: 4px; padding: 0 4px;
  vertical-align: middle;
}
.recipe-import-card {
  display: flex; gap: 10px; align-items: flex-start;
  background: var(--surface2); border-radius: 8px; padding: 8px 10px;
}
.recipe-import-thumb {
  width: 64px; height: 64px; flex-shrink: 0;
  object-fit: cover; border-radius: 6px; background: var(--surface);
}
.recipe-import-body { flex: 1; min-width: 0; }
.recipe-import-name { font-weight: 600; font-size: var(--text-sm); line-height: 1.25; }
.recipe-import-meta { font-size: .72rem; color: var(--muted); margin-top: 2px; }
.recipe-import-ings {
  font-size: .72rem; color: var(--muted); margin-top: 4px;
  line-height: 1.4;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.recipe-import-actions { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 8px; }
#recipeImportStatus code {
  background: var(--surface2); border-radius: 3px; padding: 0 3px; font-size: .95em;
}

.meal-adjust-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.meal-adjust-chip {
  background: var(--surface2);
  border: 1px solid var(--line);
  color: var(--text);
  font-size: var(--text-xs);
  padding: 5px 10px; border-radius: 14px;
  cursor: pointer; line-height: 1.2;
  transition: background .15s, border-color .15s, transform .1s;
}
html:not(.touch-input) .meal-adjust-chip:hover  { background: var(--accent); border-color: var(--accent); color: #fff; }
.meal-adjust-chip:active { transform: scale(0.95); }
.meal-ing-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 10px; }
.meal-ing-chip {
  display: inline-flex; align-items: center; gap: 4px;
  background: var(--surface2);
  border: 1px solid var(--line);
  color: var(--text);
  font-size: var(--text-xs);
  padding: 4px 6px 4px 10px; border-radius: 14px; line-height: 1.2;
}
.meal-ing-chip button {
  background: none; border: none; color: var(--muted); cursor: pointer;
  font-size: .95rem; line-height: 1; padding: 0 2px;
}
.meal-ing-chip button:hover { color: var(--red); }

/* ── Budget ── */
.budget-pill {
  background: color-mix(in srgb, var(--red) 18%, transparent); color: var(--text);
  border-radius: 6px; padding: 3px 9px; font-size: var(--text-sm);
  font-weight: var(--weight-semi); cursor: pointer; border: 1px solid color-mix(in srgb, var(--red) 35%, transparent);
  transition: background .15s;
}
.budget-pill:hover { background: color-mix(in srgb, var(--red) 32%, transparent); }
.budget-summary-bar {
  display: flex; align-items: center; justify-content: space-between;
  background: color-mix(in srgb, var(--red) 10%, transparent); border-radius: 8px;
  padding: 6px 12px; margin-bottom: 8px; font-size: var(--text-base);
  border: 1px solid color-mix(in srgb, var(--red) 25%, transparent);
}
.budget-summary-label { color: var(--muted); }
.budget-summary-amount { font-weight: var(--weight-bold); color: var(--red); }
.budget-day-section {
  background: color-mix(in srgb, var(--red) 7%, transparent); border-radius: 8px;
  padding: 8px 10px; margin-bottom: 8px;
  border: 1px solid color-mix(in srgb, var(--red) 20%, transparent);
}
.budget-day-item {
  display: flex; justify-content: space-between;
  font-size: var(--text-base); padding: 3px 0;
  border-bottom: 1px solid color-mix(in srgb, var(--red) 10%, transparent);
}
.budget-day-item:last-child { border-bottom: none; }
.budget-day-name { color: var(--text); }
.budget-day-amt  { font-weight: var(--weight-semi); color: var(--red); font-variant-numeric: tabular-nums; }
.budget-day-total {
  display: flex; justify-content: space-between;
  font-size: var(--fs-item); font-weight: var(--weight-bold);
  padding-top: 5px; margin-top: 3px;
  border-top: 1px solid rgba(255,193,7,.2);
}
.budget-day-total .budget-day-total-label { color: var(--muted); }
.budget-day-total .budget-day-total-amt   { color: var(--red); font-variant-numeric: tabular-nums; }

/* ── Day view: section headers + secondary rows (shared by budget/to-do) ── */
.day-section-header {
  font-size: var(--fs-title); font-weight: var(--weight-bold);
  margin-bottom: 6px;
}
.day-todo-section {
  background: color-mix(in srgb, var(--accent) 7%, transparent); border-radius: 8px;
  padding: 8px 10px; margin-bottom: 8px;
  border: 1px solid color-mix(in srgb, var(--accent) 20%, transparent);
}
.day-todo-row {
  display: flex; justify-content: space-between;
  font-size: var(--fs-item); padding: 3px 0;
  border-bottom: 1px solid color-mix(in srgb, var(--accent) 10%, transparent);
}
.day-todo-row:last-child { border-bottom: none; }
.day-todo-text { color: var(--text); }
.day-todo-assignee { color: var(--accent); font-size: var(--fs-meta); }
.day-allday-add {
  width: 30px; height: 30px; flex-shrink: 0; align-self: center;
  border-radius: 50%; border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  color: var(--accent); font-size: var(--text-base); line-height: 1;
  display: flex; align-items: center; justify-content: center;
  cursor: pointer; padding: 0;
}
html:not(.touch-input) .day-allday-add:hover { background: color-mix(in srgb, var(--accent) 20%, transparent); }
.day-allday-empty { font-size: var(--fs-meta); color: var(--muted); padding-top: 3px; }
.school-day-section {
  background: color-mix(in srgb, var(--accent2) 7%, transparent); border-radius: 8px;
  padding: 8px 10px; margin-bottom: 8px;
  border: 1px solid color-mix(in srgb, var(--accent2) 20%, transparent);
}
/* Header + every subject sit on one line (startMarqueeIfOverflowing() in
   family-hub.js decides whether it needs to scroll — see below) instead of
   the old one-row-per-subject list, which was the tallest thing in Day view
   on a day with 2-3 subjects. */
.school-day-line {
  display: flex; align-items: center; gap: 8px;
  overflow: hidden;
}
.school-day-label {
  font-size: var(--fs-title); font-weight: var(--weight-bold);
  flex-shrink: 0;
}
.school-day-track {
  display: inline-flex; align-items: center; gap: 10px;
  min-width: 0; white-space: nowrap;
}
.school-day-item {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: var(--fs-item); flex-shrink: 0;
}
.school-day-sep { color: var(--muted); flex-shrink: 0; }
.school-day-subject-name { color: var(--text); }
/* Only added when startMarqueeIfOverflowing() measures real overflow — most
   days fit and should stay still, not animate for its own sake. */
.school-day-scrolling .school-day-track {
  animation: school-day-marquee 6s ease-in-out infinite;
  animation-delay: 1s;   /* the "after one second" pause before it moves */
}
@keyframes school-day-marquee {
  0%, 12%  { transform: translateX(0); }
  50%, 62% { transform: translateX(var(--marquee-dist, 0)); }
  100%     { transform: translateX(0); }
}
@media (prefers-reduced-motion: reduce) {
  .school-day-line { overflow-x: auto; }
  .school-day-scrolling .school-day-track { animation: none; }
}
.week-budget-strip {
  grid-row: 5;
  border-top: 1px solid color-mix(in srgb, var(--red) 25%, transparent);
  padding: 4px 5px 5px; background: color-mix(in srgb, var(--red) 7%, transparent);
}
.week-budget-item {
  font-size: var(--text-xs); color: var(--red);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  padding: 1px 3px; border-radius: 3px;
}
.budget-modal-inner { max-width: 520px; }
.budget-item-row {
  display: flex; align-items: center; gap: 8px;
  background: var(--surface2); border-radius: 8px;
  padding: 9px 12px; margin-bottom: 6px;
}
.budget-item-dot { width: 10px; height: 10px; border-radius: 50%; background: var(--red); flex-shrink: 0; }
.budget-item-name  { flex: 1; font-size: var(--text-sm); font-weight: var(--weight-semi); }
.budget-item-meta  { font-size: var(--text-xs); color: var(--muted); }
.budget-item-amt   { font-weight: var(--weight-bold); color: var(--red); font-size: var(--text-sm); margin: 0 6px; }
.budget-item-del   {
  background: none; border: none; color: var(--muted); cursor: pointer;
  font-size: var(--text-md); padding: 0 3px; line-height: 1;
}
.budget-item-del:hover { color: var(--red); }
.cal-event-budget {
  font-size: var(--text-xs); border-radius: 3px; padding: 1px 4px;
  margin: 0 -4px 2px;
  background: color-mix(in srgb, var(--red) 20%, transparent); color: var(--red);
  font-weight: var(--weight-semi); white-space: nowrap; overflow: hidden;
  text-overflow: ellipsis; cursor: default;
}
/* Mobile Month cells fit ~5 characters, and a trailing "..." would take up
   most of them. Placed after the base .cal-event/.cal-event-budget rules so
   it wins on source order. */
@media (max-width: 640px), (max-height: 500px) and (orientation: landscape) and (pointer: coarse) {
  .cal-event, .cal-event-budget { text-overflow: clip; }
}

/* Phone turned sideways: ~430px tall is too short for the mini-month strip
   plus a six-row Month grid, so drop the strip (swipe already changes
   months) and let the grid scroll at a readable row height rather than
   crushing every week to a sliver. */
@media (max-height: 500px) and (orientation: landscape) and (pointer: coarse) {
  body #yearStrip { display: none; } /* body: outranks the later base #yearStrip rule */
  .cal-grid { grid-auto-rows: minmax(64px, 1fr); overflow-y: auto; }
}

/* ── SMS contact popover ── */
.sms-btn-wrap { position: relative; }
/* In the shopping input row: as tall as the input and Add button beside it */
.list-input-row .sms-btn-wrap { display: flex; }
.sms-popover {
  display: none;
  position: fixed;
  background: #1e2130;
  border: 1px solid rgba(128,128,128,.25);
  border-radius: 10px;
  box-shadow: 0 8px 32px rgba(0,0,0,.6);
  min-width: 210px;
  z-index: 60;
  overflow: hidden;
}
body.light-mode .sms-popover {
  background: #f5f6fa;
  border-color: rgba(0,0,0,.15);
}
.sms-popover-title {
  font-size: var(--text-xs); font-weight: var(--weight-bold); letter-spacing: .05em;
  text-transform: uppercase; color: var(--muted);
  padding: 9px 14px 6px;
  border-bottom: 1px solid rgba(128,128,128,.1);
}
.sms-contact-row {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 14px; cursor: pointer;
  transition: background .12s;
  border-bottom: 1px solid rgba(128,128,128,.07);
  font-size: var(--text-base);
}
.sms-contact-row:last-child { border-bottom: none; }
.sms-contact-row:hover { background: rgba(255,255,255,.07); }
body.light-mode .sms-contact-row:hover { background: rgba(0,0,0,.05); }
.sms-contact-nick { flex: 1; font-weight: var(--weight-semi); }
.sms-contact-phone { font-size: var(--text-sm); color: var(--muted); }
.sms-popover-empty {
  padding: 14px; font-size: var(--text-base); color: var(--muted); text-align: center;
}

/* ── SMS settings contact list ── */
.sms-contacts-list { margin-bottom: 10px; }
.sms-setting-row {
  display: grid;
  grid-template-columns: 1fr 1fr 28px;
  align-items: center; gap: 8px;
  padding: 7px 0;
  border-bottom: 1px solid rgba(128,128,128,.1);
  font-size: var(--text-base);
}
.sms-setting-row:last-child { border-bottom: none; }
.sms-setting-nick { font-weight: var(--weight-semi); }
.sms-setting-phone { color: var(--muted); font-size: var(--text-sm); }
.sms-add-row {
  display: grid;
  grid-template-columns: 1fr 1fr auto;
  gap: 6px; margin-top: 6px;
}
.sms-add-row input {
  background: var(--surface2);
  border: 1px solid rgba(255,255,255,.1);
  border-radius: 7px; color: var(--text);
  padding: 7px 10px; font-size: var(--text-base);
}
.sms-add-row input:focus { outline: none; border-color: var(--accent); }

/* ── Lists (Todo + Shopping) ── */
.list-input-row {
  display: flex; gap: 4px; margin-bottom: 5px;
  min-width: 0;
}
.list-input-row input {
  flex: 1;
  min-width: 0;
  background: var(--surface2);
  border: 1px solid rgba(255,255,255,.1);
  border-radius: 6px;
  color: var(--text);
  padding: 4px 8px;
  font-size: var(--text-xs);
}
/* Secondary icon button beside Add (To-Do ⋯, Shopping 📱): same look in both
   rows, always left of Add so the Add buttons line up across cards. */
.list-input-icon-btn { background: var(--surface2); color: var(--muted); padding: 0 9px; }
.list-input-row input::placeholder { color: var(--muted); }
.list-input-row input:focus { outline: none; border-color: var(--accent); }
.list-items { list-style: none; }
.list-item {
  display: flex;
  align-items: center;
  gap: 5px;
  padding: 2px 0;
  border-bottom: 1px solid rgba(255,255,255,.04);
  font-size: var(--text-xs);
  line-height: 1.3;
  min-height: 0;
}
.list-item:last-child { border-bottom: none; }
.list-check {
  width: 12px; height: 12px; border-radius: 50%;
  border: 1.5px solid var(--muted);
  cursor: pointer; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  transition: all .15s;
}
.list-check.done {
  background: var(--green);
  border-color: var(--green);
}
.list-check.done::after {
  content: '✓'; color: #fff; font-size: 8px; font-weight: var(--weight-bold);
}
/* Label + badges on one line */
.list-label-wrap {
  flex: 1;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 3px;
  min-width: 0;
}
.list-label { font-size: var(--text-xs); line-height: 1.3; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.list-label.done { text-decoration: line-through; color: var(--muted); }
.list-meta { font-size: var(--text-xs); color: var(--muted); }
.list-badges { display: contents; } /* children participate directly in label-wrap flex */
.list-badge {
  font-size: .62rem; padding: 0 4px; border-radius: 8px;
  font-weight: var(--weight-semi); white-space: nowrap; flex-shrink: 0;
}
.list-badge-due  { background: color-mix(in srgb, var(--accent) 22%, transparent); color: var(--accent); }
.list-badge-due.overdue { background: color-mix(in srgb, var(--red) 22%, transparent); color: var(--red); }
.list-badge-assign { background: rgba(76,175,130,.22); color: var(--green); }
/* Todo add-details expando */
.todo-extra {
  display: none; gap: 6px; flex-wrap: wrap;
  margin-bottom: 6px; padding: 6px 8px;
  background: var(--surface2); border-radius: 6px;
}
.todo-extra.open { display: flex; }
.todo-extra select, .todo-extra input[type=date] {
  background: var(--surface); border: 1px solid rgba(255,255,255,.12);
  border-radius: 5px; color: var(--text); padding: 3px 6px;
  font-size: var(--text-xs); flex: 1; min-width: 100px;
}
.todo-extra label { font-size: var(--text-xs); color: var(--muted); white-space: nowrap; align-self: center; }
/* Calendar todo events */
.cal-event-todo {
  font-size: .65rem; border-radius: 3px; padding: 1px 4px;
  margin: 0 -4px 2px;
  background: color-mix(in srgb, var(--accent) 20%, transparent); color: var(--accent);
  font-weight: 600; white-space: nowrap; overflow: hidden;
  text-overflow: ellipsis; cursor: default;
}

/* ── Event Editor Modal ── */
.cal-event, .week-event, .day-event-pill { cursor: pointer; }
.cal-event:hover, .week-event:hover, .day-event-pill:hover { filter: brightness(1.15); }
.cal-day-empty-click { cursor: pointer; }
.cal-day-empty-click:hover { background: color-mix(in srgb, var(--accent) 7%, transparent); border-radius: 4px; }
#eventEditorModal .modal {
  width: min(480px, 95vw);
  max-height: 90vh;
  overflow-y: auto;
  /* This element is a genuinely scrollable box (real overflow content), and
     WebKit applies elastic rubber-band bounce locally to any scrollable
     element on its own, independent of the document root: html/body's own
     overscroll-behavior (elsewhere in this file) only stops the document
     from panning, it doesn't reach into a descendant's own local bounce.
     Confirmed on-device in both iOS Safari and Chrome (same WebKit engine
     underneath), so this needs its own overscroll-behavior too. */
  overscroll-behavior: contain;
  /* surface/border/shadow come from the --modal-* tokens on .modal; the
     old 3-D tilt and accent glow were dropped 2026-11-07 for one modal
     look per theme */
}
/* Brighter, more colorful interior than the plain .field defaults elsewhere
   (Settings etc. are untouched — this only targets the event editor) */
#eventEditorModal .field label {
  color: var(--text);
  font-weight: var(--weight-semi);
  opacity: .82;
}
#eventEditorModal .field input,
#eventEditorModal .field textarea,
#eventEditorModal .field select {
  border: 1.5px solid color-mix(in srgb, var(--accent) 30%, var(--line));
  background: color-mix(in srgb, var(--accent) 4%, var(--surface2));
}
/* .btn-secondary's --surface2 fill nearly matches the modal surface, so
   Cancel/Delete read as bare text without the same outline as the inputs */
#eventEditorModal .btn-secondary {
  border: 1px solid color-mix(in srgb, var(--accent) 30%, var(--line));
}
#eventEditorModal .field input:focus,
#eventEditorModal .field textarea:focus,
#eventEditorModal .field select:focus {
  background: var(--surface2);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 20%, transparent);
}
.evt-editor-close-btn {
  display: flex; align-items: center; justify-content: center;
  width: 32px; height: 32px;
  border-radius: 50%;
  background: var(--surface2);
  border: 1.5px solid var(--line);
  color: var(--muted);
  font-size: 1.1rem; line-height: 1;
  cursor: pointer;
  transition: background .15s, border-color .15s, transform .1s;
  flex-shrink: 0;
}
.evt-editor-close-btn:hover {
  background: color-mix(in srgb, var(--text) 12%, var(--surface2));
  border-color: var(--muted);
  color: var(--text);
  transform: scale(1.08);
}
.evt-editor-cal-select {
  display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px;
}
.evt-editor-cal-opt {
  padding: 4px 12px; border-radius: 20px;
  border: 1.5px solid color-mix(in srgb, currentColor 32%, transparent);
  font-size: .8rem; cursor: pointer; transition: all .15s;
  background: color-mix(in srgb, currentColor 14%, var(--surface2)); color: var(--text);
}
.evt-editor-cal-opt.selected {
  border-color: currentColor; font-weight: 700;
  background: color-mix(in srgb, currentColor 28%, var(--surface2));
}
.evt-editor-time-row {
  display: flex; flex-wrap: wrap; gap: 6px;
}
/* flex-basis 150px (not 0) so a field that can't fit its neighbor at that
   width wraps to its own line instead of the two native date/time controls
   overlapping — iOS Safari's picker has a fixed minimum render width that
   ignores a narrower CSS box, which is what caused the overlap this guards
   against. Reduced input padding below buys back a bit more room so paired
   fields still fit side by side on typical phone widths. */
.evt-editor-time-row > .field {
  flex: 1 1 150px; min-width: 150px;
}
.evt-editor-time-row input[type=date] {
  padding-left: 6px; padding-right: 6px;
  /* Best-effort: iOS's native date/time picker paints wider than the box
     it's given and isn't clipped by default, so the overflow bleeds into
     the gap and the neighboring field instead of respecting this box's
     edge. Unverified whether WebKit actually honors overflow on a native
     form control's own internal rendering (it may not). If this doesn't
     fully contain it, .evt-editor-time-row needs flex-direction: column
     instead, which guarantees no overlap since there's no neighbor to
     bleed into. */
  overflow: hidden;
}
.evt-editor-footer {
  display: flex; justify-content: space-between; align-items: center;
  margin-top: 20px; gap: 10px;
}
.btn-danger {
  background: color-mix(in srgb, var(--red) 15%, transparent); color: var(--red);
  border: 1px solid color-mix(in srgb, var(--red) 30%, transparent);
  padding: 8px 16px; border-radius: 8px; cursor: pointer; font-size: .9rem;
  transition: background .15s;
}
html:not(.touch-input) .btn-danger:hover { background: color-mix(in srgb, var(--red) 30%, transparent); }
.evt-editor-allday-row {
  display: flex; align-items: center; gap: 8px; margin: 0 0 14px;
}
.evt-editor-members { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.evt-member-chip {
  padding: 4px 12px; border-radius: 20px;
  border: 1.5px solid color-mix(in srgb, currentColor 38%, transparent);
  font-size: .8rem; cursor: pointer; transition: all .15s;
  background: color-mix(in srgb, currentColor 12%, var(--surface2)); color: var(--text);
}
/* Selected: border-color is set inline to the member's color alongside the
   fill (openEventEditor()), so a selected chip keeps a visible edge */
.evt-member-chip.selected { color: #fff; font-weight: 600; }
.evt-member-badge {
  display: inline-flex; align-items: center; justify-content: center;
  width: 14px; height: 14px; border-radius: 50%;
  font-size: .58rem; font-weight: 700; color: #fff;
  margin-left: 3px; flex-shrink: 0; vertical-align: middle;
  /* Light inner ring so a badge stays visible on an event filled with the
     same member color. Inset, so an overflow:hidden parent can't clip it */
  box-shadow: inset 0 0 0 1px color-mix(in srgb, #fff 85%, transparent);
}
.evt-editor-select {
  background: var(--surface2); color: var(--text);
  border: 1px solid rgba(255,255,255,.15); border-radius: 8px;
  padding: 6px 10px; font-size: .9rem; cursor: pointer; width: 100%; margin-top: 4px;
}
.evt-editor-ends-row {
  display: flex; align-items: center; gap: 8px; margin-top: 8px;
  font-size: .9rem; color: var(--muted);
}
.evt-editor-ends-row input[type=number] { width: 90px; }
.evt-editor-ends-row input[type=date]   { width: auto; flex: 1; }
.evt-editor-scope-row { display: flex; gap: 6px; margin-top: 6px; }
.evt-scope-btn {
  padding: 5px 14px; border-radius: 20px;
  border: 1.5px solid color-mix(in srgb, var(--accent) 30%, var(--line));
  font-size: .82rem; cursor: pointer; transition: all .15s;
  background: var(--surface2); color: var(--text);
}
.evt-scope-btn.selected {
  border-color: var(--accent); background: color-mix(in srgb, var(--accent) 20%, transparent);
  color: var(--accent); font-weight: 600;
}
/* One border for every control in the event editor: inputs, selects, chips,
   scope toggles and footer buttons all use 1.5px, in the same accent tint as
   the text fields (colored chips tint from their own color instead). Before
   this, the footer .btn and close button had none or a different weight, and
   the scope toggle's white border vanished in light mode. */
#eventEditorModal .btn,
#eventEditorModal .evt-editor-close-btn {
  border: 1.5px solid color-mix(in srgb, var(--accent) 30%, var(--line));
}
/* iOS renders a native date input at its own intrinsic width and ignores
   width:100%, so Start date ran past the card's right edge. Dropping the
   native appearance lets it take the box it's given. */
#eventEditorModal input[type=date] {
  -webkit-appearance: none;
  appearance: none;
  display: block;
  min-width: 0;
  min-height: 2.6em;
}
#eventEditorModal input[type=date]::-webkit-date-and-time-value { text-align: left; }
.week-day-col { cursor: default; }
.week-day-body-add {
  cursor: pointer;
  padding: 4px 6px;
  border-radius: 6px;
  margin: 4px 6px;
  font-size: .72rem;
  color: var(--muted);
  text-align: center;
  transition: background .15s;
}
.week-day-body-add:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); color: var(--accent); }
.list-delete {
  background: none; border: none; color: var(--muted);
  cursor: pointer; font-size: .65rem; padding: 0 1px;
  opacity: 0; transition: opacity .15s; flex-shrink: 0; line-height: 1;
}
.list-item:hover .list-delete { opacity: 1; }
.list-empty { color: var(--muted); font-size: var(--text-xs); text-align: center; padding: 6px 0; }

/* ── Event Details (read-only events: ICS feeds, holidays, local calendars) ── */
/* Compound selector: a bare .evt-details-modal ties the base .modal rule
   further down and loses on source order. Its surface comes from the base
   rule's --modal-bg, which is opaque (it once inherited the translucent
   --surface and went illegible over a busy photo). */
.modal.evt-details-modal { max-width: 460px; }
.evt-details-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 10px; margin-bottom: 4px;
}
.modal .evt-details-head h2 { margin-bottom: 0; line-height: 1.2; }
.evt-details-when {
  font-size: var(--fs-item); color: var(--text);
  font-variant-numeric: tabular-nums;
  margin-bottom: 10px;
}
/* The read-only badge is the whole point of this view — say it in words, and
   carry the calendar's own colour so it still reads as that calendar. */
.evt-details-source {
  display: inline-block;
  font-size: var(--text-xs); color: var(--muted);
  border-left: 3px solid var(--accent);
  padding: 2px 0 2px 8px;
  margin-bottom: 14px;
}
.evt-details-where {
  font-size: var(--fs-meta); color: var(--text);
  margin-bottom: 10px;
}
.evt-details-desc {
  font-size: var(--fs-meta); color: var(--text);
  line-height: var(--leading-loose);
  white-space: pre-wrap;      /* feed descriptions carry real newlines */
  overflow-wrap: anywhere;    /* and sometimes long unbroken URLs */
  max-height: 40vh; overflow-y: auto;
}
.evt-details-link { text-decoration: none; margin-top: 12px; display: inline-block; }

/* ── NFL Matchups ── */
/* Team-brand surfaces: the fills are each team's own color (set inline as
   --away/--home), like an event pill's ev.color, so the text and logo discs on
   them are literal white rather than theme tokens. */
.nfl-matchup:empty { display: none; }
.nfl-matchup { margin: 4px 0 14px; }
.modal.is-matchup .evt-details-head h2 {
  position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0);
}
.modal.is-matchup .evt-details-head { justify-content: flex-end; margin-bottom: 8px; }
.nfl-banner {
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center;
  padding: 18px 10px 16px; border-radius: 16px; color: #fff;
  background:
    radial-gradient(120% 90% at 50% 0%, rgba(255,255,255,.18), transparent 60%),
    linear-gradient(110deg, var(--away) 0 47%, var(--home) 53% 100%);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.12), 0 8px 24px rgba(0,0,0,.25);
  text-shadow: 0 1px 3px rgba(0,0,0,.55);
}
.nfl-side { display: flex; flex-direction: column; align-items: center; gap: 2px; min-width: 0; text-align: center; }
.nfl-side .nfl-mark {
  width: 92px; height: 92px; margin-bottom: 8px;
  background: #fff; border-radius: 50%;
  box-shadow: 0 4px 14px rgba(0,0,0,.35), 0 0 0 3px rgba(255,255,255,.35);
}
.nfl-city { font-size: var(--text-xs); text-transform: uppercase; letter-spacing: .08em; opacity: .9; }
.nfl-name { font-size: var(--fs-title); font-weight: 800; line-height: 1.1; overflow-wrap: anywhere; }
.nfl-sep {
  font-size: var(--text-sm); font-weight: 800; letter-spacing: .1em;
  width: 44px; height: 44px; border-radius: 50%; display: grid; place-items: center;
  background: rgba(0,0,0,.35); box-shadow: 0 0 0 2px rgba(255,255,255,.5);
}
.nfl-kickoff {
  text-align: center; margin-top: 12px;
  font-size: var(--fs-title); font-weight: 700; color: var(--text);
  font-variant-numeric: tabular-nums;
}
.nfl-tv {
  display: flex; flex-wrap: wrap; justify-content: center; align-items: center;
  gap: 6px; margin-top: 10px; font-size: var(--fs-meta);
}
.nfl-tv-chip {
  padding: 3px 10px; border-radius: 999px;
  font-size: var(--text-xs); font-weight: 700; letter-spacing: .03em; color: var(--text);
  background: color-mix(in srgb, var(--text) 10%, transparent);
  border: 1px solid var(--line);
}
/* Logo or fallback color disc, at any size: the wrapper sets the box */
.nfl-mark { display: inline-grid; place-items: center; flex-shrink: 0; overflow: hidden; }
.nfl-mark img { width: 82%; height: 82%; object-fit: contain; }
.nfl-mark .nfl-colors { width: 100%; height: 100%; }
/* Pill badges: two small logo discs ahead of "Bills @ Vikings" */
.nfl-badges { display: inline-flex; gap: 2px; margin-right: 5px; vertical-align: middle; }
.nfl-badges .nfl-mark {
  width: 1.45em; height: 1.45em; border-radius: 50%;
  background: #fff; box-shadow: 0 0 0 1px rgba(0,0,0,.15);
}

/* Tighter card body for list widgets */
#shopCard .card-body, #todoCard .card-body {
  padding: 6px 12px 8px;
}
/* Allow SMS popover to overflow the card header boundary */
#shopCard .card-header {
  overflow: visible;
  flex-wrap: nowrap;
  gap: 6px;
}
#shopCard .card-header h2 {
  flex-shrink: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
#shopCard .card-header > div {
  flex-wrap: nowrap;
  flex-shrink: 0;
  gap: 3px;
}
#shopCard .card-header .btn-sm {
  padding: 3px 7px;
  white-space: nowrap;
}
/* ToDo list scrolls when long */
#todoCard .list-items {
  max-height: 220px;
  overflow-y: auto;
  padding-right: 2px;
}

/* Settings modal */
.modal-overlay {
  position: fixed; inset: 0;
  background: var(--modal-scrim);
  backdrop-filter: var(--modal-scrim-blur);
  -webkit-backdrop-filter: var(--modal-scrim-blur);
  display: flex; align-items: flex-start; justify-content: center;
  z-index: 100; padding: 20px;
  display: none;
  overflow-y: auto;
}
.modal {
  background: var(--modal-bg);
  border: 1px solid var(--modal-border);
  backdrop-filter: var(--modal-blur);
  -webkit-backdrop-filter: var(--modal-blur);
  border-radius: var(--radius);
  padding: 28px;
  width: 100%; max-width: 520px;
  box-shadow: var(--modal-shadow);
  margin: auto;
}
#settingsModal .modal {
  max-width: min(calc(100vw - 2rem), 860px);
  max-height: min(88vh, 620px);
  display: flex; flex-direction: column;
}
#settingsModal .settings-panels {
  flex: 1;
  min-height: 0;
}
.modal h2 { font-size: var(--text-lg); font-weight: var(--weight-semi); margin-bottom: 14px; }
#settingsModal .modal h2 { margin-bottom: 12px; }
.field { margin-bottom: 14px; }
.field label { display: block; font-size: var(--text-sm); color: var(--muted); margin-bottom: 5px; }
.field input,
.field textarea,
.field select {
  width: 100%;
  background: var(--surface2);
  border: 1px solid rgba(255,255,255,.1);
  border-radius: 8px;
  color: var(--text);
  padding: 9px 12px;
  font-size: var(--text-base);
  font-family: inherit;
}
.field input:focus,
.field textarea:focus,
.field select:focus { outline: none; border-color: var(--accent); }
/* The text-input sizing above must not stretch radios/checkboxes: a 100%-wide
   radio pushes its own label to the far side of the row */
.field input[type=checkbox],
.field input[type=radio] { width: auto; padding: 0; }
.modal-actions { display: flex; gap: 10px; justify-content: flex-end; margin-top: 20px; flex-shrink: 0; }
.btn-secondary { background: var(--surface2); color: var(--text); border-color: transparent; box-shadow: none; }
.btn-mode-active {
  background: var(--control-active-bg) !important; color: var(--control-active-ink) !important;
  border-color: var(--control-active-border) !important; box-shadow: var(--control-active-shadow) !important;
}

/* ── Settings — grouped two-pane nav ── */
.settings-body {
  display: flex;
  gap: 18px;
  flex: 1;
  min-height: 0;
  margin-bottom: 4px;
}
.settings-nav {
  flex: 0 0 190px;
  overflow-y: auto;
  min-height: 0;
  border-right: 1px solid var(--line);
  padding-right: 8px;
}
.settings-nav-group { margin-bottom: 14px; }
.settings-nav-group:last-child { margin-bottom: 0; }
.settings-nav-label {
  font-size: .66rem;
  text-transform: uppercase;
  letter-spacing: .07em;
  color: var(--muted);
  font-weight: var(--weight-semi);
  padding: 2px 10px 5px;
  opacity: .85;
}
.settings-nav-item {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 7px 10px;
  border: none;
  border-radius: var(--tab-radius);
  background: none;
  color: var(--text);
  text-align: left;
  cursor: pointer;
  font-family: inherit;
  font-size: var(--text-sm);
  font-weight: var(--weight-semi);
  transition: background .15s;
}
.settings-nav-item:hover:not(.active) { background: var(--surface2); }
.settings-nav-item.active {
  background: var(--control-active-bg); color: var(--control-active-ink);
  box-shadow: var(--control-active-shadow);
}
/* Advanced (Easter Eggs) stays hidden until unlocked via the gear */
.settings-nav-group--gated { display: none; }
body.eggs-unlocked .settings-nav-group--gated { display: block; }

.settings-content {
  flex: 1;
  min-width: 0;  /* flex items default to min-width:auto — without this a
                    wide descendant (a long calendar name, an input row)
                    keeps this pane from shrinking to the modal's width */
  min-height: 0;
  display: flex;
  flex-direction: column;
}
.settings-back { display: none; }  /* wide screens: both panes visible, no back */

/* Helper copy under a field, and the callout that opens some panels */
.settings-hint {
  font-size: var(--text-xs);
  color: var(--muted);
  line-height: 1.45;
  margin: 6px 0 0;
}
.settings-intro {
  display: flex; gap: 10px; align-items: flex-start;
  background: var(--surface2);
  border-radius: var(--radius);
  padding: 10px 12px;
  margin: 0 0 14px;
  font-size: var(--text-sm);
  color: var(--muted);
  line-height: 1.45;
}
.settings-intro p { margin: 0; }
.settings-intro p:only-child { flex: 1; }
/* Radio choices as pills, same shape as the Appearance buttons */
.choice-group { display: flex; flex-wrap: wrap; gap: 8px; }
.choice-group label {
  display: inline-flex; align-items: center; gap: 6px;
  margin: 0;
  padding: 7px 12px;
  border-radius: 8px;
  border: 1px solid transparent;
  background: var(--surface2);
  color: var(--text);
  font-size: var(--text-sm);
  cursor: pointer;
}
.choice-group label:has(input:checked) {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 14%, transparent);
}
.choice-group input { margin: 0; accent-color: var(--accent); }

/* Labeled subsection header inside a panel — breaks long panels into groups */
.settings-section-head {
  font-size: .8rem;
  font-weight: var(--weight-semi);
  color: var(--text);
  letter-spacing: .01em;
  margin: 20px 0 12px;
  padding-top: 16px;
  border-top: 1px solid var(--line);
}
.settings-panel > .settings-section-head:first-child {
  margin-top: 2px;
  padding-top: 0;
  border-top: none;
}
/* Groups several .lc-section blocks (Calendar panel), so it reads a level
   above their titles: same small caps as the settings nav labels */
.settings-group-head {
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--muted);
  margin-bottom: 0;
}

/* Narrow: drill-in — show the nav list, tap an item to slide in its panel */
@media (max-width: 700px) {
  .settings-nav {
    flex: 1 1 auto;
    border-right: none;
    padding-right: 0;
  }
  .settings-body.show-panel .settings-nav { display: none; }
  .settings-body:not(.show-panel) .settings-content { display: none; }
  .settings-back {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    background: none;
    border: none;
    color: var(--accent);
    font-family: inherit;
    font-size: var(--text-sm);
    font-weight: var(--weight-semi);
    cursor: pointer;
    padding: 4px 2px;
    margin-bottom: 8px;
    flex-shrink: 0;
  }
}
.settings-panel { display: none; }
.settings-panel.active { display: block; }
.settings-panels {
  flex: 1;
  overflow-y: auto;
  padding-right: 4px;
  margin-bottom: 4px;
  min-height: 0;
}
/* ── Local Calendars settings panel ── */
.lc-section {
  padding: 12px 0;
  border-bottom: 1px solid var(--line);
}
.lc-section-title {
  font-size: var(--text-sm);
  font-weight: var(--weight-bold);
  color: var(--text);
  margin-bottom: 8px;
  letter-spacing: .02em;
}
.lc-list { display: flex; flex-direction: column; gap: 4px; margin-bottom: 8px; }
.lc-empty { font-size: var(--text-xs); color: var(--muted); padding: 4px 0; }
.lc-item {
  display: flex; align-items: center; gap: 8px;
  padding: 5px 8px; border-radius: 6px;
  background: var(--surface2); font-size: var(--text-xs);
}
.lc-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
.lc-item-label {
  flex: 1;
  min-width: 0; /* let a long calendar name shrink + ellipsize instead of
                   forcing the whole row (and .lc-del beside it) wider */
  font-weight: var(--weight-semi);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.lc-item-meta  { color: var(--muted); white-space: nowrap; flex-shrink: 0; }
.lc-del {
  background: none; border: none; color: var(--muted);
  cursor: pointer; font-size: .85rem; padding: 0 2px; line-height: 1;
  flex-shrink: 0;
}
.lc-del:hover { color: var(--red); }
.lc-add-row {
  display: flex; gap: 5px; align-items: center; flex-wrap: wrap;
}
.lc-input {
  flex: 1; min-width: 80px;
  background: var(--surface2);
  border: 1px solid rgba(255,255,255,.1);
  border-radius: 6px; color: var(--text);
  padding: 5px 8px; font-size: var(--text-xs);
}
.lc-input-sm { flex: 0 0 auto; width: 90px; }
.lc-input-date { flex: 0 0 auto; width: 140px; }
.lc-input:focus { outline: none; border-color: var(--accent); }
.lc-select {
  background: var(--surface2);
  border: 1px solid rgba(255,255,255,.1);
  border-radius: 6px; color: var(--text);
  padding: 5px 6px; font-size: var(--text-xs); cursor: pointer;
}
.lc-color {
  width: 32px; height: 28px; border: none; border-radius: 6px;
  padding: 0; cursor: pointer; background: none;
}
.lc-day-checks {
  display: flex; gap: 6px; align-items: center;
}
.lc-day-checks label {
  display: flex; align-items: center; gap: 3px;
  font-size: var(--text-xs); color: var(--muted); cursor: pointer;
  accent-color: var(--accent);
}
body.light-mode .lc-input,
body.light-mode .lc-select  { border-color: rgba(0,0,0,.15); }

/* ── Recurring (Calendar settings) ── */
.lc-recur-hint { margin: -4px 0 8px; }
/* Name over schedule: a schedule summary beside the name crowded it out on phones */
.lc-recur-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.lc-recur-body .lc-item-meta { white-space: normal; }

/* Add/edit is a drill-in screen: #settingsModal.recur-editing hides the rest
   of the Calendar panel and swaps the footer to the item's own Cancel/Save */
.lc-recur-form { display: none; }
#settingsModal.recur-editing .lc-recur-form { display: flex; }
#settingsModal:not(.recur-editing) .settings-recur-action { display: none; }
#settingsModal.recur-editing .settings-main-action,
#settingsModal.recur-editing .settings-back,
#settingsModal.recur-editing .lc-recur-listonly,
#settingsModal.recur-editing .settings-panel[data-panel="calendar"] > :not(.lc-recur-section) { display: none; }

.lc-recur-form { flex-direction: column; gap: 14px; }
.lc-recur-form .lc-section-title { margin-bottom: 0; }
.lc-recur-back {
  align-self: flex-start;
  background: none; border: none; padding: 4px 2px;
  color: var(--accent); font-family: inherit;
  font-size: var(--text-sm); font-weight: var(--weight-semi); cursor: pointer;
}
.lc-recur-what { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.lc-recur-what .lc-input { flex: 1 1 160px; min-width: 0; }
.lc-recur-emoji {
  flex: 0 0 auto; width: 40px; height: 40px;
  border: 1px solid var(--line); border-radius: 10px;
  background: var(--surface2); font-size: 1.3rem; line-height: 1; cursor: pointer;
}
.lc-recur-color { flex: 0 0 auto; width: 28px; height: 28px; border-radius: 50%; overflow: hidden; }
.lc-recur-color::-webkit-color-swatch-wrapper { padding: 0; }
.lc-recur-color::-webkit-color-swatch { border: none; border-radius: 50%; }
.lc-recur-color::-moz-color-swatch { border: none; border-radius: 50%; }

.lc-recur-grid {
  display: grid; grid-template-columns: auto 1fr;
  gap: 12px 14px; align-items: center;
}
.lc-recur-label { font-size: var(--text-xs); font-weight: var(--weight-semi); color: var(--muted); }
.lc-recur-field { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; min-width: 0; }
.lc-recur-text { font-size: var(--text-xs); color: var(--muted); }
.lc-input-num  { flex: 0 0 auto; width: 56px; min-width: 0; }
#lcRecurTime   { width: auto; }
.lc-recur-allday { display: flex; align-items: center; gap: 6px; font-size: var(--text-xs); cursor: pointer; accent-color: var(--accent); }

/* Weekday chips: the checkbox stays in the label for state and keyboard
   focus, visually replaced by the letter */
.lc-recur-days { display: flex; gap: 4px; min-width: 0; }
/* Chips share the row and shrink a little rather than wrap one onto its own line */
.lc-recur-days label { position: relative; cursor: pointer; flex: 1 1 0; max-width: 34px; min-width: 0; }
.lc-recur-days input { position: absolute; opacity: 0; width: 0; height: 0; }
.lc-recur-days span {
  display: grid; place-items: center; aspect-ratio: 1;
  border-radius: 50%; border: 1px solid var(--line);
  font-size: var(--text-xs); font-weight: var(--weight-semi); color: var(--muted);
}
.lc-recur-days input:checked + span { background: var(--accent); border-color: var(--accent); color: #fff; }
.lc-recur-days input:focus-visible + span { outline: 2px solid var(--accent); outline-offset: 2px; }

.panel-empty {
  text-align: center; padding: 32px 0;
  color: var(--muted); font-size: var(--text-base);
}
.panel-empty .panel-empty-icon { font-size: 2.2rem; margin-bottom: 10px; }
.panel-empty p { margin-top: 6px; font-size: var(--text-base); }

#settingsBtn, #saverNowBtn {
  background: none; border: none; color: var(--muted); cursor: pointer;
  font-size: 1rem; padding: 2px 4px; line-height: 1;
}
#settingsBtn:hover, #saverNowBtn:hover { color: var(--text); }

/* Spinner */
.spinner {
  display: inline-block; width: 16px; height: 16px;
  border: 2px solid rgba(255,255,255,.2);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin .7s linear infinite;
  vertical-align: middle;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* Color dots for calendars */
.colors { --c0:#6c8ef5;--c1:#f5a623;--c2:#4caf82;--c3:#e06c75;--c4:#c678dd;--c5:#61afef; }

/* ── Calendar column (main cal + mini strip stacked) ── */
.cal-column {
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-width: 0;
}

/* ── 6-month strip ── */
#yearStrip {
  background: var(--card-bg);
  border-radius: var(--radius);
  box-shadow: var(--card-shadow);
  border: 1px solid var(--card-border);
  backdrop-filter: var(--card-blur);
  -webkit-backdrop-filter: var(--card-blur);
  overflow: hidden;
  display: flex;
  align-items: stretch;
}
.strip-nav-btn {
  background: var(--control-bg);
  border: 1px solid var(--control-border);
  color: var(--text);
  width: 24px; height: 24px;
  border-radius: 6px;
  cursor: pointer;
  font-size: .95rem;
  display: flex; align-items: center; justify-content: center;
  flex-shrink: 0;
}
html:not(.touch-input) .strip-nav-btn:hover { background: var(--accent); }
.strip-side-nav {
  background: var(--well-bg);
  border: none;
  color: var(--text);
  width: 28px;
  cursor: pointer;
  font-size: 1.4rem;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  transition: background .15s;
}
html:not(.touch-input) .strip-side-nav:hover { background: var(--accent); }

.mini-grid {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  padding: 8px 10px;
  gap: 8px;
  flex: 1;
}

.mini-card {
  background: var(--well-bg);
  box-shadow: var(--well-shadow);
  border-radius: calc(var(--tab-radius) - 1px);
  padding: 5px 4px 4px;
  cursor: pointer;
  transition: background .15s, transform .15s;
  border: 1px solid transparent;
}
.mini-card:hover {
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  transform: translateY(-1px);
}
.mini-card.current-month,
body.has-bg .mini-card.current-month {
  border-color: transparent;
  background: var(--today-bg);
  box-shadow: var(--today-ring);
}
body.has-bg .mini-card {
  background: var(--mini-card-bg-photo);
}
body.has-bg .mini-card:hover {
  background: color-mix(in srgb, var(--accent) 20%, transparent);
}

.mini-month-label {
  font-size: .72rem;
  font-weight: 700;
  color: var(--text);
  text-align: center;
  margin-bottom: 5px;
  letter-spacing: .03em;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
}
/* The current month already reads from its card (accent fill and ring,
   today's circle, the week band), so the label stays plain and "Now" is a
   soft chip rather than an outlined box. */
.year-card-now-badge {
  font-size: .62rem;
  font-weight: var(--weight-bold);
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  border-radius: var(--chip-radius);
  padding: 1px 6px;
  line-height: 1.5;
  flex-shrink: 0;
}
.mini-cal-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  /* no column gap, so the current-week band reads as one unbroken strip */
  gap: 1px 0;
}
/* Current week: a tinted band across its seven cells (set by renderYearStrip) */
.mini-week-today {
  background: color-mix(in srgb, var(--accent) 18%, transparent);
  font-weight: var(--weight-semi);
}
.mini-week-today.wk-start { border-radius: 6px 0 0 6px; }
.mini-week-today.wk-end   { border-radius: 0 6px 6px 0; }
.mini-dow {
  font-size: .52rem;
  text-align: center;
  color: var(--muted);
  padding-bottom: 2px;
  font-weight: 600;
}
.mini-day {
  font-size: .58rem;
  text-align: center;
  padding: 1px 0;
  border-radius: 3px;
  line-height: 1.6;
  color: var(--muted);
  position: relative;
}
.mini-day.in-month { color: var(--text); }
/* Today: a full accent circle with a soft halo. The day sits in a centred
   flex column, so without a fixed size the fill shrank to the digits and
   read as a thin sliver (one-digit days especially). */
.mini-day.today,
.year-card-day.today {
  background: var(--accent);
  color: #fff;
  font-weight: var(--weight-bold);
  width: 1.8em;
  height: 1.8em;
  line-height: 1.8em;
  padding: 0;
  border-radius: 50%;
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 35%, transparent);
}
/* dot row under day number for events */
.mini-dots {
  display: flex;
  justify-content: center;
  gap: 1px;
  margin-top: 1px;
}
.mini-dot {
  width: 3px; height: 3px;
  border-radius: 50%;
  flex-shrink: 0;
}

/* ── Year Card (sidebar 2-month view) ── */
.year-card-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
  padding: 2px 0 4px;
}

.year-card-mini {
  cursor: pointer;
  padding: 10px 8px 8px;
}
.year-card-month-label {
  /* keep "November 2026" on one line; the Now chip wraps under it if the
     card is too narrow, instead of the month name breaking in two */
  white-space: nowrap;
  flex-wrap: wrap;
  row-gap: 3px;
  font-size: var(--text-xs);
  font-weight: var(--weight-bold);
  text-align: center;
  margin-bottom: 6px;
  color: var(--text);
  letter-spacing: .02em;
}

.year-card-cal-grid {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.year-card-dow-row,
.year-card-week-row {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 1px;
}

.year-card-week-row.today-week {
  background: color-mix(in srgb, var(--accent) 18%, transparent);
  border-radius: 6px;
  font-weight: var(--weight-semi);
}

body.colorblind-mode .year-card-week-row.today-week {
  background: rgba(86,180,233,0.2);
}

.year-card-day {
  font-size: .68rem;
  text-align: center;
  padding: 2px 0;
  border-radius: 3px;
  line-height: 1.5;
  color: var(--muted);
}

.year-card-day.in-month { color: var(--text); }

/* ── Settings calendar list ── */
#settingCalList { margin-bottom: 8px; }
/* cal-row is used for both Google cals and ICS subs */
.cal-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 0;
  border-bottom: 1px solid rgba(255,255,255,.05);
}
.cal-row:last-child { border-bottom: none; }
/* Color swatch wraps a hidden <input type=color> */
.cal-color-wrap {
  position: relative;
  width: 22px; height: 22px;
  border-radius: 50%;
  cursor: pointer;
  border: 2px solid rgba(255,255,255,.2);
  overflow: hidden;
  flex-shrink: 0;
}
.cal-color-wrap input[type=color] {
  position: absolute; inset: -4px;
  width: calc(100% + 8px); height: calc(100% + 8px);
  border: none; padding: 0; cursor: pointer;
  opacity: 0;
}
.cal-color-swatch {
  position: absolute; inset: 0;
  pointer-events: none;
}
.cal-label-input {
  background: var(--surface2);
  border: 1px solid rgba(255,255,255,.1);
  border-radius: 6px;
  color: var(--text);
  padding: 5px 8px;
  font-size: .82rem;
  flex: 1;
  min-width: 0;
}
.cal-label-input:focus { outline: none; border-color: var(--accent); }
/* Show/hide checkbox in cal rows */
.cal-vis-toggle {
  flex-shrink: 0;
  accent-color: var(--accent);
  width: 15px; height: 15px;
  cursor: pointer;
}
/* Weekdays-only label for ICS rows */
/* ICS sub = cal-row + a weekdays line indented under the label input
   (15px toggle + 22px swatch + two 8px gaps) */
.ics-sub-item { border-bottom: 1px solid rgba(255,255,255,.05); padding-bottom: 7px; }
.ics-sub-item:last-child { border-bottom: none; }
.ics-sub-item .cal-row { border-bottom: none; padding-bottom: 4px; }
.ics-weekdays-label {
  display: flex;
  align-items: center;
  gap: 6px;
  padding-left: 53px;
  font-size: var(--text-xs);
  color: var(--muted);
  cursor: pointer;
}
.ics-add-form { display: flex; flex-direction: column; gap: 6px; margin-top: 6px; }
.ics-add-form .lc-add-row { flex-wrap: nowrap; }
.ics-add-form .lc-color { flex-shrink: 0; }
.cal-remove-btn {
  background: none; border: none; color: var(--muted);
  cursor: pointer; font-size: .9rem; padding: 2px;
  flex-shrink: 0;
  opacity: 0; transition: opacity .15s;
}
.cal-row:hover .cal-remove-btn { opacity: 1; }

/* ── Toggle switch ── */
.toggle-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 0;
  font-size: .85rem;
}
.toggle-switch {
  position: relative;
  width: 36px; height: 20px;
  flex-shrink: 0;
}
.toggle-switch input { opacity: 0; width: 0; height: 0; }
.toggle-track {
  position: absolute; inset: 0;
  /* Off state reads from --muted so it shows on light themes too
     (a white wash disappeared there, leaving only the white knob) */
  background: color-mix(in srgb, var(--muted) 45%, transparent);
  border-radius: 20px;
  cursor: pointer;
  transition: background .2s;
}
.toggle-track::after {
  content: '';
  position: absolute;
  left: 3px; top: 3px;
  width: 14px; height: 14px;
  border-radius: 50%;
  background: #fff;
  transition: transform .2s;
}
.toggle-switch input:checked + .toggle-track { background: var(--accent); }
.toggle-switch input:checked + .toggle-track::after { transform: translateX(16px); }
.toggle-label { color: var(--text); }
.toggle-sub   { font-size: .75rem; color: var(--muted); }

/* ── Background image picker ── */
.bg-drop-zone {
  border: 2px dashed rgba(255,255,255,.15);
  border-radius: 10px;
  padding: 20px;
  text-align: center;
  cursor: pointer;
  transition: border-color .15s, background .15s;
  font-size: var(--text-base);
  color: var(--muted);
  margin-top: 8px;
}
.bg-drop-zone:hover,
.bg-drop-zone.drag-over {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 7%, transparent);
  color: var(--text);
}
.bg-drop-zone input[type=file] { display: none; }
.bg-preview-strip {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  margin-top: 10px;
}
.bg-thumb {
  width: 54px; height: 54px;
  border-radius: 6px;
  object-fit: cover;
  border: 2px solid rgba(255,255,255,.1);
}
.bg-thumb-more {
  width: 54px; height: 54px;
  border-radius: 6px;
  background: var(--surface2);
  display: flex; align-items: center; justify-content: center;
  font-size: var(--text-sm); color: var(--muted);
  border: 2px solid rgba(255,255,255,.08);
}
#bgStatus { font-size: 0.82rem; color: var(--muted); margin-top: 6px; }
.field-row { display: flex; gap: 8px; align-items: center; }
.field-row label { font-size: 0.82rem; color: var(--muted); white-space: nowrap; }
.field-row input[type=number] {
  width: 64px;
  background: var(--surface2);
  border: 1px solid rgba(255,255,255,.1);
  border-radius: 8px;
  color: var(--text);
  padding: 7px 10px;
  font-size: 0.9rem;
}

/* ── Inspirational Quote ── */
.quote-text {
  font-size: .9rem;
  line-height: 1.65;
  color: var(--text);
  font-style: italic;
  position: relative;
  padding-left: 14px;
}
.quote-text::before {
  content: '\201C';
  position: absolute;
  left: 0;
  top: -2px;
  font-size: 1.6rem;
  line-height: 1;
  color: var(--accent);
  font-style: normal;
}
.quote-author {
  margin-top: 8px;
  font-size: .78rem;
  color: var(--muted);
  text-align: right;
  font-style: normal;
}

/* ── News Feed ── */
.news-item {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 6px 0;
  border-bottom: 1px solid rgba(128,128,128,.1);
}
.news-item:last-child { border-bottom: none; }
.news-source {
  font-size: .68rem;
  font-weight: var(--weight-semi);
  color: var(--accent);
  text-transform: uppercase;
  letter-spacing: .04em;
}
.news-headline {
  font-size: .82rem;
  color: var(--text);
  text-decoration: none;
  line-height: 1.35;
}
.news-headline:hover { text-decoration: underline; color: var(--accent); }

/* ── Stock Ticker ── */
.stock-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 0;
  border-bottom: 1px solid rgba(128,128,128,.1);
  font-size: .85rem;
}
.stock-row:last-child { border-bottom: none; }
.stock-symbol { font-weight: var(--weight-bold); min-width: 48px; }
.stock-name   { flex: 1; color: var(--muted); font-size: .75rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.stock-price  { font-weight: var(--weight-semi); min-width: 54px; text-align: right; }
.stock-change { min-width: 54px; text-align: right; font-size: .78rem; font-weight: var(--weight-semi); }

/* ── Countdowns ── */
.countdown-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 7px 0;
  border-bottom: 1px solid rgba(128,128,128,.1);
  gap: 10px;
}
.countdown-item:last-child { border-bottom: none; }
.countdown-label {
  font-size: var(--text-sm);
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.countdown-days {
  font-size: var(--text-sm);
  color: var(--accent);
  flex-shrink: 0;
  text-align: right;
}
.countdown-days strong { font-size: 1.15em; }

/* ── Easter eggs ── */
.modal-tab[data-tab="eggs"] { display: none; }
body.eggs-unlocked .modal-tab[data-tab="eggs"] { display: inline-flex; }

#shopCard { position: relative; }

/* Emoji fireworks sparks */
@keyframes sparkFly {
  0%   { transform: translate(0,0) scale(1); opacity: 1; }
  100% { transform: translate(var(--dx), var(--dy)) scale(0); opacity: 0; }
}
.emoji-spark {
  position: fixed;
  width: 8px; height: 8px;
  border-radius: 50%;
  pointer-events: none;
  z-index: 99999;
  animation: sparkFly 0.6s ease-out forwards;
}

/* "Destroy the ring" toast */
@keyframes ringToastIn  { from { opacity:0; transform:translateX(-50%) translateY(12px); } to { opacity:1; transform:translateX(-50%) translateY(0); } }
@keyframes ringToastOut { from { opacity:1; } to { opacity:0; } }
.ring-toast {
  position: fixed;
  bottom: 32px; left: 50%;
  transform: translateX(-50%);
  background: #1a0a00;
  color: #ff6a00;
  border: 1px solid #ff6a00;
  border-radius: 8px;
  padding: 10px 20px;
  font-size: .95rem;
  font-weight: 600;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.4s ease;
  z-index: 99998;
  box-shadow: 0 0 20px rgba(255,100,0,0.4);
}
.ring-toast-show { opacity: 1; }

/* Generic egg toast */
.egg-toast {
  position: fixed;
  bottom: 32px; left: 50%;
  transform: translateX(-50%) translateY(12px);
  background: var(--surface);
  border: 1px solid var(--accent);
  border-radius: 8px;
  padding: 9px 20px;
  font-size: .9rem;
  font-weight: 600;
  color: var(--text);
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.35s ease, transform 0.35s ease;
  z-index: 99997;
  box-shadow: 0 4px 16px rgba(0,0,0,.25);
}
.egg-toast-show {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}

/* May the 4th badge */
.may4-badge {
  display: inline-block;
  margin-left: 10px;
  font-size: .72rem;
  font-weight: 700;
  color: var(--accent);
  letter-spacing: .03em;
  animation: may4Pulse 2s ease-in-out infinite;
}
@keyframes may4Pulse {
  0%,100% { opacity: 1; }
  50%      { opacity: 0.5; }
}

/* Perfect weather 72° */
@keyframes perfectPulse {
  0%,100% { text-shadow: none; }
  50%      { text-shadow: 0 0 12px rgba(255,210,80,.9); }
}
.wx-perfect::after {
  content: ' 😎';
  animation: perfectPulse 1s ease-in-out 3;
}

/* Heatwave sweat drops */
@keyframes sweatDrop {
  0%   { transform: translateY(0);   opacity: 1; }
  100% { transform: translateY(80px); opacity: 0; }
}
.wx-sweat {
  position: absolute;
  top: 8px;
  font-size: 1.1rem;
  pointer-events: none;
  animation: sweatDrop 2s ease-in forwards;
  z-index: 5;
}
#weatherCard { position: relative; }

/* Full moon glow */
@keyframes moonGlow {
  0%,100% { text-shadow: 0 0 4px rgba(220,220,180,.4); }
  50%      { text-shadow: 0 0 14px rgba(220,220,180,1), 0 0 28px rgba(220,220,180,.5); }
}
.wx-full-moon {
  display: inline-block;
  animation: moonGlow 2.5s ease-in-out infinite;
}

/* $42 Don't Panic badge */
.dont-panic-badge {
  display: inline-block;
  margin-left: 6px;
  background: #000;
  color: #fff;
  font-size: .6rem;
  font-weight: 900;
  letter-spacing: .06em;
  border-radius: 3px;
  padding: 1px 6px;
  vertical-align: middle;
  font-family: monospace;
}
@keyframes wormRise {
  0%   { transform: translateY(0) scaleX(1);   opacity: 1; }
  40%  { transform: translateY(-18px) scaleX(-1); opacity: 1; }
  70%  { transform: translateY(-28px) scaleX(1);  opacity: 1; }
  100% { transform: translateY(-40px) scaleX(-1); opacity: 0; }
}
.cal-worm {
  position: absolute;
  bottom: 2px;
  left: 50%;
  transform: translateX(-50%);
  font-size: 1.1rem;
  line-height: 1;
  pointer-events: none;
  animation: wormRise 2.2s ease-in-out forwards;
  z-index: 20;
}
.one-ring-banner {
  position: absolute;
  bottom: 12px;
  left: 50%;
  transform: translateX(-50%) translateY(8px);
  background: var(--surface);
  border: 1px solid var(--accent);
  border-radius: 20px;
  padding: 5px 16px;
  font-size: var(--text-xs);
  font-style: italic;
  color: var(--accent);
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.4s ease, transform 0.4s ease;
  z-index: 10;
}
.one-ring-banner-show {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}

/* ── Daily Briefing Banner ── */
.briefing-pill {
  font-size: var(--text-xs);
  padding: 3px 10px;
  border-radius: 20px;
  background: var(--surface2);
  border: 1px solid rgba(255,255,255,.10);
  color: var(--muted);
  cursor: pointer;
  white-space: nowrap;
  transition: background .15s, color .15s, border-color .15s;
}
.briefing-pill:hover { color: var(--text); border-color: rgba(255,255,255,.22); }
.briefing-pill.active {
  background: color-mix(in srgb, var(--accent) 18%, transparent);
  border-color: color-mix(in srgb, var(--accent) 42%, transparent);
  color: var(--accent);
}
body.light-mode .briefing-pill { border-color: rgba(0,0,0,.12); }
body.light-mode .briefing-pill.active { background: rgba(79,142,247,.12); border-color: rgba(79,142,247,.35); }

.briefing-banner {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 10px 12px;
  background: linear-gradient(135deg, rgba(79,142,247,.12), rgba(126,87,194,.10));
  border-bottom: 1px solid rgba(79,142,247,.18);
  font-size: var(--text-sm);
  line-height: 1.5;
  color: var(--text);
  flex-shrink: 0;
}
.briefing-body { flex: 1; min-width: 0; }
.briefing-loading {
  justify-content: center;
  color: var(--muted);
  font-size: var(--text-sm);
  padding: 8px 12px;
}
.briefing-dots { letter-spacing: .15em; }

/* Light theme */
body.light-mode .briefing-banner {
  background: linear-gradient(135deg, rgba(79,142,247,.08), rgba(126,87,194,.06));
  border-bottom-color: rgba(79,142,247,.2);
}
/* Nautical themes */

/* ── Family Assistant ── */
.assistant-card-body {
  padding: 0 !important;
  display: flex;
  flex-direction: column;
}
.assistant-messages {
  overflow-y: auto;
  padding: 10px 12px;
  display: flex;
  flex-direction: column;
  gap: 7px;
  max-height: 260px;
  min-height: 60px;
}
.assistant-welcome {
  color: var(--muted);
  font-size: var(--text-xs);
  text-align: center;
  padding: 6px 4px;
  line-height: 1.55;
}
/* Quick-prompt ticker — auto-scrolls like the weather ticker; pauses on hover/focus so chips stay clickable */
.assistant-ticker {
  overflow: hidden;
  white-space: nowrap;
  padding: 4px 0 6px;
}
.assistant-ticker-track {
  display: flex;
  width: max-content;
  animation: assistant-ticker-scroll linear 24s infinite;
}
.assistant-ticker:hover .assistant-ticker-track,
.assistant-ticker:focus-within .assistant-ticker-track {
  animation-play-state: paused;
}
.assistant-ticker-group {
  display: flex;
  gap: 6px;
  padding-right: 6px;   /* same as gap, so the loop seam is even */
  flex-shrink: 0;
}
@keyframes assistant-ticker-scroll {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}
@media (prefers-reduced-motion: reduce) {
  .assistant-ticker { overflow-x: auto; }
  .assistant-ticker-track { animation: none; }
}
.assistant-chip {
  background: var(--surface2);
  border: 1px solid rgba(255,255,255,.10);
  color: var(--text);
  font-size: var(--text-xs);
  padding: 6px 11px;
  border-radius: 14px;
  cursor: pointer;
  line-height: 1.2;
  flex-shrink: 0;       /* keep each chip full-width; the row scrolls instead of wrapping */
  white-space: nowrap;
  transition: background .15s, border-color .15s, transform .1s;
}
html:not(.touch-input) .assistant-chip:hover  { background: var(--accent); border-color: var(--accent); color: #fff; }
.assistant-chip:active { transform: scale(0.95); }
.assistant-msg {
  max-width: 86%;
  padding: 7px 11px;
  border-radius: 14px;
  font-size: var(--text-sm);
  line-height: 1.45;
  word-break: break-word;
}
.assistant-msg.user {
  align-self: flex-end;
  background: var(--accent);
  color: #fff;
  border-bottom-right-radius: 4px;
}
.assistant-msg.ai {
  align-self: flex-start;
  background: var(--surface2);
  color: var(--text);
  border-bottom-left-radius: 4px;
}
.assistant-thinking {
  letter-spacing: 4px;
  opacity: 0.5;
  animation: assistantPulse 1.1s ease-in-out infinite;
}
@keyframes assistantPulse {
  0%, 100% { opacity: 0.3; }
  50%       { opacity: 0.9; }
}
.assistant-voice-status {
  padding: 4px 12px 0;
  font-size: var(--text-sm);
  color: var(--muted);
  animation: assistantPulse 1.6s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) {
  .assistant-voice-status { animation: none; }
}
.assistant-icon-btn {
  background: none;
  border: none;
  color: var(--muted);
  cursor: pointer;
  font-size: 1.3rem;
  line-height: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.assistant-clear-btn { font-size: 1.6rem; }
.assistant-input-row {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px 10px;
  border-top: 1px solid rgba(255,255,255,.06);
}
.assistant-input {
  flex: 1;
  /* A text input won't shrink below its ~20-character default width
     without this, which pushed Send off the card's right edge. */
  min-width: 0;
  background: var(--surface2);
  border: 1px solid rgba(255,255,255,.1);
  border-radius: 20px;
  padding: 6px 13px;
  color: var(--text);
  font-size: var(--text-sm);
  outline: none;
  font-family: inherit;
}
.assistant-input:focus {
  border-color: var(--accent);
}
.assistant-input::placeholder {
  color: var(--muted);
}
.assistant-send {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--accent);
  color: #fff;
  border: none;
  cursor: pointer;
  font-size: 1.1rem;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  transition: opacity .15s;
}
.assistant-send:hover { opacity: .82; }
/* Drawn arrow, sized to the circle: the old "↑" text glyph stayed thin and
   small when the button grew to 52px on the wall panel. */
.assistant-send svg {
  width: 55%; height: 55%;
  fill: none; stroke: currentColor; stroke-width: 2.6;
  stroke-linecap: round; stroke-linejoin: round;
}

.assistant-mic {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--surface2);
  border: 1px solid rgba(255,255,255,.1);
  cursor: pointer;
  font-size: .9rem;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  transition: background .15s, box-shadow .15s;
}
html:not(.touch-input) .assistant-mic:hover { background: var(--surface); }

/* 'prompt' — subtle ring to hint first-time setup */
.assistant-mic.mic-prompt {
  box-shadow: 0 0 0 2px var(--accent);
}
.assistant-mic.mic-prompt:hover {
  background: var(--accent);
}

/* 'denied' — muted, strikethrough-style indicator */
.assistant-mic.mic-blocked {
  opacity: .45;
  filter: grayscale(1);
  cursor: not-allowed;
  position: relative;
}
.assistant-mic.mic-blocked::after {
  content: '';
  position: absolute;
  inset: 4px;
  border-radius: 50%;
  border: 2px solid #c0392b;
  opacity: .7;
}
.assistant-mic.mic-blocked:hover {
  opacity: .65;
  filter: grayscale(.5);
  cursor: pointer;
}

/* 'recording' — red pulse */
.assistant-mic.recording {
  background: #c0392b;
  border-color: #c0392b;
  box-shadow: 0 0 0 3px rgba(192,57,43,.35);
  animation: micPulse 1s ease-in-out infinite;
}
@keyframes micPulse {
  0%, 100% { box-shadow: 0 0 0 3px rgba(192,57,43,.35); }
  50%       { box-shadow: 0 0 0 6px rgba(192,57,43,.15); }
}

/* Help / priming / note messages */
.assistant-mic-help,
.assistant-mic-priming,
.assistant-note {
  font-size: var(--text-xs) !important;
  line-height: 1.55;
}
.assistant-mic-help { border-left: 2px solid #c0392b; padding-left: 8px !important; }
.assistant-mic-priming { opacity: .75; font-style: italic; }
.assistant-note { opacity: .7; font-style: italic; }
.assistant-confirm-actions { display: flex; gap: 8px; margin-top: 8px; }
.mic-help-step {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  background: var(--accent);
  color: #fff;
  border-radius: 50%;
  font-size: .68rem;
  font-weight: 700;
  font-style: normal;
  margin-right: 4px;
  flex-shrink: 0;
}
/* Light mode adjustments */
body.light-mode .assistant-msg.user { background: var(--accent); color: #fff; }
body.light-mode .assistant-input {
  border-color: rgba(0,0,0,.12);
  background: #f0f1f5;
}
body.light-mode .assistant-input-row {
  border-top-color: rgba(0,0,0,.08);
}
/* Nautical mode */

/* ── Widget drag-and-drop ── */
.widget-drag-handle {
  cursor: grab;
  color: var(--muted);
  font-size: 1rem;
  padding: 0 6px 0 0;
  user-select: none;
  opacity: 0;
  transition: opacity .15s;
  flex-shrink: 0;
  touch-action: none;   /* a finger on the handle drags (startWidgetDrag), not scrolls */
}
.card-header:hover .widget-drag-handle { opacity: 0.5; }
.widget-drag-handle:hover { opacity: 1 !important; cursor: grab; }
.widget-dragging { opacity: 0.35; }
.widget-drop-target {
  outline: 2px dashed var(--accent);
  outline-offset: 3px;
  border-radius: 12px;
}
/* No hover to reveal it on touch, so it stays visible, with a finger-sized
   hit area (the negative margin keeps the header row the same height). The
   wall panel may report a mouse, so Wall display gets the same treatment. */
@media (hover: none) {
  .widget-drag-handle { opacity: 0.4; padding: 10px 12px 10px 4px; margin: -10px 0; }
}
html.wall-display .widget-drag-handle {
  opacity: 0.45; font-size: 1.3rem; padding: 10px 12px 10px 4px; margin: -10px 0;
}

/* ── Widget visibility toggles (Settings → Widgets) ── */
.widget-toggle-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin-top: 6px;
}

/* Column header row */
.widget-placement-header {
  display: grid;
  grid-template-columns: 18px 1fr 70px 70px 70px;
  gap: 8px;
  padding: 4px 12px 6px;
  font-size: var(--text-xs);
  color: var(--muted);
  font-weight: var(--weight-semi);
  text-transform: uppercase;
  letter-spacing: .04em;
}
.widget-placement-col-label {
  text-align: center;
}
.widget-placement-hint {
  margin: 2px 0 8px;
  font-size: var(--text-xs);
  color: var(--muted);
}

.widget-toggle-row {
  display: grid;
  grid-template-columns: 18px 1fr 70px 70px 70px;
  align-items: center;
  gap: 8px;
  padding: 9px 12px;
  border-radius: 8px;
  font-size: var(--text-sm);
  color: var(--text);
  transition: background .15s;
}
.widget-toggle-row:hover {
  background: var(--surface2);
}
.widget-toggle-row input[type=checkbox] {
  width: 16px;
  height: 16px;
  accent-color: var(--accent);
  cursor: pointer;
  flex-shrink: 0;
}
/* Radio placement labels */
.widget-placement-radio {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  cursor: pointer;
  font-size: var(--text-xs);
  color: var(--muted);
  padding: 4px 6px;
  border-radius: 6px;
  border: 1px solid rgba(255,255,255,.12);
  transition: background .12s, border-color .12s, color .12s;
  user-select: none;
}
.widget-placement-radio:has(input:checked) {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
  font-weight: var(--weight-semi);
}
.widget-placement-radio input[type=radio] {
  display: none;  /* visually hidden — label acts as the toggle */
}
.widget-placement-radio span { pointer-events: none; }

/* ════════════════════════════════════════════════════════════════
   TOUCHSCREEN OPTIMIZATIONS
   ════════════════════════════════════════════════════════════════ */

/* ── 1. Eliminate 300ms tap delay on all interactive elements ── */
a, button, input, select, textarea, label,
.btn, .cal-tab, .cal-view-tab, .modal-tab,
.cal-day, .cal-event, .week-event, .day-event-pill,
.list-check, .list-item, .week-meal-item, .week-add-meal-btn,
.day-meal-slot, .budget-pill, .sms-contact-row, .meal-suggestion,
.bg-thumb, .bg-thumb-del, .bg-thumb-more, .widget-toggle-row {
  touch-action: manipulation;
}

/* ── 2. Suppress hover effects on touch devices ──
   (hover states persist after tap, creating stuck highlights). The main
   controls are gated on html:not(.touch-input) instead (set from the last
   pointer type in family-hub.js), which also covers the wall panel: it has a
   mouse attached, so (hover: none) never matches there. */
@media (hover: none) {
  .cal-tab:hover                 { opacity: 1; }
  .cal-tab.cal-tab-off:hover     { opacity: .45; }
  .modal-tab:hover:not(.active)  { color: var(--muted); }
  .budget-pill:hover             { background: color-mix(in srgb, var(--red) 18%, transparent); }
  .budget-item-del:hover         { color: var(--muted); }
  .sms-contact-row:hover         { background: none; }
  .week-meal-item:hover          { background: color-mix(in srgb, var(--green) 18%, transparent); }
  .week-add-meal-btn:hover       { border-style: dashed; background: none; }
  .day-meal-slot:hover           { border-color: transparent; }
  /* Always show remove button on touch — no hover to reveal it */
  .meal-remove-btn, .day-meal-remove-btn, .cal-remove-btn { opacity: 1; }
  .day-meal-slot-link:hover      { text-decoration: none; }
  .widget-toggle-row:hover       { background: none; }
}

/* ── 3. Active (press) feedback for touch ── */
@media (hover: none) {
  .btn:active                    { opacity: .75; transform: scale(.97); }
  .cal-view-tab:active           { background: var(--surface2); color: var(--text); }
  .modal-tab:active:not(.active) { background: var(--surface2); color: var(--text); }
  .cal-month-nav button:active   { background: var(--accent); }
  .cal-tab:active                { opacity: .65; }
  .cal-day:active                { background: var(--surface2); }
  /* The 3s worm hold (setupWormHold) would otherwise start a text selection */
  .cal-day                       { -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
  .cal-event:active,
  .week-event:active,
  .day-event-pill:active         { opacity: .7; }
  .list-check:active             { transform: scale(1.15); }
  .sms-contact-row:active        { background: var(--surface2); }
  .week-meal-item:active         { background: color-mix(in srgb, var(--green) 32%, transparent); }
  .day-meal-slot:active          { border-color: var(--accent); }
  .budget-pill:active            { background: color-mix(in srgb, var(--red) 32%, transparent); }
  .widget-toggle-row:active      { background: var(--surface2); }
}

/* ── 4. Larger tap targets (touch-only) ── */
@media (hover: none) {
  /* Primary & regular buttons: taller tap targets */
  .btn        { padding: 12px 18px; }
  .btn-sm     { padding: 10px 14px; font-size: 0.82rem; }

  /* Bigger tap targets for card header icon buttons (refresh, clear done) */
  .card-header button {
    min-width: 36px;
    min-height: 36px;
    display: flex;
    align-items: center;
    justify-content: center;
  }

  /* Widget toggle rows: more height */
  .widget-toggle-row { padding: 13px 12px; }
  .widget-toggle-row input[type=checkbox] { width: 20px; height: 20px; }

  /* Calendar nav prev/next arrows: 44×44 minimum */
  .cal-month-nav button {
    width: 44px;
    height: 44px;
    font-size: 1.1rem;
  }

  /* Assistant send/mic: 30px circles are too small for fingers */
  .assistant-send,
  .assistant-mic {
    width: 42px;
    height: 42px;
    flex-shrink: 0;
  }

  /* View tabs (Month/Week/Day): taller hit area */
  .cal-view-tab {
    padding: 9px 15px;
    font-size: 0.82rem;
  }

  /* Calendar tabs (calendar name pills): taller */
  .cal-tab {
    padding: 8px 14px;
    font-size: 0.82rem;
  }

  /* Settings modal tabs */
  .modal-tab {
    padding: 10px 12px;
    font-size: 0.8rem;
  }

  /* List checkboxes: larger touch circle */
  .list-check {
    width: 28px;
    height: 28px;
    flex-shrink: 0;
  }
  .list-check.done::after { font-size: 14px; }

  /* List items: more vertical breathing room */
  .list-item { padding: 8px 0; }

  /* List text inputs: taller */
  .list-input-row input { padding: 11px 14px; font-size: 0.95rem; }

  /* Settings panels: allow full height on small screens */
  #settingsModal .settings-panels { max-height: unset; }

  /* SMS rows */
  .sms-contact-row { padding: 14px 16px; }

  /* Meal suggestion cards */
  .meal-suggestion { padding: 16px; }
}

/* ── 5. Prevent accidental text selection on buttons & interactive UI ── */
button, .cal-tab, .cal-view-tab, .modal-tab,
.cal-day, .cal-event, .week-event, .day-event-pill,
.list-check, .budget-pill, .card-header h2 {
  user-select: none;
  -webkit-user-select: none;
}

/* ── 6. Smooth momentum scrolling everywhere ── */
.settings-panels,
.sidebar,
#weekView,
.year-strip-scroll,
#eventEditorModal .modal {
  -webkit-overflow-scrolling: touch;
}

/* ── 7. Mobile: full-width inputs & bigger modal panels ── */
@media (max-width: 640px), (max-height: 500px) and (orientation: landscape) and (pointer: coarse) {
  /* Modal fills more vertical space */
  .modal { max-height: 92svh; display: flex; flex-direction: column; }
  .settings-panels { max-height: unset; flex: 1; overflow-y: auto; }
}

/* ── 8. Mobile shell: quick-add FAB + Lists sheet ──
   Calendar is the permanent mobile surface — no bottom nav bar. Two floating
   buttons replace it (add event, open the Lists sheet); Shopping/To-Do/Home
   Assistant are reparented into the sheet by applyWidgetPlacement() (see
   MOBILE_SHEET_SECTIONS in family-hub.js) rather than duplicating markup.
   Everything else in the sidebar simply stays hidden on mobile, as it always
   was — none of it was ever tab-reachable before this either. */

/* Hidden on desktop — shown only inside the 640px media query below */
.mobile-fab { display: none; }
.mobile-sheet-overlay { display: none; }

@media (max-width: 640px), (max-height: 500px) and (orientation: landscape) and (pointer: coarse) {
  .sidebar { display: none; }
  /* Portrait phones already get one column from the 860px rule; a sideways
     phone is wider than that and would keep an empty sidebar column */
  .layout { grid-template-columns: 1fr; }

  /* iOS: prevent auto-zoom when focusing any text input */
  input:not([type=checkbox]):not([type=radio]),
  select,
  textarea {
    font-size: max(16px, 1em);
  }

  .mobile-fab {
    display: flex;
    align-items: center;
    justify-content: center;
    position: fixed;
    bottom: calc(18px + env(safe-area-inset-bottom, 0px));
    width: 52px;
    height: 52px;
    border-radius: 50%;
    border: none;
    font-size: 1.4rem;
    line-height: 1;
    cursor: pointer;
    touch-action: manipulation;
    box-shadow: var(--shadow);
    /* Below .modal-overlay (100), so no modal ever has the FAB on top of
       its buttons; the Lists sheet (200) covers it too */
    z-index: 90;
  }
  .mobile-fab-add {
    right: 16px;
    background: var(--accent);
    color: #fff;
  }
  /* The FAB sits above the modal layer; hide it while the event editor is
     open so it doesn't cover the form (class set by openEventEditor()) */
  body.evt-editor-open #mobileAddFab { display: none; }

  /* App icon doubles as the Lists trigger on mobile — give it an obvious
     button outline instead of the bare, unbordered emoji desktop keeps. */
  .cal-app-emoji,
  #calBudgetPrivacyBtn {
    border: 1px solid var(--line);
    background: var(--surface);
    border-radius: 8px;
    padding: 3px 8px;
    cursor: pointer;
  }
  .cal-app-emoji:active { background: var(--surface2); }

  /* Settings — its own top-level nav item, top-right corner of row 1
     (flex:0 0 auto keeps it from shrinking in the one-row header).
     Same bordered-button treatment as the 🏠 Lists trigger for parity. */
  .cal-header-settings-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    border: 1px solid var(--line);
    background: var(--surface);
    border-radius: 8px;
    padding: 3px 8px;
    font-size: 1.05rem;
    cursor: pointer;
  }
  .cal-header-settings-btn:active { background: var(--surface2); }

  /* Lists sheet — a bottom sheet, not a full-page tab swap. Shown/hidden by
     toggling inline display (openMobileLists/closeMobileLists in
     family-hub.js), not a class — display:none above is the closed state. */
  .mobile-sheet-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,.5);
    z-index: 200;
    align-items: flex-end;
  }
  /* Solid, not var(--surface): the translucent card surface let the Month
     grid show through behind the lists. */
  .mobile-sheet {
    background: var(--surface-solid);
    width: 100%;
    max-height: 80svh;
    transition: transform .2s ease, height .2s ease, max-height .2s ease;
    border-radius: 16px 16px 0 0;
    display: flex;
    flex-direction: column;
    padding-bottom: env(safe-area-inset-bottom, 0px);
  }
  /* Drag down to close, up (or tap) to expand: setupMobileSheetDrag(). The
     visible pill is ::before; the element itself is a full-width strip so
     there's something a thumb can actually grab. */
  .mobile-sheet.expanded { height: 92svh; max-height: 92svh; }
  .mobile-sheet.dragging { transition: none; }
  .mobile-sheet-handle {
    height: 22px;
    padding-top: 9px;
    touch-action: none;
    cursor: grab;
  }
  .mobile-sheet-handle::before {
    content: '';
    display: block;
    width: 40px;
    height: 5px;
    background: var(--muted);
    opacity: .5;
    border-radius: 3px;
    margin: 0 auto;
  }
  .mobile-sheet-tabs {
    display: flex;
    gap: 6px;
    padding: 4px 12px 10px;
    border-bottom: 1px solid var(--line);
  }
  .mobile-sheet-tab {
    flex: 1;
    background: var(--surface2);
    border: none;
    color: var(--muted);
    padding: 8px 6px;
    border-radius: 8px;
    font-size: var(--text-sm);
    font-weight: var(--weight-semi);
    font-family: inherit;
    cursor: pointer;
    touch-action: manipulation;
  }
  .mobile-sheet-tab.active { background: var(--accent); color: #fff; }
  /* Icon-only actions that open something else (💡 Find Events), so they
     sit at the end of the tab row without taking a tab's share of width. */
  .mobile-sheet-action {
    flex: 0 0 auto;
    background: var(--surface2);
    border: none;
    padding: 8px 10px;
    border-radius: 8px;
    font-size: var(--text-sm);
    cursor: pointer;
    touch-action: manipulation;
  }

  .mobile-sheet-body { flex: 1; overflow-y: auto; min-height: 0; }
  .mobile-sheet-section { display: none; }
  .mobile-sheet-section.active { display: block; }
  /* Cards reparented into the sheet keep the paper material and border
     (this rule once stripped them, but Tactile always outranked it), and
     only lose their rounded corners. */
  .mobile-sheet-body .card { border-radius: 0; }
  /* The active sheet tab already names the list ("🛒 Shopping" etc.) — the
     reparented card's own header title would just repeat it, so only its
     header title is dropped in this context; action buttons in the same row
     (SMS, refresh, clear-done/-all) stay. */
  .mobile-sheet-body .card-header h2 { display: none; }
  /* Widget drag-to-reorder has no meaning inside the sheet — a card's
     position there is fixed by MOBILE_SHEET_SECTIONS, not draggable — so
     the ⠿ grip handle initWidgetDragDrop() injects into every card header
     is dead chrome here even though it's harmless (nothing calls it). */
  .mobile-sheet-body .widget-drag-handle { display: none; }
  /* The header row holds only its action-button group once the title is
     hidden; space-between (the desktop default) would flush it left. */
  .mobile-sheet-body .card-header { justify-content: flex-end; }

  .mobile-sheet-footer {
    display: flex;
    gap: 8px;
    padding: 10px 12px;
    border-top: 1px solid var(--line);
  }
  .mobile-sheet-footer button {
    flex: 1;
    background: var(--surface2);
    border: none;
    color: var(--text);
    font-family: inherit;
    padding: 10px 6px;
    border-radius: 8px;
    font-size: var(--text-sm);
    cursor: pointer;
    touch-action: manipulation;
  }
  .mobile-sheet-close { background: var(--accent) !important; color: #fff !important; }

  /* The 💡 discover-events trigger lives in the sheet's tab row instead */
  #eventsDiscoverBtn { display: none; }
  /* Six source tabs in a sideways-scrolling row showed two on a phone, with
     nothing to say the rest existed, and plain wrapping left them ragged.
     An even 3×2 grid of buttons instead, each with its count on a second
     line; Refresh moves up beside ✕ so the grid gets the full width.
     (!important: the tab row's inline display/overflow.) */
  #eventsModal .events-source-tabs {
    display: grid !important;
    grid-template-columns: repeat(3, 1fr);
    gap: 6px;
    overflow: visible !important;
  }
  #eventsModal .event-tab {
    white-space: normal;
    text-align: center;
    line-height: 1.25;
    padding: 8px 4px;
    border-radius: 8px;
  }
  /* .event-tab only: the category sub-tabs below reuse .event-tab-count */
  #eventsModal .event-tab-icon,
  #eventsModal .event-tab .event-tab-count { display: block; }
  #eventsModal .events-refresh-row { display: none; }
  #eventsModal .events-refresh-top { display: inline-flex; }
  /* Around Town's dozen category chips wrapped into five rows and left the
     list a sliver. One swipeable row instead, faded at the right edge so
     it reads as more-to-the-side. (!important: the bar's inline flex-wrap.) */
  #eventsModal .events-sub-tabs {
    flex-wrap: nowrap !important;
    overflow-x: auto;
    scrollbar-width: none;
    -webkit-mask-image: linear-gradient(to right, #000 85%, transparent);
            mask-image: linear-gradient(to right, #000 85%, transparent);
  }
  #eventsModal .events-sub-tabs::-webkit-scrollbar { display: none; }
  #eventsModal .event-subtab { flex-shrink: 0; }
  /* More room for the list itself: taller modal (its inline max-height is
     80vh), no bottom Close (✕ is in the title row), shorter card images. */
  #eventsModal .modal { max-height: 92svh !important; padding: 16px; }
  #eventsModal .modal-actions { display: none; }
  /* Image cards become compact rows: a 72px thumbnail beside the text, the
     date/distance moving from the image overlay to the footer meta line,
     and a two-line summary. About twice as many events per screen. */
  #eventsModal .event-card-img { flex-direction: row; }
  #eventsModal .event-card-img .event-image {
    flex: 0 0 72px;
    height: 72px;
    margin: 10px 0 10px 10px;
    border-radius: 8px;
  }
  #eventsModal .event-card-img .event-overlay { display: none; }
  #eventsModal .event-img-meta { display: inline; }
  #eventsModal .event-card-img .event-card-content { padding: 10px 12px; min-width: 0; }
  #eventsModal .event-card-img .event-title { margin: 0; font-size: var(--text-sm); }
  #eventsModal .event-card-img .event-description { margin: 4px 0 0; font-size: var(--text-xs); -webkit-line-clamp: 2; }
  #eventsModal .event-compact-foot { flex-wrap: wrap; row-gap: 6px; }

  /* Prev/next period arrows are redundant with the swipe gesture
     (setupCalSwipe() in family-hub.js) and just eat header space */
  .cal-month-nav button { display: none; }

  /* Trim the view tabs a little below the generic @media (hover: none) tap
     padding above (must come after it in source order to win at equal
     specificity) — row 2 of the header is otherwise taller than it needs
     to be now that it's just three short labels. */
  .cal-view-tab { padding: 7px 13px; }

  /* Tighten Day/Agenda: the desktop paddings/min-heights were sized for a
     wall panel viewed from a few feet away, not a phone held in the hand */
  .day-hour-row  { min-height: 40px; }
  .day-hour-label { padding: 3px 8px 0 0; }
  .day-time-label { padding-top: 2px; padding-right: 8px; }
  .agenda-day-header { padding: 12px 10px 5px; }
  .agenda-event, .day-event-pill, .week-event { padding: 7px 10px; }

  /* Event editor: `90vh` is iOS Safari's *large* viewport (assumes the
     address bar is collapsed), so with the bar visible the modal's bottom —
     Save/Delete — renders below the fold with nothing left to scroll into.
     `svh` is the guaranteed-visible viewport regardless of toolbar state.
     Matches the ID+class specificity of the rule this overrides, so it
     needs no !important; being later in the file settles the tie. */
  #eventEditorModal .modal {
    max-height: 90svh;
    padding-bottom: max(28px, env(safe-area-inset-bottom, 0px));
  }
  /* .modal-overlay scrolls too (base rule), so this modal was sitting in
     two nested scroll containers: dragging near the top/bottom of its own
     content handed the gesture to the overlay instead, which moved the
     whole card around the viewport rather than just scrolling what's inside
     it. #eventEditorModal .modal already caps its own height and scrolls
     internally, so the overlay never needs to scroll on top of that. */
  #eventEditorModal {
    overflow-y: hidden;
    padding: 6px;
  }
  /* Claw back a bit more width for the paired date/time fields (below).
     Stacking them to one-per-line solved the overlap outright but cost too
     much vertical space, so this trims every layer around the pair instead:
     overlay padding above, modal padding + input padding here. */
  #eventEditorModal .modal {
    padding-left: 8px;
    padding-right: 8px;
  }
  .evt-editor-time-row {
    gap: 4px;
  }
  .evt-editor-time-row > .field {
    flex-basis: 130px; min-width: 130px;
  }
  .evt-editor-time-row input[type=date] {
    padding-left: 4px; padding-right: 4px;
  }

  /* Preferences: bigger text + line-height for what's left after
     renderSettingsNav() drops the desktop-only panels (family-hub.js).
     Most panel copy is inline-styled (style="font-size:.82rem"), which
     only !important can override; the more specific rules below still win
     over the catch-all on specificity even with both marked important. */
  #settingsModal .settings-panel,
  #settingsModal .settings-panel * {
    font-size: 1rem !important;
    line-height: var(--leading-normal) !important;
  }
  #settingsModal .settings-section-head,
  #settingsModal .lc-section-title      { font-size: 1.05rem !important; }
  #settingsModal .settings-panel .settings-group-head { font-size: .8rem !important; }
  /* Helper copy one step below body text, so labels and controls lead */
  #settingsModal .settings-panel .settings-hint,
  #settingsModal .settings-panel .toggle-sub,
  #settingsModal .settings-panel .settings-intro,
  #settingsModal .settings-panel .settings-intro * { font-size: .9rem !important; }
  /* Settings that only mean something on a wall display or desktop */
  #settingsModal .settings-desktop-only { display: none; }
  /* Add forms: the first text box gets its own line, the rest share the next
     one instead of each squeezing to a truncated sliver */
  #settingsModal .lc-add-row > .lc-input,
  #settingsModal .lc-add-row > .lc-select { flex: 1 1 88px; min-width: 0; width: auto; }
  /* ...unless it's just one box and its Add button, which fit on one line */
  #settingsModal .lc-add-row > .lc-input:first-child:not(:nth-last-child(2)) { flex-basis: 100%; }
  #settingsModal #cdEmojiInput { flex: 0 0 52px; }
  #settingsModal .lc-add-row > input[type=time] { flex-basis: 110px; }
  #settingsModal .lc-add-row > input[type=date] { flex-basis: 140px; }
  /* Narrow theme buttons wrap some labels under their emoji and not others;
     stack them all so the grid reads evenly */
  #settingsModal .appearance-grid > .btn { flex-direction: column; gap: 2px !important; }
  #settingsModal .settings-nav-label    { font-size: 0.8rem !important; }
  #settingsModal .settings-nav-item     { font-size: 1.05rem !important; padding: 12px 10px !important; }
  #settingsModal .modal h2              { font-size: 1.4rem !important; }
}

/* ── Budget Card ── */
.budget-card-nav {
  background: var(--surface2);
  border: none;
  color: var(--text);
  width: 28px; height: 28px;
  border-radius: 6px;
  cursor: pointer;
  font-size: 1.1rem;
  display: flex; align-items: center; justify-content: center;
  touch-action: manipulation;
}
html:not(.touch-input) .budget-card-nav:hover { background: var(--accent); color: #fff; }

.budget-add-form-inner {
  background: var(--surface2);
  border-radius: 10px;
  padding: 14px;
  margin-bottom: 14px;
}
.budget-add-title {
  font-size: var(--text-base);
  font-weight: var(--weight-semi);
  margin-bottom: 12px;
}
.budget-select,
.budget-date-input {
  width: 100%;
  background: var(--surface);
  border: 1px solid rgba(255,255,255,.12);
  border-radius: 8px;
  color: var(--text);
  padding: 8px 10px;
  font-size: var(--text-base);
}
.budget-select:focus,
.budget-date-input:focus { outline: none; border-color: var(--accent); }

.budget-card-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 0;
  border-bottom: 1px solid rgba(255,255,255,.05);
  font-size: var(--text-sm);
}
.budget-card-row:last-of-type { border-bottom: none; }
.budget-card-row-past { opacity: .45; }
.budget-card-row-today { background: color-mix(in srgb, var(--red) 7%, transparent); border-radius: 6px; padding: 8px 6px; margin: 0 -6px; }
.budget-card-date {
  font-size: var(--text-xs);
  color: var(--muted);
  min-width: 60px;
  flex-shrink: 0;
}
.budget-card-today-badge {
  display: inline-block;
  background: color-mix(in srgb, var(--red) 25%, transparent);
  color: var(--red);
  font-size: var(--text-xs);
  font-weight: var(--weight-semi);
  border-radius: 4px;
  padding: 1px 5px;
  margin-left: 3px;
  vertical-align: middle;
}
.budget-card-name {
  flex: 1;
  font-weight: var(--weight-medium);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.budget-card-amt {
  font-weight: var(--weight-semi);
  color: var(--red);
  flex-shrink: 0;
}
.budget-manage-toggle {
  background: none;
  border: none;
  color: var(--muted);
  font-size: var(--text-sm);
  cursor: pointer;
  padding: 10px 0 4px;
  width: 100%;
  text-align: left;
  touch-action: manipulation;
}
.budget-manage-toggle:hover { color: var(--text); }
.budget-hidden-notice { font-size: var(--text-xs); color: var(--muted); text-align: center; padding: 10px 0; }

/* ── Home Assistant Card ── */
.ha-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 0;
  border-bottom: 1px solid var(--line);
}
.ha-row:last-child { border-bottom: none; }
.ha-icon  { flex: 0 0 auto; font-size: 1rem; line-height: 1; }
.ha-name  {
  flex: 1 1 auto;
  font-size: var(--text-sm);
  font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ha-state {
  flex: 0 0 auto;
  font-size: var(--text-xs);
  color: var(--muted);
  text-align: right;
}
.ha-btn {
  flex: 0 0 auto;
  background: var(--surface2);
  border: 1px solid var(--line);
  color: var(--text);
  border-radius: 6px;
  padding: 3px 8px;
  font-size: var(--text-xs);
  cursor: pointer;
  touch-action: manipulation;
  min-width: 62px;
}
html:not(.touch-input) .ha-btn:hover:not(:disabled) { background: var(--accent); border-color: var(--accent); color: #fff; }
.ha-btn:disabled { opacity: .5; cursor: default; }
/* Offline, unknown, or a bad entity ID — state is unreliable, so say so quietly
   rather than rendering a control that would lie about what it does. */
.ha-row-dim .ha-name, .ha-row-dim .ha-state { opacity: .5; }
.ha-empty {
  color: var(--muted);
  font-size: var(--text-sm);
  text-align: center;
  padding: 6px 0;
}
.ha-error {
  color: var(--muted);
  font-size: var(--text-xs);
  background: var(--surface2);
  border-radius: 6px;
  padding: 6px 8px;
  margin-bottom: 6px;
}
/* Settings → Home Assistant: Test connection result */
.ha-test      { font-size: var(--text-xs); color: var(--muted); }
.ha-test-ok   { color: var(--accent); }
.ha-test-bad  { color: var(--muted); font-weight: 600; }

/* ── TV / Display mode ─────────────────────────────────────────── */
body.tv-mode {
  font-size: 1.15rem;
}
body.tv-cursor-hidden,
body.tv-cursor-hidden * { cursor: none !important; }

/* Hide all interactive / editing chrome */
body.tv-mode #controlCard,
body.tv-mode .list-input-row,
body.tv-mode #todoExtra,
body.tv-mode .assistant-input-row,
body.tv-mode #briefingPill,
body.tv-mode #assistantTtsBtn,
body.tv-mode .widget-drag-handle,
body.tv-mode .budget-manage-toggle,
body.tv-mode #budgetAddBtn,
body.tv-mode #budgetAddForm,
body.tv-mode .bulk-btn,
body.tv-mode .cal-strip-add,
body.tv-mode [onclick*="openEventEditor"],
body.tv-mode [onclick*="openMealDetail"] .meal-edit-btn,
body.tv-mode .shop-extra-row,
body.tv-mode #shopClearDoneBtn,
body.tv-mode #shopClearAllBtn,
body.tv-mode #todoClearDoneBtn {
  display: none !important;
}

/* Larger, bolder list items for readability at distance */
body.tv-mode .list-item-text   { font-size: 1.1rem; }
body.tv-mode .card-header h2   { font-size: 1.25rem; }
body.tv-mode .cal-day-num      { font-size: 1.1rem; }
body.tv-mode .event-pill       { font-size: .95rem; padding: 3px 7px; }
body.tv-mode .mini-month-label { font-size: .9rem; }

/* Slight card padding bump so content breathes */
body.tv-mode .card-body { padding: 14px 16px; }

/* Suppress hover effects that look odd on a TV */
body.tv-mode * { transition: none !important; }
body.tv-mode .card:hover { box-shadow: none; }

/* ── Hadouken ── */
@keyframes hadouken {
  0%   { transform: translateX(-80px) scale(0.5); opacity: 0; }
  8%   { opacity: 1; transform: translateX(-80px) scale(1); }
  85%  { opacity: 1; }
  100% { transform: translateX(calc(100vw + 120px)) scale(1.3); opacity: 0; }
}
.hadouken-ball {
  position: fixed;
  top: 50%;
  left: 0;
  width: 64px;
  height: 64px;
  margin-top: -32px;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 35%, #fff 0%, #80dfff 25%, #0088ff 55%, #0033cc 100%);
  box-shadow: 0 0 18px 8px rgba(0,150,255,.9), 0 0 55px 22px rgba(0,100,255,.45);
  z-index: 9999;
  pointer-events: none;
  animation: hadouken 0.85s cubic-bezier(.4,0,.6,1) forwards;
}

/* ── Chore Chart ─────────────────────────────────────────────── */
.chore-member-section {
  margin-bottom: 14px;
}
.chore-member-section:last-child { margin-bottom: 0; }

.chore-member-header {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 4px;
  flex-wrap: wrap;
}
.chore-member-name {
  font-weight: var(--weight-semi);
  font-size: .9rem;
}
.chore-pts-total {
  font-size: .75rem;
  color: var(--muted);
  flex: 1;
}
.chore-redeem-btn {
  font-size: .72rem;
  padding: 2px 8px;
  background: var(--accent);
  color: #fff;
  animation: choreRedeemPulse 1.2s ease-in-out infinite;
}
@keyframes choreRedeemPulse {
  0%, 100% { opacity: 1; }
  50%       { opacity: .7; }
}

.chore-progress-wrap {
  height: 5px;
  background: var(--surface2);
  border-radius: 3px;
  margin-bottom: 8px;
  overflow: hidden;
}
.chore-progress-fill {
  height: 100%;
  background: var(--accent);
  border-radius: 3px;
  transition: width .4s ease;
  min-width: 4px;
}

.chore-list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.chore-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 0;
  border-bottom: 1px solid rgba(128,128,128,.1);
}
.chore-item:last-child { border-bottom: none; }

.chore-check {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  border: 2px solid var(--muted);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: .7rem;
  cursor: pointer;
  flex-shrink: 0;
  color: #fff;
  transition: background .15s, border-color .15s;
}
.chore-check:hover { border-color: var(--accent); }
.chore-check.done  { background: var(--accent); border-color: var(--accent); }

.chore-text {
  flex: 1;
  font-size: .85rem;
}
.chore-item.chore-done .chore-text {
  text-decoration: line-through;
  color: var(--muted);
}

.chore-pts-badge {
  font-size: .7rem;
  font-weight: var(--weight-semi);
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  border-radius: 10px;
  padding: 1px 6px;
  flex-shrink: 0;
}
.chore-item.chore-done .chore-pts-badge { opacity: .4; }

.chore-del-btn {
  background: none;
  border: none;
  color: var(--muted);
  cursor: pointer;
  font-size: .8rem;
  padding: 2px 4px;
  opacity: 0;
  transition: opacity .15s;
  flex-shrink: 0;
}
.chore-item:hover .chore-del-btn { opacity: 1; }

.chore-add-row {
  display: flex;
  gap: 6px;
  align-items: center;
  padding: 8px 12px 10px;
  border-top: 1px solid rgba(128,128,128,.12);
  flex-wrap: wrap;
}
.chore-add-row input[type="text"] {
  flex: 1;
  min-width: 100px;
}
.chore-pts-input {
  width: 52px;
  flex-shrink: 0;
}
.chore-assignee-sel {
  flex: 1;
  min-width: 90px;
  font-size: .82rem;
  padding: 4px 6px;
  border-radius: var(--radius-sm);
  background: var(--surface2);
  border: 1px solid rgba(128,128,128,.2);
  color: var(--text);
  cursor: pointer;
}

@media (hover: none) {
  .chore-del-btn { opacity: 0.35; }
}

/* Chore card — layout when in bottom strip */
#bottomStrip #choreCard {
  display: grid;
  grid-template-rows: auto 1fr;
  grid-template-columns: 1fr auto;
}
#bottomStrip #choreCard .card-header {
  grid-column: 1 / -1;
  grid-row: 1;
}
#bottomStrip #choreCard .card-body {
  grid-column: 1;
  grid-row: 2;
  overflow: hidden;
}
#bottomStrip #choreCard .chore-add-row {
  grid-column: 2;
  grid-row: 2;
  flex-direction: column;
  align-items: stretch;
  border-top: none;
  border-left: 1px solid rgba(128,128,128,.12);
  padding: 10px 12px;
  width: 180px;
}
#bottomStrip #choreCard .chore-add-row input[type="text"] {
  flex: none;
  min-width: unset;
  width: 100%;
}
#bottomStrip #choreCard .chore-pts-input {
  width: 100%;
}
#bottomStrip #choreCard .chore-assignee-sel {
  width: 100%;
}
#bottomStrip #choreCard .chore-add-row .btn {
  width: 100%;
}

/* Chore card — horizontal per-person layout when in bottom strip */
.chore-members-row {
  display: flex;
  flex-direction: row;
  gap: 12px;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  padding-bottom: 4px;
}
.chore-members-row .chore-member-section {
  flex: 0 0 200px;
  min-width: 180px;
  margin-bottom: 0;
  background: var(--surface2);
  border-radius: var(--radius);
  padding: 10px 10px 8px;
}

/* ════════════════════════════════════════════════════════════════
   🎫 EVENT DISCOVERY — Event cards and modal styling
   ════════════════════════════════════════════════════════════════ */

.event-tab {
  background: var(--control-bg);
  border: 1px solid var(--control-border);
  box-shadow: var(--control-shadow);
  color: var(--text);
  padding: 6px 12px;
  border-radius: var(--tab-radius);
  font-size: var(--text-sm);
  cursor: pointer;
  transition: all 0.15s;
  white-space: nowrap;
}
html:not(.touch-input) .event-tab:hover:not(.active) { border-color: var(--accent); }
.event-tab.active {
  background: var(--control-active-bg);
  color: var(--control-active-ink);
  border-color: var(--control-active-border);
  box-shadow: var(--control-active-shadow);
}
.event-tab-count {
  opacity: 0.7;
  font-size: var(--text-xs);
}
.events-refresh-top { display: none; }

/* Category sub-tabs (within a source) — smaller, pill-style */
.events-sub-tabs {
  padding-bottom: 4px;
}
.event-subtab {
  background: none;
  border: 1px solid var(--border);
  color: var(--muted);
  padding: 3px 10px;
  border-radius: 999px;
  font-size: var(--text-xs);
  cursor: pointer;
  white-space: nowrap;
  transition: all 0.15s;
}
.event-subtab:hover {
  color: var(--text);
  border-color: var(--accent);
}
.event-subtab.active {
  background: var(--accent2);
  color: var(--bg);
  border-color: var(--accent2);
}

.events-category-filters {
  display: flex;
}

.event-card {
  background: var(--surface2);
  border-radius: var(--radius);
  overflow: hidden;
  transition: transform 0.1s, box-shadow 0.1s;
  display: flex;
  flex-direction: column;
  flex-shrink: 0;
}
.event-card:hover {
  transform: translateY(-2px);
  box-shadow: 0 4px 16px color-mix(in srgb, var(--accent) 20%, transparent);
}

.event-image {
  position: relative;
  width: 100%;
  height: 140px;
  background-size: cover;
  background-position: center;
  background-color: var(--surface);
}

.event-image-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(135deg, var(--surface) 0%, var(--surface2) 100%);
}

.event-image-emoji {
  font-size: 3rem;
}

/* Date + distance overlay, top-right of the image */
.event-overlay {
  position: absolute;
  top: 8px;
  right: 8px;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 8px;
  border-radius: 6px;
  background: rgba(0, 0, 0, 0.55);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  color: #fff;
  font-size: var(--text-xs);
  line-height: 1;
}

.event-overlay-date {
  font-weight: var(--weight-semi);
}

.event-overlay-distance {
  padding-left: 6px;
  border-left: 1px solid rgba(255, 255, 255, 0.35);
  opacity: 0.9;
}

.event-card-content {
  padding: 12px;
  flex: 1;
  display: flex;
  flex-direction: column;
}

.event-card-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 8px;
}

.event-title {
  margin: 0 0 6px 0;
  font-size: var(--text-md);
  font-weight: var(--weight-semi);
  color: var(--text);
  line-height: 1.3;
  word-break: break-word;
}

.event-type-badge {
  display: inline-block;
  background: var(--accent2);
  color: var(--bg);
  font-size: var(--text-xs);
  font-weight: var(--weight-semi);
  padding: 2px 6px;
  border-radius: 3px;
  margin-left: 6px;
}

.event-description {
  font-size: var(--text-sm);
  color: var(--muted);
  margin: 6px 0 10px 0;
  line-height: 1.4;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  text-overflow: ellipsis;
}

.event-link {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  white-space: nowrap;
  padding: 5px 9px;
  background: var(--accent);
  color: var(--bg);
  text-decoration: none;
  border-radius: 4px;
  font-size: var(--text-xs);
  font-weight: var(--weight-semi);
  transition: opacity 0.1s;
}

.event-link:hover {
  opacity: 0.85;
}

.event-link-icon {
  flex-shrink: 0;
}

/* ── Compact event card (Things to Do, Library — no image) ── */
.event-card-compact .event-card-content {
  padding: 10px 12px;
}
.event-card-compact .event-title {
  margin: 0;
  font-size: var(--text-sm);
}
.event-card-compact .event-description {
  margin: 4px 0 0 0;
  font-size: var(--text-xs);
  -webkit-line-clamp: 2;
}
.event-compact-icon {
  flex-shrink: 0;
  font-size: 1.4rem;
  line-height: 1;
}
.event-compact-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-top: 8px;
}
.event-compact-meta {
  font-size: var(--text-xs);
  font-weight: var(--weight-semi);
  color: var(--muted);
}
.event-link-sm {
  padding: 3px 8px;
}
/* Image cards share the compact footer; on desktop the image overlay already
   shows date and distance, so the footer is just the actions, right-aligned. */
.event-img-meta { display: none; }
.event-card-img .event-card-actions { margin-left: auto; }

.event-card-actions {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}

.event-add-btn {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  white-space: nowrap;
  padding: 4px 9px;
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--accent);
  border-radius: 4px;
  font-size: var(--text-xs);
  font-weight: var(--weight-semi);
  cursor: pointer;
  transition: background 0.1s, color 0.1s;
}
.event-add-btn:hover {
  background: var(--accent);
  color: var(--bg);
}

/* ── Event Venue Details ── */
.event-venue {
  padding: 8px 0;
  border-top: 1px solid rgba(128, 128, 128, 0.1);
  margin: 8px 0;
  font-size: var(--text-sm);
  color: var(--text);
}

.venue-name {
  font-weight: var(--weight-semi);
  color: var(--text);
}

.venue-capacity {
  color: var(--muted);
  font-size: var(--text-xs);
  margin-left: 4px;
}

.venue-website {
  display: block;
  color: var(--accent);
  text-decoration: none;
  font-size: var(--text-sm);
  margin-top: 4px;
  transition: opacity 0.1s;
}

.venue-website:hover {
  opacity: 0.8;
}

.venue-accessibility {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 4px;
  font-size: var(--text-xs);
  color: var(--text);
}

.venue-accessibility span {
  display: inline-flex;
  align-items: center;
  gap: 3px;
}

.event-description {
  font-size: var(--text-sm);
  color: var(--text);
  margin: 10px 0;
  line-height: var(--leading-normal);
  max-height: 80px;
  overflow: hidden;
  text-overflow: ellipsis;
}

.event-actions {
  margin-top: 10px;
  display: flex;
  gap: 6px;
}

.event-actions a {
  flex: 1;
  text-align: center;
  padding: 6px 10px;
  background: var(--accent);
  color: var(--bg);
  text-decoration: none;
  border-radius: 6px;
  font-size: var(--text-sm);
  font-weight: var(--weight-semi);
  transition: opacity 0.1s;
}
.event-actions a:hover {
  opacity: 0.85;
}

.events-search-bar {
  border: 1px solid rgba(128,128,128,0.2);
  padding: 8px;
  border-radius: var(--radius);
  background: var(--surface);
}

/* Calendar header event discovery button */
.cal-event-discover-btn {
  background: none;
  border: none;
  font-size: 1.2rem;
  cursor: pointer;
  opacity: 0.7;
  transition: opacity 0.2s, transform 0.2s;
  padding: 4px 6px;
}
.cal-event-discover-btn:hover {
  opacity: 1;
  transform: scale(1.1);
}

/* Calendar overlay event indicators */
.event-overlay-indicator {
  display: inline-block;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  font-size: 12px;
  line-height: 24px;
  text-align: center;
  margin: 2px;
  background: var(--accent);
  color: var(--text);
  transition: transform 0.1s, box-shadow 0.1s;
}
.event-overlay-indicator:hover {
  transform: scale(1.2);
  box-shadow: 0 2px 8px color-mix(in srgb, var(--accent) 30%, transparent);
}

/* ════════════════════════════════════════════════════════════════
   ✦  MATERIAL — the hub's lush / hand-made look (once the "Tactile"
   flavor, now the only one). Color always comes from the active theme;
   these rules set the material: surface, type, shadow, radius, grain.
   ════════════════════════════════════════════════════════════════ */
/* ── Display type for headings + the app title ── */
body h1,
body h2,
body .cal-app-title {
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-weight: var(--display-weight);
  letter-spacing: -0.01em;
}
/* Card titles are small labels, and the display serif read as heavy and
   blocky at that size, so they use the theme's body sans instead. */
body .card-header h2 {
  font-family: var(--font-body);
  font-weight: var(--weight-semi);
  letter-spacing: 0;
}

/* ── Icons (Lucide sprite at the top of family-hub.html, icon() in JS) ──
   Line icons in currentColor with a soft accent-tinted fill (duotone), so
   they pick up each theme's ink and accent. Used for chrome only (card
   titles, toolbar buttons, Settings nav, modal titles); content emoji such
   as Easter eggs, countdown icons and weather art stay emoji. */
.icon-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
.ic {
  width: 1.15em;
  height: 1.15em;
  flex-shrink: 0;
  vertical-align: -0.2em;
  stroke: currentColor;
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
  fill: color-mix(in srgb, var(--accent) 22%, transparent);
  pointer-events: none;
}
/* an icon followed by a label; flex rows with a gap don't need it */
h2 > .ic, #settingsModalGear, .briefing-pill > .ic, .ic-lead { margin-right: .4em; }
/* The ↺ buttons kept their glyph-sized font (and the wall display's larger
   one), so the icon scales off it rather than taking 1.15em of it */
.card-refresh-btn .ic,
.assistant-clear-btn .ic { width: .7em; height: .7em; }
/* Settings intro badge: the span was sized for an emoji */
.settings-intro > span { display: flex; color: var(--accent); font-size: 1.25rem; line-height: 1; }
#settingsModalGear .ic { vertical-align: -0.15em; }
.card-header h2 > .ic,
.settings-nav-item > .ic { margin-right: 0; }
.card-header h2 > .ic { color: var(--accent); }
.mobile-sheet-tab { display: inline-flex; align-items: center; justify-content: center; gap: 6px; }
.mobile-sheet-tab.active .ic { fill: color-mix(in srgb, #fff 25%, transparent); }
/* Icon-only buttons that relied on emoji color. An icon in currentColor
   would otherwise take the browser's default button ink. */
.cal-event-discover-btn,
.cal-header-settings-btn,
.mobile-sheet-action { color: var(--text); line-height: 1; }

/* ── Card material (all from the material tokens on body) ── */
body .card {
  background: var(--card-bg);
  border: 1px solid var(--card-border);
  box-shadow: var(--card-shadow);
  backdrop-filter: var(--card-blur);
  -webkit-backdrop-filter: var(--card-blur);
  position: relative;
}
/* per-card paper grain (opacity 0 in themes without grain) */
body .card::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  border-radius: inherit;
  background-image: var(--grain);
  background-size: 160px;
  opacity: var(--grain-opacity);
  mix-blend-mode: var(--grain-blend);
  z-index: 0;
}
/* keep card content above the grain layer */
body .card > * { position: relative; z-index: 1; }

/* the calendar card has its own surface/blur tokens: a background photo
   shows through it more, and Glass drops it entirely */
body .card.cal-card {
  background: var(--cal-card-bg);
  backdrop-filter: var(--cal-card-blur);
  -webkit-backdrop-filter: var(--cal-card-blur);
}

/* ── Buttons / inputs: a touch more crafted ── */
body .btn { border-radius: var(--control-radius); }
body .btn-primary {
  box-shadow: 0 4px 14px color-mix(in srgb, var(--accent) 30%, transparent);
}

/* ── Dense components & prominent numerals ── */
/* big numbers in the display face */
body .wx-temp,
body .budget-card-amt,
body .budget-total-amount,
body .budget-day-amt,
body .budget-day-total-amt,
body .countdown-days {
  font-family: var(--font-display);
  font-optical-sizing: auto;
  letter-spacing: -0.02em;
}
/* suggestion chips as soft accent-tinted pills */
body .assistant-chip,
body .meal-adjust-chip {
  border-radius: var(--chip-radius);
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  border-color: color-mix(in srgb, var(--accent) 28%, transparent);
}
.week-meal-item,
.school-day-subject-chip { border-radius: var(--chip-radius); }

/* ── KTD-4: accessibility modes keep the material *structure* (type, radius,
      surface depth) but drop decorative texture/glow that can impair
      color discrimination ── */
body.colorblind-mode,
body.safe-palette-on {
  --grain-opacity: 0;
  --card-bg: linear-gradient(180deg, var(--surface-hi) 0%, var(--surface-solid) 20%, var(--surface-solid) 80%, var(--surface-lo) 100%);
}

/* ════════════════════════════════════════════════════════════════
   ✦  THEMES — Tactile is the base above. A theme class on body sets the
   material tokens (and, rarely, a structural rule tokens can't express).
   Light/dark/accessible colors stay on the Colors axis: each theme gives
   values for dark (the default) and for body.light-mode. A theme may
   tune the palette for light/dark but leaves colorblind-mode's alone.
   Registry: THEMES in family-hub.js. Keep these blocks after the has-bg
   and KTD-4 token rules so a theme's surfaces win over them.
   ════════════════════════════════════════════════════════════════ */

/* ── Theme: Glass (clear slabs floating over a scene or the photo) ── */
body.theme-glass {
  --font-display: 'Inter Tight', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-body:    'Inter Tight', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --display-weight: var(--weight-semi);
  --radius: 26px;
  --card-bg: rgba(255,255,255,.07);
  --cal-card-bg: transparent;
  --card-border: rgba(255,255,255,.18);
  --card-shadow: inset 0 1px 0 rgba(255,255,255,.22), inset 0 -1px 0 rgba(255,255,255,.04), 0 18px 40px rgba(0,0,0,.35);
  --card-blur: blur(12px) saturate(180%);
  --cal-card-blur: none;
  --grain-opacity: 0;
  --control-radius: 999px;
  --tab-radius: 999px;
  --control-bg: rgba(255,255,255,.08);
  --control-border: rgba(255,255,255,.18);
  --control-shadow: none;
  --control-active-bg: var(--text);
  --control-active-ink: var(--bg);
  --control-active-border: var(--text);
  --control-active-shadow: none;
  --well-bg: rgba(255,255,255,.07);
  --well-shadow: none;
  --col-bg: rgba(255,255,255,.06);
  --col-border: rgba(255,255,255,.16);
  --col-radius: 22px;
  --col-gap: 10px;
  --col-blur: blur(10px) saturate(180%);
  --today-bg: rgba(255,255,255,.16);
  --week-bg: rgba(255,255,255,.10);
  --today-ring: 0 0 0 1px rgba(255,255,255,.55), inset 0 1px 0 rgba(255,255,255,.3);
  --event-radius: 16px;
  /* solid: the wall panel's Chromium draws no backdrop-filter, so an 80%
     "frosted" fill let the calendar's text show through sharp, and even
     95% left ghost text. The glass reads from the edge and shadow. */
  --modal-bg: var(--surface-solid);
  --modal-border: rgba(255,255,255,.18);
  --modal-shadow: inset 0 1px 0 rgba(255,255,255,.22), 0 24px 60px rgba(0,0,0,.45);
  --modal-scrim: rgba(5,10,20,.60);
  --modal-scrim-blur: blur(6px);
  /* the scene the glass sits on (a background photo replaces it) */
  background:
    radial-gradient(60% 70% at 20% 10%, #1d3b5a 0%, transparent 70%),
    radial-gradient(50% 60% at 90% 85%, #3a2340 0%, transparent 70%),
    linear-gradient(160deg, #0d1622, #070a10);
}
body.theme-glass.light-mode {
  --card-bg: rgba(255,255,255,.30);
  --card-border: rgba(255,255,255,.72);
  --card-shadow: inset 0 1px 1px rgba(255,255,255,.9), inset 0 -1px 1px rgba(255,255,255,.25), 0 16px 36px rgba(20,40,80,.16);
  --control-bg: rgba(255,255,255,.30);
  --control-border: rgba(255,255,255,.75);
  --well-bg: rgba(255,255,255,.28);
  --col-bg: rgba(255,255,255,.24);
  --col-border: rgba(255,255,255,.66);
  --today-bg: rgba(255,255,255,.58);
  --week-bg: rgba(255,255,255,.40);
  --today-ring: 0 0 0 1px rgba(255,255,255,.95), 0 8px 20px rgba(20,40,80,.12);
  --modal-bg: var(--surface-solid);
  --modal-border: rgba(255,255,255,.8);
  --modal-shadow: inset 0 1px 1px rgba(255,255,255,.9), 0 24px 60px rgba(20,40,80,.22);
  --modal-scrim: rgba(20,40,80,.35);
  --modal-scrim-blur: blur(8px);
  background:
    radial-gradient(55% 70% at 18% 12%, #ffd9a8 0%, transparent 70%),
    radial-gradient(50% 60% at 85% 20%, #8fd0e8 0%, transparent 70%),
    radial-gradient(60% 60% at 60% 100%, #7ea6d8 0%, transparent 70%),
    linear-gradient(160deg, #cfe3ee, #9fb9d6);
}
/* a background photo is the scene: let it through */
body.theme-glass.has-bg { background: var(--bg); }
/* light glass needs firmer text on a bright, busy scene */
body.theme-glass.light-mode { --text: #0e1a2b; --muted: #3e4a5c; }
body.theme-glass .card.cal-card { border-color: transparent; box-shadow: none; }
body.theme-glass .cal-grid { gap: 6px; }
body.theme-glass .cal-day { backdrop-filter: var(--col-blur); -webkit-backdrop-filter: var(--col-blur); }
body.theme-glass .card.cal-card::after { display: none; }
body.theme-glass .week-event,
body.theme-glass .day-event-pill,
body.theme-glass .agenda-event { border-radius: var(--event-radius); }

/* ── Theme: Paper (a flat printed planner page) ── */
body.theme-paper {
  --font-display: 'Newsreader', Georgia, 'Times New Roman', serif;
  --font-body:    'IBM Plex Sans', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --display-weight: var(--weight-medium);
  --radius: 4px;
  --card-bg: var(--surface-solid);
  --card-border: var(--line);
  --card-shadow: none;
  --card-blur: none;
  --cal-card-blur: none;
  --grain-opacity: 0;
  --control-radius: 3px;
  --tab-radius: 3px;
  --control-bg: transparent;
  --control-border: transparent;
  --control-shadow: none;
  --control-active-bg: transparent;
  --control-active-ink: var(--text);
  --control-active-border: transparent;
  --control-active-shadow: inset 0 -2px 0 var(--accent);
  --well-bg: transparent;
  --well-shadow: inset 0 0 0 1px var(--line);
  --col-bg: transparent;
  --col-border: transparent;
  --col-radius: 0;
  --col-gap: 0;
  --col-blur: none;
  --today-bg: color-mix(in srgb, var(--accent) 7%, var(--surface-solid));
  --week-bg: color-mix(in srgb, var(--accent) 3%, var(--surface-solid));
  --today-ring: inset 0 2px 0 var(--accent);
  --event-radius: 3px;
  --chip-radius: 3px;
  /* a sheet laid on the page: ruled edge, no lift */
  --modal-bg: var(--surface-solid);
  --modal-border: var(--line);
  --modal-shadow: none;
  --modal-blur: none;
  --modal-scrim: color-mix(in srgb, var(--bg) 75%, transparent);
  --modal-scrim-blur: none;
}
/* warm newsprint palette for light and dark (Accessible keeps its own) */
body.theme-paper:not(.colorblind-mode) {
  --bg: #171613;
  --surface-solid: #201f1b;
  --surface-hi: #24231f;
  --surface-lo: #1b1a17;
  --line: #3b382f;
  --text: #ece6d6;
  --muted: #a39d8c;
  --accent: #e8875f;
}
body.theme-paper.light-mode:not(.colorblind-mode) {
  --bg: #efece4;
  --surface-solid: #fffefa;
  --surface-hi: #fffefa;
  --surface-lo: #f6f2e8;
  --line: #d8d2c2;
  --text: #1d1b16;
  --muted: #6b6658;
  --accent: #a33a1e;
}
body.theme-paper { background: var(--bg); }
/* ruled day dividers instead of boxed columns/cells */
body.theme-paper .week-day-col + .week-day-col { border-left-color: var(--line); }
body.theme-paper .cal-grid { gap: 0; }
body.theme-paper .cal-day { border-width: 0 0 1px 1px; border-color: var(--line); }
body.theme-paper .week-day-header { border-bottom-color: var(--line); }
body.theme-paper .week-event,
body.theme-paper .day-event-pill,
body.theme-paper .agenda-event { border-radius: var(--event-radius); }
body.theme-paper .cal-month-nav button { font-family: var(--font-display); }

/* ── Screensaver ── */
/* Idle-triggered full-screen photo slideshow (see SCREENSAVER_BACKLOG.md S1).
   Sits above modals (9999) but below the transient easter-egg layers. */
#screensaver {
  position: fixed;
  inset: 0;
  z-index: 20000;
  background: #000;
  opacity: 0;
  pointer-events: none;
  transition: opacity 1.2s ease;
  overflow: hidden;
}
#screensaver.active {
  opacity: 1;
  pointer-events: auto;
  cursor: none;
}
.saver-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 1.5s ease;
  will-change: transform, opacity;
}
.saver-img.visible { opacity: 1; }
/* Ken Burns pan/zoom variants — also burn-in protection for the wall display.
   Duration is set inline by advanceSaverPhoto() (dwell + crossfade). */
.saver-img.kb0 { animation: saverKb0 linear both; }
.saver-img.kb1 { animation: saverKb1 linear both; }
.saver-img.kb2 { animation: saverKb2 linear both; }
.saver-img.kb3 { animation: saverKb3 linear both; }
@keyframes saverKb0 { from { transform: scale(1.02); }                       to { transform: scale(1.10) translate(-1.4%, -1%); } }
@keyframes saverKb1 { from { transform: scale(1.10) translate(1.4%, 1%); }   to { transform: scale(1.02); } }
@keyframes saverKb2 { from { transform: scale(1.05) translate(-1%, 1%); }    to { transform: scale(1.12) translate(1%, -1%); } }
@keyframes saverKb3 { from { transform: scale(1.12) translate(1%, -1.2%); }  to { transform: scale(1.04) translate(-1%, 1%); } }
#saverClock {
  position: absolute;
  left: 7%;
  bottom: 9%;
  color: rgba(255,255,255,.92);
  text-shadow: 0 2px 18px rgba(0,0,0,.65);
  transition: transform 2.5s ease;
  pointer-events: none;
  z-index: 2;
}
#saverClockTime {
  font-size: 5.5rem;
  font-weight: 600;
  line-height: 1;
  letter-spacing: -0.02em;
}
#saverClockDate {
  font-size: 1.3rem;
  margin-top: 8px;
  opacity: .8;
}
/* Ambient info line: temp + next calendar event */
#saverInfo {
  display: flex;
  gap: 18px;
  align-items: baseline;
  font-size: 1.2rem;
  margin-top: 14px;
  opacity: .75;
}
#saverInfoEvent {
  max-width: 60vw;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Pond mode is interactive, so keep a pointer — unlike the photo slideshow,
   which hides the cursor (#screensaver.active { cursor: none }). */
#screensaver.active.pond { cursor: default; }
/* Pond Chimes mode — the vendored pond (pond/index.html) in a same-origin
   iframe, created on start and destroyed on stop so its rAF loop and audio
   graph don't outlive the screensaver. */
#saverPond {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
}
/* Wake affordance for pond mode when taps play the pond instead of waking.
   Deliberately faint — it's for the family, not for the view. */
#saverWakeBtn {
  position: absolute;
  top: 18px;
  right: 18px;
  z-index: 3;
  padding: 10px 18px;
  border: 1px solid rgba(255,255,255,.28);
  border-radius: 999px;
  background: rgba(0,0,0,.35);
  color: rgba(255,255,255,.75);
  font: inherit;
  font-size: .9rem;
  letter-spacing: .04em;
  cursor: pointer;
  opacity: .35;
  transition: opacity .3s ease;
}
#saverWakeBtn:hover, #saverWakeBtn:focus-visible { opacity: 1; }
@media (hover: none) {
  #saverWakeBtn { padding: 14px 24px; opacity: .5; }
}

/* Night dimming: photos off, dim clock only */
#screensaver.night { background: #000; }
#screensaver.night .saver-img { display: none; }
#screensaver.night #saverClock { opacity: .35; }
#screensaver.night #saverPond,
#screensaver.night #saverWakeBtn { display: none; }
/* clock in the display serif */
body #saverClockTime { font-family: var(--font-display); }

/* ── Material motion — gated on prefers-reduced-motion. Entrance rides on
      the stable layout columns (never the per-render cards, so it doesn't
      replay on every refresh); hover lift skips the large calendar card ── */
@media (prefers-reduced-motion: no-preference) {
  body .cal-column,
  body .sidebar {
    animation: tactileSettle 0.55s cubic-bezier(.22,.61,.36,1) both;
  }
  body .sidebar { animation-delay: 0.06s; }
  @keyframes tactileSettle {
    from { opacity: 0; transform: translateY(12px); }
    to   { opacity: 1; transform: none; }
  }

  body .card:not(.cal-card) {
    transition: transform 0.35s cubic-bezier(.22,.61,.36,1), box-shadow 0.35s ease;
  }
  body .card:not(.cal-card):hover {
    transform: translateY(var(--card-lift, -2px));
    box-shadow:
      0 2px 3px rgba(0,0,0,.30),
      0 8px 16px rgba(0,0,0,.24),
      0 22px 44px rgba(0,0,0,.34),
      inset 0 1px 0 var(--surface-hi),
      inset 0 -1px 0 var(--surface-lo);
  }
  body.light-mode .card:not(.cal-card):hover {
    box-shadow:
      0 2px 3px rgba(70,55,30,.10),
      0 8px 16px rgba(70,55,30,.12),
      0 22px 40px rgba(70,55,30,.16),
      inset 0 1px 0 var(--surface-hi),
      inset 0 -1px 0 var(--surface-lo);
  }
  /* Glass and Paper keep their own shadow on hover; Paper doesn't lift */
  body.theme-glass .card:not(.cal-card):hover { box-shadow: var(--card-shadow); }
  body.theme-paper .card:not(.cal-card):hover { box-shadow: var(--card-shadow); transform: none; }
}

/* ── Mobile chrome (FABs + Lists sheet) — neutral tokens only,
      so it stays KTD-4-safe under colorblind-mode/safe-palette-on ── */
@media (max-width: 640px), (max-height: 500px) and (orientation: landscape) and (pointer: coarse) {
  body .mobile-sheet {
    background: linear-gradient(180deg, var(--surface-hi) 0%, var(--surface-solid) 40%);
    box-shadow: 0 -2px 10px rgba(0,0,0,.18);
  }
  body.light-mode .mobile-sheet {
    box-shadow: 0 -2px 10px rgba(70,55,30,.10);
  }
}

/* ── On-Screen Keyboard (keyboardless wall displays) ── */
/* Compact: a centered panel capped at 720px wide, with keys sized off the
   viewport height. Full-width 54px keys took over most of the wall panel. */
.osk {
  position: fixed;
  left: 50%; bottom: 0;
  transform: translateX(-50%);
  width: min(100%, 720px);
  z-index: 99999;
  display: none;
  flex-direction: column;
  gap: 4px;
  padding: 6px 6px calc(6px + env(safe-area-inset-bottom, 0px));
  background: var(--surface-solid, #1b1e27);
  border: 1px solid var(--line, rgba(128,128,128,.25));
  border-bottom: none;
  border-radius: 12px 12px 0 0;
  box-shadow: 0 -8px 30px rgba(0,0,0,.35);
  user-select: none;
  -webkit-user-select: none;
  touch-action: manipulation;
}
.osk.visible { display: flex; }
/* While the keyboard is up the page and any open modal end above it, the
   way the hub shrank for the Pi's own keyboard. --osk-h is measured in JS. */
html.osk-open body { height: calc(100dvh - var(--osk-h, 0px)); }
html.osk-open .modal-overlay { bottom: var(--osk-h, 0px); }
.osk-row { display: flex; gap: 4px; justify-content: center; }
.osk-key {
  flex: 1 1 0;
  min-width: 0;
  height: clamp(30px, 6vh, 44px);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1rem;
  font-weight: 600;
  color: var(--text);
  background: var(--surface-hi, rgba(255,255,255,.06));
  border: 1px solid var(--line, rgba(128,128,128,.25));
  border-radius: 8px;
  cursor: pointer;
  transition: background .08s, transform .05s;
}
.osk-key:active { transform: translateY(1px); background: var(--accent, #6c8ef5); color: #fff; }
.osk-key.osk-wide  { flex: 2.4 1 0; }
.osk-key.osk-space { flex: 5 1 0; }
.osk-key.osk-fn    { font-size: .85rem; color: var(--muted, #9aa0ac); }
.osk-key.osk-active { background: var(--accent, #6c8ef5); color: #fff; }
.osk-key.osk-enter { background: var(--accent, #6c8ef5); color: #fff; }
.osk-key.osk-hide  { flex: 0 0 48px; }

/* ── Motion switches (Settings → Look → Motion; family-hub.js § Motion) ── */
/* body.no-anim-<key> outranks each animation's own rule on specificity, so
   source order doesn't matter. Endless animations only — one-shot and
   while-busy ones (spinners, mic, assistant thinking) stay. */
body.no-anim-wxIcon .wx-icon,
body.no-anim-sunGlow .wx-sun-glow,
body.no-anim-moonGlow .wx-full-moon,
body.no-anim-wxTicker .wx-ticker-track,
body.no-anim-wxTicker .wx-snarky-track,
body.no-anim-asstTicker .assistant-ticker-track,
body.no-anim-statusPulse .status-fix,
body.no-anim-choreBadge .chore-redeem-btn { animation: none; }
/* Frosted glass off: blur is set on many selectors at many specificities, so a
   blanket !important is the one rule that reliably wins. Shadows stay (focus
   rings and badge rings use them, and they cost little). */
body.no-anim-glass *, body.no-anim-glass *::before, body.no-anim-glass *::after {
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
}
/* A stopped ticker would hide its tail, so let it scroll by hand instead. */
body.no-anim-wxTicker .wx-ticker,
body.no-anim-wxTicker .wx-snarky,
body.no-anim-asstTicker .assistant-ticker { overflow-x: auto; }

/* ── Wall display (Settings → Look → 🖼 Wall display; family-hub.js § Wall display) ── */
/* A wall panel is read from across the room. Zooming the whole page made
   event titles huge before chips and sidebar text were legible, so this
   flattens the scale instead: the root grows 25% (every literal rem size and
   rem spacing with it, as the zoom did) and the token ladder is re-set so the
   small steps gain the most. Values are px at the 20px root, then desktop. */
html.wall-display {
  font-size: 20px;
  --text-xs:   0.78rem;  /* 15.6 (11.2) chips, badges, meta */
  --text-sm:   0.82rem;  /* 16.4 (12.5) */
  --text-base: 0.86rem;  /* 17.2 (14.1) body, list rows */
  --text-md:   0.92rem;  /* 18.4 (16)   event titles, day numbers */
  --text-lg:   1.05rem;  /* 21   (18.4) */
}
/* Events lose a weight step: at this size semibold read as a black slab. */
html.wall-display :is(.week-event, .day-event-pill, .agenda-event, .cal-event) {
  font-weight: var(--weight-medium);
}
html.wall-display .week-event { padding: 6px 9px 6px 10px; }
html.wall-display .week-day-num { width: 32px; height: 32px; }
html.wall-display .school-day-subject-chip { padding: 2px 7px; border-radius: 10px; }
/* Wider sidebar for the larger text (300px fits desktop sizes). Desktop
   widths only, so it never fights the tablet/phone layouts. */
@media (min-width: 1200px) {
  /* 380px up to ~1730px wide, 422px at 1920, capped at 480px */
  html.wall-display .layout { --sidebar-w: clamp(380px, 22vw, 480px); }
}
/* Finger-sized scrollbars (Chromium's default is a few px wide). The :is()
   lifts these to tie the Nautical/Aurora `.sidebar::-webkit-scrollbar` rules,
   and coming later wins the tie. Scrollbars hidden with scrollbar-width: none
   stay hidden, since that property overrides ::-webkit-scrollbar. */
html.wall-display :is(*, .sidebar)::-webkit-scrollbar { width: 36px; height: 36px; }
/* The thumb is drawn 14px wide inside the 36px bar: still a full-width grab
   area, but it no longer reads as the heaviest thing on screen. */
html.wall-display :is(*, .sidebar)::-webkit-scrollbar-track { background: transparent; }
html.wall-display :is(*, .sidebar)::-webkit-scrollbar-thumb {
  background: color-mix(in srgb, var(--muted) 45%, transparent);
  border: 11px solid transparent; background-clip: padding-box;
  border-radius: 18px; min-height: 72px;
}
/* Assistant controls at finger size. The (hover: none) touch block already
   grows send/mic, but a panel with a mouse attached never matches it. */
html.wall-display .day-allday-add { width: 44px; height: 44px; font-size: 1.3rem; }
html.wall-display .assistant-send,
html.wall-display .assistant-mic { width: 52px; height: 52px; font-size: 1.4rem; }
html.wall-display .assistant-input { padding: 12px 16px; font-size: var(--text-base); }
html.wall-display .assistant-icon-btn { min-width: 48px; min-height: 48px; font-size: 1.7rem; }
html.wall-display .assistant-clear-btn { font-size: 2rem; }
/* The card headers' ↺ at the same finger size as the Assistant's ↺ above */
html.wall-display .card-refresh-btn {
  min-width: 48px; min-height: 48px; font-size: 2rem;
  display: inline-flex; align-items: center; justify-content: center;
}
html.wall-display .briefing-pill { padding: 10px 16px; font-size: var(--text-sm); }
/* Those bigger buttons don't fit beside the title in a narrow sidebar: the
   title shrank to nothing under the Briefing pill. Keep the title whole and
   let the buttons drop to their own row when they don't fit. */
html.wall-display #assistantCard .card-header { flex-wrap: wrap; row-gap: 6px; }
html.wall-display #assistantCard .card-header h2 { min-width: max-content; }
html.wall-display #assistantCard .card-header > div { margin-left: auto; }
html.wall-display .assistant-confirm-actions .btn { min-width: 88px; min-height: 48px; font-size: var(--text-base); }

/* ── Phone overrides (generic) ──
   Kept at the very end of the file on purpose. A media query adds no
   specificity, so an override at equal specificity only wins if it comes
   after the base rule it targets. These blocks used to sit near the top,
   ahead of most component rules, and several of their overrides (modal
   padding, the small-phone month label, the Month label's min-width) were
   silently dead. New phone tweaks that target a base rule go here, or
   anywhere after that base rule. */
/* Large mobile */
/* Mobile = narrower than 640px, or a phone turned sideways (short, landscape,
   touch). Every mobile block repeats this full query, and updateIsMobile()
   in family-hub.js mirrors it: keep all of them in sync. */
@media (max-width: 640px), (max-height: 500px) and (orientation: landscape) and (pointer: coarse) {
  /* The whole type scale below is sized for a wall panel read from a few
     feet away. On a phone held at arm's length that reads as tiny body
     copy next to one oversized date heading (#calMonthLabel). Scaling the
     tokens up here — rather than the many individual font-size rules that
     already reference them — keeps everything in one coherent scale. */
  :root {
    --text-xs:   0.80rem;
    --text-sm:   0.88rem;
    --text-base: 1.00rem;
    --text-md:   1.10rem;
    --text-lg:   1.30rem;
  }

  /* Edge-to-edge: the outer gutter and the card's border/radius/shadow are
     desktop prettification that just cramp a phone screen. Safe-area insets
     stay — that's hardware (notch/home indicator), not decoration. */
  body {
    padding: env(safe-area-inset-top, 0px) env(safe-area-inset-right, 0px)
             env(safe-area-inset-bottom, 0px) env(safe-area-inset-left, 0px);
  }
  /* !important: the material/theme overrides further down
     target .card.cal-card with more classes in the selector, which outranks
     a plain .cal-card rule regardless of source order — this has to win
     over all of them, in every theme, so it doesn't chase each one. */
  .cal-card {
    border: none !important;
    border-radius: 0 !important;
    box-shadow: none !important;
  }
  .cal-app-title { font-size: var(--text-base); }
  .layout { gap: 10px; }

  .cal-view-tab { padding: 4px 10px; font-size: var(--text-sm); }
  #calMonthLabel { min-width: 0; }

  /* Month grid: shorter cells */
  .cal-day { min-height: 54px; font-size: var(--text-xs); }
  .cal-day-num { font-size: var(--text-xs); }
  .cal-event { font-size: var(--text-xs); }

  /* Week view: the days stack as full-width rows instead of seven columns
     scrolling sideways, so a sideways swipe always means "next week" like
     every other view. Each row keeps the desktop band order (header, all-day,
     timed, to-dos + chores, bills, meals), just top to bottom in DOM order:
     the subgrid row placement is switched off. The grid itself scrolls
     vertically; overflow-x stays hidden so calSwipeScroller() ignores it. */
  .week-grid {
    display: flex; flex-direction: column; gap: 6px;
    overflow-y: auto; overflow-x: hidden;
    overscroll-behavior-y: contain;
    padding-bottom: 72px;   /* last day scrolls clear of #mobileAddFab */
  }
  .week-day-col { display: flex; flex-direction: column; flex-shrink: 0; }
  .week-day-col > * { grid-row: auto; }
  .week-day-header {
    display: flex; align-items: center; flex-wrap: wrap; gap: 4px 8px;
    text-align: left; padding: 6px 8px;
  }
  .week-day-num { margin: 0; }
  .school-day-badge, .school-day-subjects { margin-top: 0; }
  .week-day-body { overflow: visible; padding: 4px 6px; }
  .week-day-allday { padding: 4px 6px 0; }
  .week-tasks-strip { padding: 4px 6px 5px; }
  .week-empty { margin-top: 0; text-align: left; }
  .week-event-time { display: inline; margin-right: 6px; }
  .week-meal-item, .week-add-meal-btn { min-height: 0; padding: 6px 8px; font-size: var(--text-sm); }

  /* Day view: narrower time label */
  .day-hour-row { grid-template-columns: 44px 1fr; }
  .day-time-label { width: 44px; font-size: var(--text-xs); }
  .day-hour-label { font-size: var(--text-xs); padding-right: 6px; }
  /* #mobileAddFab already opens the editor on the viewed day */
  .day-allday-add { display: none; }

  /* Year strip: 3 columns */
  .mini-grid { grid-template-columns: repeat(3, 1fr); padding: 10px; gap: 8px; }

  /* Sidebar: stack single column, full-width cards */
  .sidebar { flex-direction: column; align-items: stretch; }
  .sidebar > .card { flex: none; width: 100%; box-sizing: border-box; }

  /* Weather forecast rows */
  .wx-day { font-size: var(--text-sm); gap: 5px; }

  /* Settings modal: full width */
  .modal-overlay { padding: 10px; padding-bottom: max(10px, env(safe-area-inset-bottom, 0px)); }
  .modal { padding: 20px 16px; }
}

/* Small mobile */
@media (max-width: 400px) {
  .cal-view-tab { padding: 3px 7px; font-size: var(--text-xs); }
  .cal-day { min-height: 44px; }
  .mini-grid { grid-template-columns: repeat(2, 1fr); }
  #calMonthLabel { font-size: 1rem; }
}

/* Glass on a phone: its 22px column radius turns narrow Month cells into pills */
@media (max-width: 640px), (max-height: 500px) and (orientation: landscape) and (pointer: coarse) {
  body.theme-glass { --col-radius: 12px; }
}
