/* ============================================================
   Ospaces — "Elevate"   (loaded after refine.css, before rtl.css)
   ------------------------------------------------------------
   WHY THIS EXISTS
   redesign.css set the identity and refine.css did the craft pass.
   This layer is the USABILITY pass: every rule below answers a defect
   found by putting each screen up at desktop, phone and dark, and by
   an accessibility read of the markup. Nothing here is decoration.

    1 · RAIL TEXT TOKENS. Muted text was defined once, for paper. On
        the dark rail the same token measured 4.15:1 (section titles)
        and, on the tab strip, 3.14:1 — below the 4.5:1 a label needs.
   1a · TOKEN ALIASES. Feature sheets used ~12 colour names that were
        never defined, so they fell back to hard-coded light values:
        white dialogs in dark mode.
   1b · SELECTS. No arrow in light mode, a tiled one in dark.
   1c · BOOT SCREEN. The app opened on an empty dark window.
    2 · TOAST. `left: 50%` + shrink-to-fit caps a fixed box at HALF the
        viewport, so on a phone a one-line message wrapped into an
        eight-line column. It was also only slid 80px down to "hide",
        so anything taller stayed on screen (and its button stayed in
        the Tab order) forever.
    3 · CONFIRM DIALOG. Styles for js/confirm.js, which replaces the
        browser's own confirm()/prompt() boxes on destructive actions.
    4 · EMPTY / STATUS STATE. One component (`.es`) for "this isn't
        here" screens, in place of a bare grey sentence.
    5 · SIDEBAR FOOTER. It was 174px — a fifth of a laptop screen —
        which left room for ONE row of the user's own pages above it.
    6 · HOVER-ONLY CONTROLS. The section "+" buttons and row actions
        were invisible without a mouse: never on touch, never on focus.
    7 · TAB STRIP. Readable labels, a visible focus ring, and off on
        phones — where it sat underneath the menu button.
    8 · VIEWS. Small consistency fixes: one "today" style across the
        calendars, calmer unread rows in the Inbox.
    9 · ONE SCRIM. Thirty-odd overlays each dimmed the app their own
        way — twelve different colours, fourteen of them blurred — so
        the same app looked like a different product per dialog.
   10 · PHONES. The overrides that must come last (see CLAUDE.md §7).

   House rules kept: no glass, no new fonts, tokens only, nothing in
   the DOM contract (IDs / classes the JS and tests rely on) changes.
   Delete this one <link> and the app reverts.
   ============================================================ */

/* ============================================================
   1 · Rail text tokens — muted text that is legible on the dark frame
   ============================================================ */
:root {
  --rail-text:   #f1eee5;                    /* 15.6:1 on the frame */
  --rail-text-2: #bcb7a7;                    /*  8.2:1 */
  --rail-text-3: #9a927f;                    /*  5.3:1 (was #877f6d — 4.15:1) */
  --rail-hover:  rgba(255, 250, 238, 0.07);
  --rail-line:   rgba(255, 249, 235, 0.10);
  /* a fill dark enough to carry white button text at 4.5:1 */
  --danger-strong: color-mix(in srgb, var(--danger) 86%, #000);
  /* THE dim behind anything modal (refine.css's values for #modal-layer) */
  --scrim: rgba(24, 19, 10, 0.52);
}
html[data-theme="dark"] { --scrim: rgba(6, 5, 9, 0.66); }
#sidebar { --text-faint: var(--rail-text-3); }

/* ============================================================
   1a · Token aliases — names features use that were never defined
   ------------------------------------------------------------
   Several feature sheets were written against a different token
   vocabulary (`--surface`, `--muted`, `--hover`, `--brand`…). None of
   those names exist here, so every use silently took its hard-coded
   LIGHT fallback: in dark mode the voice-capture dialog and the
   database row page were white panels with light text on them,
   hovers were a black tint on a black surface, and "brand" colour
   ignored the accent the user picked. Defining the names once, as
   aliases of the real tokens, repairs every such surface at a
   stroke and keeps them tracking the theme. (Declared on #sidebar
   too, so inside the rail they resolve against ITS overrides.)
   tests/elevate_test.mjs fails on any new undefined name.
   ============================================================ */
:root, #sidebar {
  --surface:        var(--bg-modal);
  --surface-2:      var(--bg-code);
  --muted:          var(--text-light);
  --faint:          var(--text-faint);
  --text-secondary: var(--text-light);
  --bg-secondary:   var(--bg-hover);
  --bg-soft:        var(--bg-hover);
  --hover:          var(--bg-hover);
  --font-body:      var(--font);
  --brand:          var(--accent);
  --hl:             color-mix(in srgb, var(--accent) 18%, transparent);
  --danger-bg:      color-mix(in srgb, var(--danger) 11%, transparent);
  --danger-ghost:   color-mix(in srgb, var(--danger) 12%, transparent);
}

/* ============================================================
   1b · Selects — one arrow, drawn once, in both themes
   ------------------------------------------------------------
   refine.css removes the platform arrow and draws a chevron as a
   background-image on `select`. But most selects carry a class whose
   `background:` SHORTHAND outranks a bare `select`, which wiped the
   image, its position and its no-repeat: in light mode those selects
   had no arrow at all, and in dark mode — where a more specific rule
   put only the image back — the chevron TILED across the control.
   Longhands at a specificity no component rule reaches fix both, and
   leave each select's own background COLOUR alone.
   ============================================================ */
:root select:not([multiple]):not([size]) {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%236b6558' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 9px center;
  background-size: 15px;
  padding-right: 30px;
}
html[data-theme="dark"] select:not([multiple]):not([size]) {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23a8a396' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
}
html[dir="rtl"] select:not([multiple]):not([size]) {
  background-position: left 9px center; padding-right: 11px; padding-left: 30px;
}

/* ============================================================
   1c · Boot screen — never an empty window
   ------------------------------------------------------------
   #login-screen and #app both start hidden and stay that way until
   the auth client answers "signed in or not", after ~150 modules have
   loaded. Until then the window was a blank dark rectangle that read
   as a crash. This shows the mark and a quiet progress line instead.
   It is pure CSS (the CSP allows no inline script, and the point is
   to be on screen BEFORE the script runs): the sibling selectors
   below remove it as soon as a real surface appears. A 200ms delay on
   the fade-in means a fast, cached start never flashes it.
   ============================================================ */
#boot-splash {
  position: fixed; inset: 0; z-index: 1;
  display: grid; place-items: center;
  background: var(--frame);
}
#login-screen:not(.hidden) ~ #boot-splash,
#app:not(.hidden) ~ #boot-splash,
#public-view:not(.hidden) ~ #boot-splash { display: none; }
.boot-inner {
  display: flex; flex-direction: column; align-items: center; gap: 24px;
  animation: boot-in 0.4s ease-out 0.2s both;
}
.boot-logo { width: 54px; height: 54px; margin: 0; border-radius: 16px; }
.boot-bar {
  width: 128px; height: 3px; border-radius: 3px; overflow: hidden;
  background: var(--rail-line);
}
.boot-bar i {
  display: block; width: 38%; height: 100%; border-radius: inherit;
  background: var(--accent-soft);
  animation: boot-slide 1.25s cubic-bezier(0.45, 0.05, 0.3, 0.95) infinite;
}
@keyframes boot-in { from { opacity: 0; transform: translateY(4px); } }
@keyframes boot-slide { from { transform: translateX(-110%); } to { transform: translateX(275%); } }
@media (prefers-reduced-motion: reduce) {
  /* style.css cuts every animation to one near-instant run, which would park
     the sliding segment off the end of its track: show a still line instead */
  .boot-bar i { width: 100%; opacity: 0.55; animation: none !important; }
}

/* ============================================================
   2 · Toast — sized by the viewport, fully hidden when hidden
   ============================================================ */
#toast {
  /* centred by auto margins between two insets, so its width can use the
     whole screen; `left: 50%` limited it to half of it */
  left: 16px; right: 16px; bottom: 24px;
  width: fit-content; max-width: min(680px, calc(100vw - 32px));
  margin-inline: auto;
  padding: 10px 12px 10px 16px; gap: 10px;
  line-height: 1.4; text-align: start;
  /* hidden means hidden: not painted, not focusable, not announced twice */
  transform: translateY(10px); opacity: 0; visibility: hidden;
  transition: transform var(--dur-3) var(--ease-out), opacity var(--dur-2) linear,
              visibility 0s linear var(--dur-3);
}
#toast.show {
  transform: none; opacity: 1; visibility: visible;
  transition-delay: 0s;
}
#toast:not(.has-action) { padding-inline: 16px; }
.toast-msg { min-width: 0; overflow-wrap: anywhere; }
.toast-action {
  min-height: 28px; padding: 3px 9px; margin: 0;
  border-radius: 7px; white-space: nowrap;
  background: rgba(255, 255, 255, 0.10);
  transition: background var(--dur-1);
}
.toast-action:hover { background: rgba(255, 255, 255, 0.18); }
.toast-close {
  flex: 0 0 auto; width: 26px; height: 26px; margin-inline-start: -2px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 7px; color: rgba(238, 240, 251, 0.72);
  transition: background var(--dur-1), color var(--dur-1);
}
.toast-close:hover { background: rgba(255, 255, 255, 0.12); color: #fff; }
#toast :focus-visible { outline: 2px solid #fff; outline-offset: 1px; }

/* ============================================================
   3 · Confirm / prompt dialog (js/confirm.js)
   ------------------------------------------------------------
   Its own layer, above the shared modal (200/250) because several
   callers ask from inside one.
   ============================================================ */
/* The voice button floated at z-index 1200 (ai.js) — above the scrim of
   every dialog, so it stayed lit over all of them and, on a phone, sat on a
   sheet's bottom buttons. At rest it now lives just under the popover layer
   (100) and just above the shared-page view (95, PUBLIC_VIEW_CSS in app.js):
   over the app, under anything modal. A dock that is listening, thinking or
   speaking keeps its old height — it is then a live control the user must be
   able to reach to stop it. */
body .vdock { z-index: 96; }
body .vdock[data-state="listening"],
body .vdock[data-state="thinking"],
body .vdock[data-state="speaking"] { z-index: 1200; }
.cf-layer {
  /* Above EVERY overlay the app defines. Feature layers climb as high as 1600
     (the type editor is 1500, row-page popovers 1600) and the question is
     asked from inside them — at 1300 "Delete this type?" opened BEHIND the
     editor that asked it. tests/elevate_test.mjs fails if a layer overtakes
     this one. (Only the Turnstile widget on public sites sits higher.) */
  position: fixed; inset: 0; z-index: 5000;
  display: grid; place-items: center; padding: 20px;
  background: var(--scrim);
  animation: cf-fade var(--dur-2) linear;
}
.cf-dialog {
  width: min(430px, 100%);
  display: grid; grid-template-columns: auto minmax(0, 1fr); column-gap: 14px; row-gap: 20px;
  padding: 22px 22px 18px; border-radius: 16px;
  color: var(--text); background: var(--bg-modal);
  box-shadow: var(--shadow-popover), var(--pane-ring);
  animation: cf-rise var(--dur-3) var(--ease-emph);
}
@keyframes cf-fade { from { opacity: 0; } }
@keyframes cf-rise { from { opacity: 0; transform: translateY(8px) scale(0.985); } }
.cf-badge {
  width: 38px; height: 38px; border-radius: 11px;
  display: grid; place-items: center;
  color: var(--accent); background: var(--accent-tint);
}
.cf-dialog.is-danger .cf-badge {
  color: var(--danger); background: color-mix(in srgb, var(--danger) 13%, transparent);
}
.cf-body { min-width: 0; align-self: center; }
.cf-title {
  font-family: var(--font-display); font-size: 16.5px; font-weight: 600;
  line-height: 1.3; letter-spacing: -0.1px; overflow-wrap: anywhere;
}
.cf-msg { margin-top: 6px; font-size: 13.5px; line-height: 1.5; color: var(--text-light); }
.cf-input { width: 100%; margin-top: 12px; min-height: 38px; }
.cf-actions { grid-column: 1 / -1; display: flex; justify-content: flex-end; gap: 8px; }
.cf-btn {
  min-height: 36px; padding: 0 15px; border-radius: var(--radius-ctl);
  font-size: 13.5px; font-weight: 600; white-space: nowrap;
  color: var(--text); background: var(--bg);
  border: 1px solid var(--border-strong);
  transition: background var(--dur-1), border-color var(--dur-1), filter var(--dur-1);
}
.cf-btn:hover { background: var(--bg-hover); }
.cf-ok {
  color: var(--accent-foreground, #fff); background: var(--accent); border-color: var(--accent);
}
.cf-ok:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
.cf-dialog.is-danger .cf-ok { color: #fff; background: var(--danger-strong); border-color: var(--danger-strong); }
.cf-dialog.is-danger .cf-ok:hover { background: var(--danger-strong); filter: brightness(0.93); }
@media (max-width: 520px) {
  .cf-layer { align-items: end; padding: 12px; }
  .cf-actions { flex-direction: column-reverse; }
  .cf-btn { min-height: 44px; }
}

/* ============================================================
   4 · Empty / status state — say what happened, offer the way out
   ============================================================ */
.es {
  display: flex; flex-direction: column; align-items: center; text-align: center;
  gap: 6px; max-width: 440px; margin: 0 auto; padding: 84px 24px 64px;
}
.es-art {
  width: 56px; height: 56px; margin-bottom: 12px; border-radius: 16px;
  display: grid; place-items: center;
  color: var(--text-light); background: var(--bg-hover);
  box-shadow: inset 0 0 0 1px var(--border);
}
.es-title {
  font-family: var(--font-display); font-size: 18px; font-weight: 600;
  letter-spacing: -0.2px; color: var(--text);
}
.es-msg { font-size: 14px; line-height: 1.55; color: var(--text-light); }
.es-actions { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px; margin-top: 16px; }
.es-btn {
  min-height: 36px; padding: 0 16px; border-radius: var(--radius-ctl);
  font-size: 13.5px; font-weight: 600;
  color: var(--accent-foreground, #fff); background: var(--accent);
  transition: background var(--dur-1);
}
.es-btn:hover { background: var(--accent-hover); }

/* a full-page database that is still loading shows the SHAPE of a table
   rather than one grey line in an empty pane */
.db-root:not(.db-root-inline) > .db-loading { padding: 26px 6px; }
.db-root:not(.db-root-inline) > .db-loading::after {
  content: ""; display: block; height: 210px; margin-top: 18px;
  border-radius: var(--radius-card); border: 1px solid var(--border);
  background:
    linear-gradient(100deg, transparent 35%, var(--bg) 50%, transparent 65%) 0 0 / 220% 100%,
    repeating-linear-gradient(180deg, transparent 0 41px, var(--border) 41px 42px),
    var(--bg-hover);
  animation: es-shimmer 1.5s linear infinite;
}
@keyframes es-shimmer { from { background-position: 120% 0, 0 0, 0 0; } to { background-position: -100% 0, 0 0, 0 0; } }

/* ============================================================
   5 · Sidebar footer — a labelled dock, not three stacked rows
   ------------------------------------------------------------
   refine.css gave Settings a row of its own because three labels
   side by side truncated. Stacking the icon over its label fits all
   three in ONE row with their names intact, in every language, and
   hands ~40px of every screen back to the page tree.
   ============================================================ */
.sb-footer { padding-top: 6px; gap: 2px; }
.sb-footer-row { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 4px; margin: 2px 0 1px; }
.sb-footer-row #btn-settings { grid-column: auto; }
.sb-footer-row .sb-action {
  flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  padding: 7px 4px 6px; min-height: 0;
  font-size: 10.5px; font-weight: 500; line-height: 1.2; letter-spacing: 0.15px; text-align: center;
  color: var(--text-light);
}
.sb-footer-row .sb-action:hover { color: var(--text); }
.sb-footer-row .sb-action > span:not(.sb-action-icon) { max-width: 100%; }
.sb-footer-row .sb-action .sb-action-icon { width: 16px; height: 16px; }
.sb-user { padding-top: 7px; }

/* ============================================================
   6 · Controls that only existed under a mouse pointer
   ============================================================ */
.sb-section-add:focus-visible,
.sb-section-title:focus-within .sb-section-add { opacity: 1; }
#btn-collapse:focus-visible { opacity: 1; }
/* the row a keyboard user is on is marked like a hovered one */
#sidebar .tree-item:focus-visible { background: var(--bg-hover); color: var(--text); outline-offset: -2px; }
/* touch: there is no hover, so "show on hover" meant "never" */
@media (hover: none) {
  .sb-section-add { opacity: 0.75; }
  .tree-item .ti-actions { opacity: 0.6; }
  #btn-collapse { opacity: 0.9; }
  .inbox-del { opacity: 0.6; }
}

/* ============================================================
   7 · Tab strip — part of the frame, legible, keyboard-visible
   ============================================================ */
.tabs-bar {
  height: 38px; padding: 6px 8px 0; gap: 2px; align-items: flex-end;
  border-bottom: none;
}
.tabs-scroll { align-items: flex-end; align-self: stretch; }
.tab-item {
  height: 32px; padding: 0 8px 0 11px; gap: 7px;
  border: none; border-radius: 9px 9px 0 0;
  color: var(--rail-text-2);            /* was --text-light: paper ink on the dark frame, 3.14:1 */
  transition: background var(--dur-1), color var(--dur-1);
}
.tab-item:hover { background: var(--rail-hover); color: var(--rail-text); }
.tab-item.active, .tab-item.active:hover {
  color: var(--text); background: var(--bg);
  box-shadow: none; border: none;
}
.tab-item.active::after { display: none; }
.tab-item:focus-visible { outline: 2px solid var(--accent-soft); outline-offset: -2px; }
.tab-item.active:focus-visible { outline-color: var(--accent); }
.tab-close { width: 18px; height: 18px; border-radius: 5px; color: inherit; }
.tab-item:hover .tab-close, .tab-item.active .tab-close, .tab-item:focus-visible .tab-close { opacity: 0.6; }
.tab-item:not(.active) .tab-close:hover { background: var(--rail-hover); color: var(--rail-text); }
.tabs-new-btn {
  width: 28px; height: 28px; margin: 0 0 3px 2px; border-radius: 8px;
  color: var(--rail-text-2);
}
.tabs-new-btn:hover { background: var(--rail-hover); color: var(--rail-text); }
.tabs-new-btn:focus-visible { outline-color: var(--accent-soft); }

/* ============================================================
   8 · Views — one "today", calmer unread
   ============================================================ */
/* Breadcrumbs asked for an ellipsis but were `inline-flex`, and text-overflow
   does nothing on a flex container: a long title was simply sliced through a
   letter ("Getting Start|"). A crumb that is only text (a page or database
   title) becomes a plain block; crumbs that lay out an icon beside a label
   (Home, Inbox, My Tasks…) keep their flex row. Without :has() the rule is
   dropped and the old clipping stays — nothing breaks. */
.crumb:not(:has(> *)) { display: block; }
/* Daily Notes outlined today; the Home month and the Calendar FILL it.
   One convention: today is the filled day. */
.daily-cell.today, .daily-cell.today:hover {
  color: var(--accent-foreground, #fff); background: var(--accent);
  box-shadow: none; font-weight: 600;
}
.daily-cell.today .daily-dot { background: currentColor; }
.daily-cell { transition: background var(--dur-1), color var(--dur-1); }
.daily-cal-label { font-family: var(--font-display); letter-spacing: -0.1px; }

/* Inbox: a full-row tint on every unread item turned a busy inbox into a
   solid lilac block. Unread is carried by the dot and by weight; read rows
   step back instead. */
.inbox-row.unread,
html[data-theme="dark"] .inbox-row.unread { background: transparent; }
.inbox-row.unread:hover, .inbox-row.unread:focus-visible,
html[data-theme="dark"] .inbox-row.unread:hover,
html[data-theme="dark"] .inbox-row.unread:focus-visible { background: var(--bg-hover); }
.inbox-row.unread .inbox-line { color: var(--text); }
.inbox-row:not(.unread) .inbox-actor,
.inbox-row:not(.unread) .inbox-page { font-weight: 500; color: var(--text-light); }
.inbox-row:not(.unread) .inbox-ic { opacity: 0.7; }
.inbox-row:focus-visible { outline: var(--ring-w) solid var(--accent); outline-offset: -2px; }

/* ============================================================
   9 · One scrim
   ------------------------------------------------------------
   refine.css took the blur off #modal-layer ("the house dislikes
   glass") and gave it a warm dim, but every feature overlay kept its
   own: indigo, navy or black, 32–60% opaque, blurred 1.5–3px. They
   now share the token. Only colour and blur change — each overlay
   keeps its own layout and stacking.
   ============================================================ */
.link-modal-back, .act-modal-overlay, .agent-scrim, .auto-layer, .pb-modal-back,
.cal-ee-back, .dash-overlay, .rowpage-scrim, .dbperm-overlay, .dev-layer, .docx-layer,
.enc-overlay, .ent-scrim, .fc-overlay, .fcm-overlay, .fh-overlay, .history-overlay,
.mtg-overlay, .guide-scrim, .wsearch-overlay, .share-overlay, .sa-overlay, .st-scrim,
.tasks-qa-overlay, .team-overlay, .tplgal-overlay, .verify-overlay, .vsc-overlay,
.wc-overlay, #sb-scrim, .mnav-scrim, .pcap-layer {
  background: var(--scrim);
  -webkit-backdrop-filter: none; backdrop-filter: none;
}
/* five of them re-colour themselves for dark mode at a higher specificity;
   the token already carries the dark value, so match and override those too */
html[data-theme="dark"] .agent-scrim, html:not([data-theme="light"]) .agent-scrim,
:root:not([data-theme="light"]) .dbperm-overlay,
html[data-theme="dark"] .fh-overlay, html[data-theme="dark"] .st-scrim,
html[data-theme="dark"] .wc-overlay { background: var(--scrim); }

/* ============================================================
   10 · Phones
   ============================================================ */
@media (max-width: 760px) {
  /* The strip is the first thing in the pane, so the fixed menu button sat on
     top of the first tab while the topbar below kept an empty 56px reserved
     for it. A phone shows one page at a time; the strip comes off. */
  .tabs-bar { display: none; }

  /* clear the voice button, which lives in the bottom-right corner */
  #toast { bottom: calc(72px + env(safe-area-inset-bottom, 0px)); }

  /* §5 resets the footer tiles' min-height for desktop; refine.css's 44px
     touch target for them is declared earlier, so it has to be re-stated here */
  .sb-footer-row .sb-action { min-height: 48px; }

  .es { padding: 56px 20px 48px; }

  /* THE READING COLUMN. mobile.css gives the page 16px side padding here, but
     redesign.css re-declares `#page-body { padding: 0 56px }` later at the same
     specificity and won — so a 390px phone spent 112px on margins and wrapped
     its text in a 278px column. (Same failure refine.css §15 fixed for the
     topbar.) 20px keeps the block handle, which sits at -16px, on screen. */
  #page-body { padding: 0 20px 26vh; }

  /* A heading's collapse arrow lives 68px LEFT of the text — off-screen on a
     phone, so a collapsed heading could never be opened again. Here it sits at
     the end of the line, shown while the heading is collapsed or being edited;
     the line keeps a fixed gap for it so nothing reflows when it appears. */
  .b-h1, .b-h2, .b-h3 { padding-right: 30px; }
  .heading-tri { left: auto; right: 0; pointer-events: none; }
  .block[data-collapsed="1"] > .heading-tri,
  .block:focus-within > .heading-tri { opacity: 1; pointer-events: auto; }
  html[dir="rtl"] .b-h1, html[dir="rtl"] .b-h2, html[dir="rtl"] .b-h3 { padding-right: 2px; padding-left: 30px; }
  html[dir="rtl"] .heading-tri { right: auto; left: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .db-root:not(.db-root-inline) > .db-loading::after { animation: none; }
}

/* ============================================================
   11 · HOME, 2026-10 — the day as the organising object.
   ------------------------------------------------------------
   Home used to open on a five-row checklist and three equally
   tall cards, one of them usually empty. Now the first thing on
   the page is THIS WEEK: seven day tiles that say what each day
   holds, today filled. The checklist rests as a single line, the
   Tasks card can take a reminder directly, and the cards size to
   what they contain. Markup: js/hometoday.js, js/onboarding.js.
   ============================================================ */

/* ----- week ribbon ----- */
.wk-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  margin: 2px 2px 10px;
}
.wk-brief { font-size: 14px; color: var(--text-light); min-width: 0; }
.wk-open {
  flex: none; border: 0; background: none; cursor: pointer;
  font: inherit; font-size: 12.5px; font-weight: 500; color: var(--text-faint);
  padding: 4px 8px; border-radius: var(--radius-ctl);
  transition: color .12s var(--ease-out), background .12s var(--ease-out);
}
.wk-open:hover { color: var(--accent); background: var(--bg-hover); }

.wk {
  display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 8px;
  margin-bottom: 18px;
}
.wk-day {
  position: relative;
  display: flex; flex-direction: column; align-items: flex-start; gap: 1px;
  min-width: 0; min-height: 92px;
  padding: 11px 12px 10px;
  border: 1px solid var(--border);
  border-radius: 14px;
  background: var(--bg-modal);
  color: var(--text);
  font: inherit; text-align: start; cursor: pointer;
  transition: border-color .14s var(--ease-out), background .14s var(--ease-out), transform .14s var(--ease-out);
}
.wk-day:hover { border-color: var(--border-strong); background: var(--bg-hover); }
.wk-day:active { transform: scale(.985); }
.wk-dow { font-size: 12px; font-weight: 500; color: var(--text-faint); }
.wk-num {
  font-family: var(--font-display); font-size: 26px; font-weight: 600;
  line-height: 1.1; letter-spacing: -0.6px;
  font-variant-numeric: tabular-nums;
}
.wk-cap {
  margin-top: auto; padding-top: 6px;
  font-size: 11.5px; font-weight: 500; color: var(--text-light);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%;
  min-height: 1.2em;
}
.wk-marks { position: absolute; top: 14px; inset-inline-end: 12px; display: inline-flex; gap: 3px; }
.wk-m { width: 6px; height: 6px; border-radius: 50%; display: block; }
.wk-m-due  { background: var(--accent); }
.wk-m-note { background: var(--gold); }
.wk-m-row  { background: var(--text-faint); }
.wk-past:not(.wk-today) .wk-num,
.wk-past:not(.wk-today) .wk-dow { color: var(--text-faint); }
.wk-past:not(.wk-today) { background: transparent; }

/* today: the one filled tile on the page */
.wk-today,
.wk-today:hover {
  background: var(--accent); border-color: var(--accent); color: var(--accent-foreground);
  box-shadow: 0 10px 24px -12px color-mix(in srgb, var(--accent) 70%, transparent);
}
.wk-today .wk-dow, .wk-today .wk-cap { color: color-mix(in srgb, var(--accent-foreground) 82%, transparent); }
.wk-today .wk-m-due  { background: var(--accent-foreground); }
.wk-today .wk-m-note { background: var(--gold-soft); }
.wk-today .wk-m-row  { background: color-mix(in srgb, var(--accent-foreground) 60%, transparent); }

/* the single entrance on Home: the week settles in, once per visit */
@keyframes wk-rise { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
.wk-enter .wk-day { animation: wk-rise .36s var(--ease-out) both; }
.wk-enter .wk-day:nth-child(2) { animation-delay: .03s; }
.wk-enter .wk-day:nth-child(3) { animation-delay: .06s; }
.wk-enter .wk-day:nth-child(4) { animation-delay: .09s; }
.wk-enter .wk-day:nth-child(5) { animation-delay: .12s; }
.wk-enter .wk-day:nth-child(6) { animation-delay: .15s; }
.wk-enter .wk-day:nth-child(7) { animation-delay: .18s; }

/* ----- widgets: sized by content, not by a column count ----- */
.wg-bar { margin-bottom: 8px; }
.wg-grid { display: flex; flex-wrap: wrap; gap: 14px; align-items: stretch; }
.wg-card { flex: 1 1 250px; container-type: inline-size; }
.wg-card[data-widget="tasks"] { flex: 1.5 1 320px; }
.wg-card[data-widget="calendar"] { flex: 1 1 270px; max-width: 420px; }
.ht-mc-day { aspect-ratio: auto; height: 32px; }
.ht-empty, .wg-empty { justify-content: flex-start; flex: 0 0 auto; padding: 8px 6px; }
/* a card that ends up alone on its row lays its list out in columns */
@container (min-width: 560px) {
  .wg-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 1px 14px; }
}

/* quick add, at the foot of the Tasks card */
.ht-add {
  display: flex; align-items: center; gap: 6px;
  margin-top: auto; padding-top: 10px;
}
.ht-add .ht-add-in {
  flex: 1 1 auto; min-width: 0; height: 36px;
  padding: 0 12px;
  border: 1px solid var(--border); border-radius: var(--radius-ctl);
  background: var(--bg-input); color: var(--text);
  font: inherit; font-size: 13px;
}
.ht-add .ht-add-in::placeholder { color: var(--text-placeholder); }
.ht-add .ht-add-in:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-tint); }
.ht-add-go {
  flex: none; width: 36px; height: 36px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--border); border-radius: var(--radius-ctl);
  background: var(--bg-modal); color: var(--text-light); cursor: pointer;
  transition: color .12s var(--ease-out), border-color .12s var(--ease-out), background .12s var(--ease-out);
}
.ht-add-go:hover { color: var(--accent); border-color: var(--accent); background: var(--bg-hover); }

/* ----- getting started: one line at rest ----- */
.ob-card { padding: 12px 14px; margin-bottom: 18px; box-shadow: none; }
.ob-head { align-items: center; gap: 12px; }
.ob-head-main { flex: 1 1 auto; min-width: 0; }
.ob-title { font-size: 14.5px; }
.ob-sub { margin-top: 0; }
.ob-ring {
  flex: none; width: 40px; height: 40px; border-radius: 50%;
  display: grid; place-items: center;
  background:
    radial-gradient(closest-side, var(--bg-modal) 76%, transparent 78%),
    conic-gradient(var(--accent) calc(var(--ob-p) * 1%), var(--border-strong) 0);
}
.ob-ring-n { font-size: 11px; font-weight: 600; color: var(--text-light); font-variant-numeric: tabular-nums; }
.ob-next, .ob-toggle {
  flex: none; height: 34px; padding: 0 12px;
  border-radius: var(--radius-ctl); font: inherit; font-size: 13px; font-weight: 500; cursor: pointer;
  white-space: nowrap;
  transition: background .12s var(--ease-out), border-color .12s var(--ease-out), color .12s var(--ease-out);
}
.ob-next { border: 1px solid transparent; background: var(--bg-active); color: var(--accent); font-weight: 600; max-width: 260px; overflow: hidden; text-overflow: ellipsis; }
.ob-next:hover { border-color: var(--accent); }
.ob-toggle { border: 1px solid var(--border); background: transparent; color: var(--text-light); }
.ob-toggle:hover { background: var(--bg-hover); color: var(--text); }
.ob-card:not(.ob-open) .ob-bar { display: none; }
.ob-steps[hidden] { display: none !important; }
.ob-card.ob-open .ob-bar { margin: 12px 0 10px; }

@media (max-width: 760px) {
  /* the week scrolls sideways rather than squeezing seven tiles into 350px */
  .wk {
    grid-template-columns: repeat(7, 86px); overflow-x: auto; scrollbar-width: none;
    margin-inline: -20px; padding-inline: 20px; scroll-snap-type: x proximity;
  }
  .wk::-webkit-scrollbar { display: none; }
  .wk-day { scroll-snap-align: center; min-height: 88px; }
  .wk-brief { font-size: 13.5px; }
  .wg-card, .wg-card[data-widget="tasks"], .wg-card[data-widget="calendar"] { flex: 1 1 100%; max-width: none; }
  .ob-head { flex-wrap: wrap; }
  .ob-head-main { flex: 1 1 0; }
  .ob-next { order: 5; flex: 1 1 auto; max-width: none; }
  .ob-toggle { order: 6; }
}

@media (prefers-reduced-motion: reduce) {
  .wk-enter .wk-day { animation: none; }
  .wk-day { transition: none; }
}

/* Customize sits with "Open calendar" on the brief line (it had a row to itself) */
.wk-tools { flex: none; display: inline-flex; align-items: center; gap: 2px; }
.wk-open.wg-customize {
  display: inline-flex; align-items: center; gap: 6px;
  border: 0; min-height: 0; padding: 4px 8px; font-size: 12.5px;
}
.wk-open.wg-customize:hover { border: 0; }
.wk-open.wg-customize[aria-expanded="true"] { color: var(--accent); background: var(--bg-active); }

@media (max-width: 760px) {
  /* checklist line on a phone: progress + title + dismiss, then the two actions */
  .ob-head-main { flex: 1 1 calc(100% - 110px); }
  .ob-dismiss { order: 3; }
  .ob-next { order: 5; flex: 1 1 0; }
  .ob-toggle { order: 6; }
  .wk-head { align-items: center; }
  .wk-open[data-cal="week"] { display: none; }   /* the Calendar card below has the same link */
}

/* ============================================================
   12 · PHONE TAB BAR — the four places you go, under your thumb.
   ------------------------------------------------------------
   On a phone everything used to sit behind the menu button in the
   far top corner. Home, Search, New page and Inbox are now one tap
   away at the bottom; the menu button still opens the full rail.
   Markup + wiring: js/mobilenav.js (it clicks the rail's own
   buttons, so there is one implementation of each action). The
   centre "+" opens the capture sheet instead (§28).
   ============================================================ */
.mtab { display: none; }
@media (max-width: 760px) {
  .mtab {
    position: fixed; z-index: 90;
    left: 0; right: 0; bottom: 0;
    display: grid; grid-template-columns: repeat(5, 1fr); align-items: center;
    height: calc(58px + env(safe-area-inset-bottom, 0px));
    padding: 0 6px env(safe-area-inset-bottom, 0px);
    background: var(--bg-modal);
    border-top: 1px solid var(--border);
  }
  #app.hidden ~ .mtab, body.sb-open .mtab, body.focus-mode .mtab { display: none; }
  .mtab-btn {
    position: relative;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
    height: 52px; min-width: 0;
    border: 0; background: none; cursor: pointer;
    font: inherit; font-size: 10.5px; font-weight: 500; color: var(--text-faint);
    border-radius: 12px;
    -webkit-tap-highlight-color: transparent;
  }
  .mtab-btn svg { width: 22px; height: 22px; }
  .mtab-btn:active { background: var(--bg-hover); }
  .mtab-btn[aria-current="page"] { color: var(--accent); }
  .mtab-new { color: var(--accent-foreground); }
  .mtab-new .mtab-plus {
    width: 44px; height: 44px; border-radius: 14px;
    display: grid; place-items: center;
    background: var(--accent); color: var(--accent-foreground);
    box-shadow: 0 8px 18px -8px color-mix(in srgb, var(--accent) 80%, transparent);
  }
  .mtab-new:active { background: none; }
  .mtab-new:active .mtab-plus { transform: scale(.94); }
  .mtab-badge {
    position: absolute; top: 5px; left: calc(50% + 6px);
    min-width: 16px; height: 16px; padding: 0 4px; border-radius: 999px;
    background: var(--accent); color: var(--accent-foreground);
    font-size: 10px; font-weight: 600; line-height: 16px; text-align: center;
  }
  .mtab-badge:empty { display: none; }

  /* everything that floats or scrolls makes room for the bar */
  #page-scroll { padding-bottom: calc(58px + env(safe-area-inset-bottom, 0px)); }
  #toast { bottom: calc(72px + 58px + env(safe-area-inset-bottom, 0px)); }
  .vdock { bottom: calc(70px + env(safe-area-inset-bottom, 0px)) !important; }
}
@media print { .mtab { display: none !important; } }

/* ============================================================
   13 · DATABASE TABLE — quieter frame, clearer header.
   ------------------------------------------------------------
   The table ended in a grey bar that looked like a disabled row
   (it is the Calculate row, and only said so on hover — which a
   touch screen never does). The header now reads as a header,
   the Calculate row sits on paper and names itself where there is
   no hover, and on a phone the toolbar scrolls sideways instead
   of wrapping into two ragged rows.
   ============================================================ */
.db-root .db-table { border-radius: var(--radius-card); }
.db-root .db-thead { background: var(--bg-hover); }
.db-root .db-th { font-weight: 600; }
.db-root .db-summary { background: transparent; }
.db-root .db-summary .db-col-frozen { background: var(--bg); }
.db-root .db-new-btn, .db-root .db-new-caret { color: var(--accent-foreground); }
@media (hover: none) {
  .db-root .db-sum-cell.is-empty .db-sum-calc,
  .db-root .db-sum-cell.is-empty .db-sum-chev { opacity: .75; }
}
@media (max-width: 760px) {
  .db-root .db-toolbar {
    flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none;
    margin-inline: -16px; padding-inline: 16px;
  }
  .db-root .db-toolbar::-webkit-scrollbar { display: none; }
  .db-root .db-toolbar > * { flex: none; }
  .db-root .db-tool-spacer { flex: 1 0 8px; }
  /* "+ New" stays in reach while the tools scroll under it */
  .db-root .db-new-wrap {
    position: sticky; inset-inline-end: 0;
    box-shadow: -10px 0 12px -4px var(--bg);
  }
}

/* ============================================================
   14 · CALENDAR — room to work, and everything movable.
   ------------------------------------------------------------
   The calendar opened as a 960 × 585 box on any screen: its own
   height (760px) lost to `.modal { max-height: 65vh }`, so a month
   was six short rows and a week showed eight hours. It now takes
   the window. In the week and day views an event can be dragged
   to a new TIME (a ghost shows where it will land) and stretched
   from its bottom edge; js/calendar.js has the handlers.
   ============================================================ */
#modal-layer:has(> .cal-modal) { padding: 20px; align-items: stretch; justify-content: center; }
.modal.cal-modal {
  width: min(1480px, 100%); max-width: none;
  height: 100%; max-height: none;
}
.cal-modal .cal-grid { flex: 1; min-height: 0; }
.cal-modal .cal-cell { min-height: 84px; }
.cal-modal .cal-ev { font-size: 12.5px; }
.cal-modal .cal-tg-evt-t { font-size: 12.5px; }
.cal-modal .cal-tg-evt-time { font-size: 11px; }

/* something being dragged: where it will land */
.cal-tg-ghost {
  position: absolute; left: 2px; right: 2px; z-index: 4; pointer-events: none;
  border: 2px dashed var(--accent); border-radius: 6px;
  background: var(--accent-tint);
}
.cal-tg-ghost-t {
  position: absolute; top: 2px; inset-inline-start: 6px;
  font-size: 11px; font-weight: 600; color: var(--accent);
}
.cal-tg-col.cal-drop { background: var(--accent-tint); }

/* the bottom edge of an event: drag to change its length */
.cal-tg-evt-rs {
  position: absolute; left: 0; right: 0; bottom: 0; height: 8px;
  cursor: ns-resize; touch-action: none;
}
.cal-tg-evt-rs::after {
  content: ""; position: absolute; left: 50%; bottom: 2px; width: 18px; height: 3px;
  margin-left: -9px; border-radius: 2px; background: rgba(255, 255, 255, 0.75);
  opacity: 0; transition: opacity .12s var(--ease-out);
}
.cal-tg-evt:hover .cal-tg-evt-rs::after, .cal-tg-evt-resizing .cal-tg-evt-rs::after { opacity: 1; }
.cal-tg-evt-resizing { z-index: 5; box-shadow: 0 6px 16px -6px rgba(0, 0, 0, 0.45); }
@media (hover: none) { .cal-tg-evt-rs { height: 14px; } .cal-tg-evt-rs::after { opacity: 1; } }

/* Home's month card: full-size days again (§11 had squeezed them to 32px) */
.ht-mc-day { height: auto; aspect-ratio: 1 / 1; max-height: 46px; font-size: 13px; }
.wg-card[data-widget="calendar"] { flex: 1.2 1 300px; max-width: 460px; }

@media (max-width: 760px) {
  #modal-layer:has(> .cal-modal) { padding: 0; }
  .modal.cal-modal { border-radius: 0; }
  .wg-card[data-widget="calendar"] { max-width: none; }
}

/* ============================================================
   15 · CALENDAR CATEGORIES — your own calendars, each a colour.
   ------------------------------------------------------------
   A side panel like the one people already know: Create, a small
   month to jump around with, then "My calendars" — tick one to
   show or hide everything in it, the pencil to rename, recolour
   or delete it. Events take their calendar's colour everywhere.
   Markup + logic: js/calendar.js (SIDE PANEL, event editor).
   ============================================================ */
.cal-main { flex: 1; min-height: 0; display: flex; }
.cal-main > .cal-body { flex: 1 1 auto; min-width: 0; }
.cal-side {
  flex: none; width: 248px; overflow-y: auto;
  padding: 14px 14px 18px;
  border-inline-end: 1px solid var(--border);
  display: flex; flex-direction: column; gap: 18px;
}
.cal-modal.cal-side-closed .cal-side { display: none; }
.cal-side-toggle { margin-inline-end: 4px; }
/* the header's own "+ Event" repeats the panel's Create while the panel shows */
.cal-modal:not(.cal-side-closed) .cal-add-btn { display: none; }

.cal-create {
  align-self: flex-start;
  display: inline-flex; align-items: center; gap: 10px;
  height: 44px; padding: 0 20px 0 14px;
  border: 1px solid var(--border); border-radius: 14px;
  background: var(--bg-input); color: var(--text);
  font: inherit; font-size: 14px; font-weight: 600; cursor: pointer;
  box-shadow: var(--shadow-card);
  transition: border-color .12s var(--ease-out), background .12s var(--ease-out), transform .12s var(--ease-out);
}
.cal-create:hover { border-color: var(--accent); background: var(--bg-hover); }
.cal-create:active { transform: scale(.98); }
.cal-create-plus {
  width: 24px; height: 24px; border-radius: 8px; display: grid; place-items: center;
  background: var(--accent); color: var(--accent-foreground); font-size: 15px; line-height: 1;
}

/* small month */
.cal-mini-head { display: flex; align-items: center; gap: 2px; margin-bottom: 6px; }
.cal-mini-title { flex: 1; font-size: 13px; font-weight: 600; color: var(--text); padding-inline-start: 4px; }
.cal-mini-nav { width: 24px; height: 24px; }
.cal-mini-wd, .cal-mini-grid { display: grid; grid-template-columns: repeat(7, 1fr); }
.cal-mini-wd span { text-align: center; font-size: 10.5px; font-weight: 600; color: var(--text-faint); padding: 2px 0; }
.cal-mini-day {
  position: relative; aspect-ratio: 1 / 1;
  border: 0; background: none; border-radius: 50%; cursor: pointer;
  font: inherit; font-size: 11.5px; color: var(--text);
  font-variant-numeric: tabular-nums;
}
.cal-mini-day:hover { background: var(--bg-hover); }
.cal-mini-out { color: var(--text-faint); opacity: .55; }
.cal-mini-busy::after {
  content: ""; position: absolute; left: 50%; bottom: 2px; width: 3px; height: 3px; margin-left: -1.5px;
  border-radius: 50%; background: var(--accent);
}
.cal-mini-sel { background: var(--bg-active); color: var(--accent); font-weight: 600; }
.cal-mini-today, .cal-mini-today:hover { background: var(--accent); color: var(--accent-foreground); font-weight: 600; }
.cal-mini-today.cal-mini-busy::after { background: var(--accent-foreground); }

/* the lists */
.cal-side-h { font-size: 12.5px; font-weight: 600; color: var(--text-light); margin: 0 0 6px 4px; }
.cal-cat {
  display: flex; align-items: center; gap: 9px;
  min-height: 32px; padding: 0 4px; border-radius: var(--radius-ctl);
}
.cal-cat:hover { background: var(--bg-hover); }
.cal-cat .cal-cat-cb {
  appearance: none; -webkit-appearance: none; flex: none;
  width: 17px; height: 17px; margin: 0; padding: 0; cursor: pointer;
  border: 2px solid var(--catc); border-radius: 5px; background: transparent;
  display: grid; place-items: center;
  transition: background .1s var(--ease-out);
}
.cal-cat .cal-cat-cb:checked { background: var(--catc); }
.cal-cat .cal-cat-cb:checked::after {
  content: ""; width: 9px; height: 5px; margin-top: -2px;
  border-left: 2px solid #fff; border-bottom: 2px solid #fff; transform: rotate(-45deg);
}
.cal-cat-name {
  flex: 1; min-width: 0; cursor: pointer; font-size: 13.5px; color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; padding: 6px 0;
  user-select: none;
}
.cal-cat-off .cal-cat-name { color: var(--text-faint); }
.cal-cat-edit {
  flex: none; width: 26px; height: 26px; border: 0; border-radius: 7px; background: none;
  color: var(--text-faint); cursor: pointer; display: grid; place-items: center;
  opacity: 0; transition: opacity .12s var(--ease-out), background .12s var(--ease-out);
}
.cal-cat:hover .cal-cat-edit, .cal-cat-edit:focus-visible { opacity: 1; }
.cal-cat-edit:hover { background: var(--bg-active); color: var(--accent); }
@media (hover: none) { .cal-cat-edit { opacity: 1; } }
.cal-cat-add {
  margin-top: 4px; height: 32px; padding: 0 8px; border: 0; border-radius: var(--radius-ctl);
  background: none; color: var(--text-light); font: inherit; font-size: 13px; cursor: pointer; text-align: start;
}
.cal-cat-add:hover { background: var(--bg-hover); color: var(--accent); }

/* add / edit a calendar */
.cal-cat-form {
  display: flex; flex-direction: column; gap: 10px;
  margin: 4px 0; padding: 10px;
  border: 1px solid var(--border); border-radius: var(--radius-card); background: var(--bg-input);
}
.cal-cat-form .cal-cat-in {
  width: 100%; height: 34px; padding: 0 10px;
  border: 1px solid var(--border); border-radius: var(--radius-ctl);
  background: var(--bg); color: var(--text); font: inherit; font-size: 13.5px;
}
.cal-cat-form .cal-cat-in:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-tint); }
.cal-cat-sws { display: grid; grid-template-columns: repeat(6, 1fr); gap: 7px; }
.cal-cat-sw, .cal-cat-form .cal-cat-custom {
  width: 100%; aspect-ratio: 1 / 1; padding: 0; cursor: pointer;
  border: 2px solid transparent; border-radius: 50%; outline-offset: 2px;
}
.cal-cat-sw.sel, .cal-cat-form .cal-cat-custom.sel { box-shadow: 0 0 0 2px var(--bg-input), 0 0 0 4px var(--text); }
.cal-cat-form .cal-cat-custom { background: none; border: 1px dashed var(--border-strong); overflow: hidden; }
.cal-cat-custom::-webkit-color-swatch-wrapper { padding: 0; }
.cal-cat-custom::-webkit-color-swatch { border: 0; border-radius: 50%; }
.cal-cat-custom::-moz-color-swatch { border: 0; border-radius: 50%; }
.cal-cat-foot { display: flex; align-items: center; gap: 6px; }
.cal-cat-foot button {
  height: 30px; padding: 0 10px; border-radius: var(--radius-ctl);
  font: inherit; font-size: 12.5px; font-weight: 500; cursor: pointer;
  border: 1px solid var(--border); background: transparent; color: var(--text-light);
}
.cal-cat-foot button:hover { background: var(--bg-hover); color: var(--text); }
.cal-cat-foot .cal-cat-del { margin-inline-end: auto; color: var(--danger); border-color: transparent; padding: 0 6px; }
.cal-cat-foot .cal-cat-save { background: var(--accent); border-color: var(--accent); color: var(--accent-foreground); font-weight: 600; }
.cal-cat-foot .cal-cat-save:hover { background: var(--accent-hover); color: var(--accent-foreground); }

/* events wear their calendar's colour: a solid bar on the tinted chip */
.cal-modal .cal-ev-evt { box-shadow: inset 3px 0 0 var(--evc); padding-inline-start: 8px; }
.cal-modal .cal-ev-evt .cal-ev-dot { display: none; }

/* the event editor: which calendar */
.cal-ee-cats { display: flex; flex-wrap: wrap; gap: 6px; }
.cal-ee-cat {
  display: inline-flex; align-items: center; gap: 7px;
  height: 30px; padding: 0 11px; border-radius: 999px; cursor: pointer;
  border: 1px solid var(--border); background: transparent; color: var(--text-light);
  font: inherit; font-size: 12.5px; font-weight: 500;
  transition: border-color .12s var(--ease-out), background .12s var(--ease-out), color .12s var(--ease-out);
}
.cal-ee-cat:hover { background: var(--bg-hover); color: var(--text); }
.cal-ee-cat.sel { border-color: var(--text); color: var(--text); background: var(--bg-hover); }
.cal-ee-cat-dot { width: 10px; height: 10px; border-radius: 50%; background: var(--border-strong); flex: none; }

@media (max-width: 900px) {
  /* narrow: the panel slides over the grid instead of squeezing it */
  .cal-modal .cal-main { position: relative; }
  .cal-modal .cal-side {
    display: none; position: absolute; z-index: 6; inset-block: 0; inset-inline-start: 0;
    width: min(280px, 86%); background: var(--bg-modal);
    box-shadow: 14px 0 30px -18px rgba(0, 0, 0, 0.45);
  }
  .cal-modal.cal-side-open .cal-side { display: flex; }
  .cal-modal .cal-add-btn, .cal-modal:not(.cal-side-closed) .cal-add-btn { display: inline-block; }
  .cal-head { flex-wrap: wrap; row-gap: 8px; }
  .cal-title { min-width: 0; font-size: 16px; }
}

/* ============================================================
   16 · CALENDAR — repeating events, reminders in calendars,
        dragging with a finger, a wider palette.
   ============================================================ */
/* 24 presets + the free picker = 25: five rows of five, no orphan */
.cal-cat-sws { grid-template-columns: repeat(5, 1fr); gap: 8px; padding: 0 6px; }

/* event editor: repeat */
.cal-ee-repeat { flex-wrap: wrap; }
.cal-ee-repeat .cal-ee-rep {
  flex: 1 1 180px; min-width: 0; height: 36px; padding: 0 10px;
  border: 1px solid var(--border); border-radius: var(--radius-ctl);
  background: var(--bg-input); color: var(--text); font: inherit; font-size: 13.5px;
}
.cal-ee-until-wrap { display: inline-flex; align-items: center; gap: 8px; font-size: 13px; color: var(--text-light); }
.cal-ee-series {
  font-size: 12.5px; color: var(--text-light); line-height: 1.45;
  padding: 8px 10px; border-radius: var(--radius-ctl); background: var(--bg-hover);
}
.cal-ee-series[hidden] { display: none; }
.cal-ee-del-one { margin-inline-end: 6px; }

/* a repeating event says so */
.cal-ev-repeats .cal-ev-tx::after,
.cal-tg-evt.cal-ev-repeats .cal-tg-evt-time::after { content: " ↻"; opacity: .7; }

/* reminder card */
.cal-rem-card { gap: 12px; }
.cal-rem-text { font-size: 15px; font-weight: 600; color: var(--text); line-height: 1.4; overflow-wrap: anywhere; }

/* dragging with a finger: nothing selects, nothing pops a menu, the page holds still */
.cal-modal .cal-ev[draggable="true"], .cal-modal .cal-tg-evt {
  -webkit-user-select: none; user-select: none; -webkit-touch-callout: none;
}
.cal-modal.cal-touch-dragging .cal-ev-dragging { opacity: .55; outline: 2px solid var(--accent); outline-offset: 1px; }
.cal-modal.cal-touch-dragging .cal-tg-scroll,
.cal-modal.cal-touch-dragging .cal-body { overscroll-behavior: contain; }
.cal-modal .cal-cell.cal-drop, .cal-modal .cal-tg-allday-cell.cal-drop {
  background: var(--accent-tint); box-shadow: inset 0 0 0 2px var(--accent);
}

/* ============================================================
   17 · DATABASE OPTION COLOURS — tell "Cash" from "Card".
   ------------------------------------------------------------
   Option chips were nine pale tints with the same dark ink on
   every one: gray and blue differed by a few points of lightness,
   and a column of them read as one colour. A chip is now drawn
   from ONE solid colour (--oc, set by paintOption() in
   js/database.js — a palette name or the user's own #rrggbb): a
   tinted fill, ink of the same hue, a solid dot and a hairline.
   Three cues, so it holds for colour-blind readers and in both
   themes. The picker opens in place under the option's row.

   The selector is deliberately heavy: the old per-colour
   backgrounds are `html[data-theme="dark"] .db-root .bg-x`
   (0,3,1) and this must win over every one of them.
   ============================================================ */
html .db-chip.oc.oc.oc {
  background: color-mix(in srgb, var(--oc) 20%, var(--bg));
  color: color-mix(in srgb, var(--oc) 34%, var(--text));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--oc) 30%, transparent);
  font-weight: 500;
  gap: 6px; padding: 2px 9px 2px 7px;
}
html .db-chip.oc.oc.oc::before {
  content: ""; flex: none; width: 7px; height: 7px; border-radius: 50%;
  background: var(--oc);
}
/* timeline bars tinted by an option */
html .db-tl-bar.db-tl-bar-tinted.oc.oc {
  background: color-mix(in srgb, var(--oc) 30%, var(--bg));
  color: color-mix(in srgb, var(--oc) 35%, var(--text));
  box-shadow: inset 3px 0 0 var(--oc), inset 0 0 0 1px color-mix(in srgb, var(--oc) 35%, transparent);
}
/* the status-group colour button: a dot of the group's colour */
html .db-opt-color.db-opt-color-dot.oc.oc {
  background: none; position: relative;
}
html .db-opt-color.db-opt-color-dot.oc.oc::after {
  content: ""; position: absolute; inset: 5px; border-radius: 50%; background: var(--oc);
}
.db-opt-color[aria-expanded="true"] { background: var(--bg-active); }

/* the choices: 18 colours + any colour, six to a row */
.db-oc-swatches { display: grid; grid-template-columns: repeat(6, 26px); gap: 7px; padding: 6px 8px 8px; }
.db-oc-swatches .db-oc-swatch, .db-oc-swatches .db-oc-custom {
  width: 26px; height: 26px; padding: 0; cursor: pointer;
  border: 0; border-radius: 50%; outline-offset: 2px;
  transition: transform .1s var(--ease-out);
}
.db-oc-swatches .db-oc-swatch:hover, .db-oc-swatches .db-oc-custom:hover { transform: scale(1.12); }
.db-oc-swatches .db-opt-swatch-on { outline: none; box-shadow: 0 0 0 2px var(--bg-modal), 0 0 0 4px var(--text); }
.db-oc-swatches .db-oc-custom { background: none; border: 1px dashed var(--border-strong); overflow: hidden; }
.db-oc-custom::-webkit-color-swatch-wrapper { padding: 0; }
.db-oc-custom::-webkit-color-swatch { border: 0; border-radius: 50%; }
.db-oc-custom::-moz-color-swatch { border: 0; border-radius: 50%; }
.db-oc-inline { margin: 0 4px 6px; border-radius: var(--radius-ctl); background: var(--bg-hover); }
@media (prefers-reduced-motion: reduce) { .db-oc-swatches .db-oc-swatch, .db-oc-swatches .db-oc-custom { transition: none; } }

/* ============================================================
   18 · DATABASE TOTALS — the numbers a table is kept for.
   ------------------------------------------------------------
   A number column now totals itself (the footer used to say
   nothing until "Calculate" was found on hover); each group has
   its own subtotal line; a number can be shown as money; and a
   Summary view lays rows × columns × a calculation out as a
   table — "Amount by Category, by month". Logic: js/pivot.js,
   js/database.js (effectiveSummary, groupSummary, renderSummaryView).
   ============================================================ */

/* the table's own total line: it reads as the bottom line, not as a spare row */
.db-root .db-summary-total { border-top: 2px solid var(--border-strong); }
.db-root .db-summary-total .db-sum-val { font-size: 13.5px; color: var(--text); }
.db-root .db-summary .db-sum-label { text-transform: none; letter-spacing: 0; }
.db-root .db-summary .db-sum-val { font-variant-numeric: tabular-nums; }

/* a group's subtotals: quieter, and not a control */
.db-root .db-group-summary { background: var(--bg-hover); border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); }
.db-root .db-group-summary .db-col-frozen { background: var(--bg); }
.db-root .db-sum-static { cursor: default; }
.db-root .db-sum-static:hover { background: none; }
.db-root .db-subgroup-summary { background: transparent; }

/* formatted numbers sit on the right, digits in columns */
.db-root .db-cell-num { text-align: end; font-variant-numeric: tabular-nums; }

/* number format, in the property menu */
.db-numfmt { display: grid; grid-template-columns: 1fr auto; gap: 6px; padding: 2px 8px 8px; }
.db-numfmt select, .db-numfmt input {
  min-width: 0; height: 32px; padding: 0 8px;
  border: 1px solid var(--border); border-radius: var(--radius-ctl);
  background: var(--bg-input); color: var(--text); font: inherit; font-size: 13px;
}
.db-numfmt .db-numfmt-style { grid-column: 1; }
.db-numfmt .db-numfmt-symbol { grid-column: 2; width: 64px; text-align: center; }
.db-numfmt .db-numfmt-dec { grid-column: 1 / -1; }
.db-numfmt-sample { grid-column: 1 / -1; font-size: 12px; color: var(--text-light); font-variant-numeric: tabular-nums; }

/* ----- Summary view ----- */
.db-pivot { padding-top: 4px; }
.db-pivot-bar { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 10px 14px; padding: 6px 0 14px; }
.db-pivot-field { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.db-pivot-flabel { font-size: 12px; font-weight: 500; color: var(--text-light); }
.db-pivot-sel {
  height: 34px; max-width: 220px; padding: 0 28px 0 10px;
  border: 1px solid var(--border); border-radius: var(--radius-ctl);
  background-color: var(--bg-input); color: var(--text); font: inherit; font-size: 13.5px;
}
.db-pivot-sel:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.db-pivot-scroll { overflow-x: auto; border: 1px solid var(--border); border-radius: var(--radius-card); }
.db-pivot-table { border-collapse: separate; border-spacing: 0; min-width: 100%; font-size: 13.5px; }
.db-pivot-caption {
  caption-side: top; text-align: start; padding: 10px 14px;
  font-family: var(--font-display); font-size: 15px; font-weight: 600; color: var(--text);
  border-bottom: 1px solid var(--border);
}
.db-pivot-table th, .db-pivot-table td { padding: 9px 14px; border-bottom: 1px solid var(--border); white-space: nowrap; }
.db-pivot-table thead th {
  position: sticky; top: 0; z-index: 1;
  background: var(--bg-hover); background-color: color-mix(in srgb, var(--text) 4%, var(--bg));
  font-size: 12.5px; font-weight: 600; color: var(--text-light); text-align: end;
}
.db-pivot-table thead th.db-pivot-corner { text-align: start; }
.db-pivot-rowhead {
  position: sticky; inset-inline-start: 0; z-index: 1;
  text-align: start; font-weight: 500; color: var(--text); background: var(--bg);
  border-inline-end: 1px solid var(--border);
}
.db-pivot-table thead th.db-pivot-corner { inset-inline-start: 0; z-index: 2; border-inline-end: 1px solid var(--border); }
.db-pivot-num {
  text-align: end; font-variant-numeric: tabular-nums; color: var(--text);
  /* the bigger the number, the stronger the tint behind it */
  background: color-mix(in srgb, var(--accent) calc(var(--heat, 0) * 20%), transparent);
}
.db-pivot-num.is-empty::after { content: "–"; color: var(--text-faint); }
.db-pivot-table .db-pivot-total { font-weight: 600; background: color-mix(in srgb, var(--text) 4%, var(--bg)); }
.db-pivot-table td.db-pivot-total { border-inline-start: 1px solid var(--border); }
.db-pivot-table tfoot th, .db-pivot-table tfoot td { border-top: 2px solid var(--border-strong); border-bottom: 0; }
.db-pivot-table .db-pivot-grand { font-size: 14.5px; color: var(--accent); }
.db-pivot-table tbody tr:hover td:not(.db-pivot-total) { box-shadow: inset 0 0 0 999px var(--bg-hover); }

@media (max-width: 760px) {
  .db-pivot-bar { gap: 8px 10px; }
  .db-pivot-field { flex: 1 1 calc(50% - 10px); }
  .db-pivot-sel { max-width: none; width: 100%; }
  .db-pivot-table th, .db-pivot-table td { padding: 8px 10px; }
}

/* a pivot cut down to a readable size says what it left out */
.db-pivot-note { margin: 0 0 10px; padding: 8px 12px; border-radius: var(--radius-ctl); background: var(--bg-hover); font-size: 12.5px; color: var(--text-light); }

/* ============================================================
   19 · DATABASE CALENDAR — the rows of a database, by day.
   ------------------------------------------------------------
   It was a bare month grid: Sunday-first whatever the preference,
   grey pills that all looked alike, nothing to drag, no totals,
   and rows with no date simply missing. Now: month or week, chips
   in their option's colour with their amount, a total on every
   day and for the period, drag a row to another day, and a tray
   for rows that have no date yet. Markup: js/database.js
   (calendarModel, renderCalendar).
   ============================================================ */
.db-root .db-cal-nav { flex-wrap: wrap; gap: 8px 10px; margin-bottom: 12px; align-items: center; }
.db-root .db-cal-title {
  font-family: var(--font-display); font-size: 18px; font-weight: 600; color: var(--text);
  min-width: 170px; text-align: center;
}
.db-root .db-cal-step { width: 32px; height: 32px; padding: 0; font-size: 18px; line-height: 1; }
.db-cal-modes { display: inline-flex; gap: 2px; padding: 3px; border-radius: 9px; background: var(--bg-hover); margin-inline-start: 6px; }
.db-cal-mode {
  height: 28px; padding: 0 12px; border: 0; border-radius: 7px; background: transparent;
  color: var(--text-light); font: inherit; font-size: 13px; font-weight: 500; cursor: pointer;
}
.db-cal-mode:hover { color: var(--text); }
.db-cal-mode.on { background: var(--bg-input); color: var(--text); box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08); }
.db-cal-field { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--text-light); }
.db-cal-sel {
  height: 32px; max-width: 170px; padding: 0 26px 0 9px;
  border: 1px solid var(--border); border-radius: var(--radius-ctl);
  background-color: var(--bg-input); color: var(--text); font: inherit; font-size: 13px;
}
.db-cal-period {
  display: inline-flex; align-items: baseline; gap: 8px;
  padding: 5px 12px; border-radius: var(--radius-ctl);
  background: var(--bg-active);
}
.db-cal-period-l { font-size: 12px; color: var(--text-light); }
.db-cal-period-v { font-size: 15px; font-weight: 600; color: var(--accent); font-variant-numeric: tabular-nums; }

/* rows with no date */
.db-cal-undated {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
  padding: 8px 10px; margin-bottom: 10px;
  border: 1px dashed var(--border-strong); border-radius: var(--radius-card);
}
.db-cal-undated-l { font-size: 12.5px; font-weight: 600; color: var(--text-light); margin-inline-end: 4px; }
.db-cal-undated .db-cal-event { margin-top: 0; max-width: 200px; }
.db-cal-undated-more, .db-cal-undated-hint { font-size: 12px; color: var(--text-faint); }
.db-cal-undated-hint { margin-inline-start: auto; }

/* the grid */
.db-root .db-cal-grid { border-radius: var(--radius-card); background: var(--bg); }
.db-root .db-cal-dow { font-weight: 600; color: var(--text-light); background: var(--bg-hover); padding: 8px 6px; }
.db-root .db-cal-cell { min-height: 112px; padding: 6px; display: flex; flex-direction: column; gap: 3px; min-width: 0; transition: background .1s var(--ease-out); }
.db-root .db-cal-week .db-cal-cell { min-height: 420px; }
.db-root-inline .db-cal-cell { min-height: 72px; }
.db-root .db-cal-out { background: var(--bg-hover); }
.db-root .db-cal-out .db-cal-daynum { color: var(--text-faint); }
.db-root .db-cal-day { gap: 6px; min-height: 22px; justify-content: flex-start; }
.db-cal-daynum {
  flex: none; min-width: 22px; height: 22px; padding: 0 4px; border-radius: 11px;
  display: inline-grid; place-items: center;
  font-size: 12.5px; font-weight: 600; color: var(--text); font-variant-numeric: tabular-nums;
}
.db-cal-today .db-cal-daynum { background: var(--accent); color: var(--accent-foreground); }
.db-root .db-cal-today { box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--accent) 45%, transparent); }
.db-cal-daytotal {
  margin-inline-start: auto; font-size: 11.5px; font-weight: 600; color: var(--text-light);
  font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.db-root .db-cal-add { flex: none; width: 22px; height: 22px; border-radius: 6px; font-size: 15px; line-height: 1; }
.db-root .db-cal-day .db-cal-daytotal + .db-cal-add { margin-inline-start: 0; }
.db-root .db-cal-day .db-cal-add:nth-child(2) { margin-inline-start: auto; }
@media (hover: none) { .db-root .db-cal-add { opacity: .7; } }

/* a row */
.db-root .db-cal-event {
  display: flex; align-items: center; gap: 6px; margin-top: 0;
  padding: 3px 7px; border-radius: 6px;
  font-size: 12.5px; color: var(--text); background: var(--bg-hover);
  box-shadow: inset 3px 0 0 var(--border-strong);
  user-select: none; -webkit-user-select: none;
}
.db-root .db-cal-event:hover { background: color-mix(in srgb, var(--text) 9%, transparent); }
.db-root .db-cal-event-col {
  background: color-mix(in srgb, var(--oc) 18%, var(--bg));
  box-shadow: inset 3px 0 0 var(--oc);
}
.db-root .db-cal-event-col:hover { background: color-mix(in srgb, var(--oc) 28%, var(--bg)); }
.db-cal-event-t { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.db-cal-event-v { flex: none; font-size: 11.5px; font-weight: 600; color: var(--text-light); font-variant-numeric: tabular-nums; }
.db-root .db-cal-event[draggable="true"] { cursor: grab; }
.db-root .db-cal-event.db-cal-dragging { opacity: .45; }
.db-root .db-cal-event:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.db-root .db-cal-cell.db-cal-drop { background: var(--accent-tint); box-shadow: inset 0 0 0 2px var(--accent); }
.db-cal-more {
  align-self: flex-start; border: 0; background: none; cursor: pointer;
  padding: 2px 6px; border-radius: 6px; font: inherit; font-size: 12px; color: var(--text-light);
}
.db-cal-more:hover { background: var(--bg-hover); color: var(--accent); }

@media (max-width: 760px) {
  .db-root .db-cal-title { min-width: 0; font-size: 16px; flex: 1 1 auto; }
  .db-cal-field { flex: 1 1 calc(50% - 10px); }
  .db-cal-sel { max-width: none; flex: 1; }
  .db-root .db-cal-cell { min-height: 76px; padding: 4px 3px; }
  .db-root .db-cal-week .db-cal-cell { min-height: 260px; }
  .db-root .db-cal-event { padding: 2px 4px; font-size: 11.5px; }
  .db-cal-event-v, .db-cal-daytotal { display: none; }   /* no room beside a title at this width */
  .db-cal-undated-hint { display: none; }
}
@media (prefers-reduced-motion: reduce) { .db-root .db-cal-cell { transition: none; } }

/* an amount on a tinted chip reads in full-strength ink (the lighter ink fell under 4.5:1 on some tints) */
.db-root .db-cal-event .db-cal-event-v { color: var(--text); }
.db-cal-undated .db-cal-undated-more { color: var(--text-light); }

/* ============================================================
   20 · CALENDAR ITEM MENUS — Edit and Delete on the item itself.
   ------------------------------------------------------------
   Right-click (or the Menu key / Shift+F10) an event, reminder or
   row in either calendar. The Plan calendar's menu is its own
   element (.cal-menu, inside the calendar so it sits above it);
   the database calendar reuses the database popover (.db-pop).
   ============================================================ */
.cal-menu {
  position: fixed; z-index: 30; min-width: 190px; padding: 5px;
  border: 1px solid var(--border); border-radius: 13px;
  background: var(--bg-modal); box-shadow: var(--shadow-popover);
}
.cal-menu-item {
  display: block; width: 100%; padding: 8px 10px; border: 0; border-radius: 8px;
  background: none; color: var(--text); font: inherit; font-size: 13.5px; text-align: start; cursor: pointer;
}
.cal-menu-item:hover, .cal-menu-item:focus-visible { background: var(--bg-hover); outline: none; }
.cal-menu-item:focus-visible { box-shadow: inset 0 0 0 2px var(--accent); }
.cal-menu-item.danger { color: var(--danger); }
.cal-menu-sep { height: 1px; margin: 4px 2px; background: var(--border); }

/* the database popover, used as a menu: its items take focus */
.db-pop.db-cal-menu .db-pop-item:focus { outline: none; background: var(--bg-hover); box-shadow: inset 0 0 0 2px var(--accent); }

/* ============================================================
   21 · BUDGETS — a limit, what is spent, what is left.
   ------------------------------------------------------------
   One meter everywhere a budget shows (the Budget view, the
   database calendar, Home): accent while there is room, gold from
   80% of the limit, danger once it is passed. The colour is never
   the only sign — the words beside it say "left" or "over by".
   ============================================================ */
.db-budget { padding-top: 4px; max-width: 980px; }
.db-bud-nav { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 8px 12px; padding: 6px 0 14px; }
.db-bud-nav > .db-tool-btn, .db-bud-nav > .db-bud-title { align-self: center; }
.db-bud-title { min-width: 150px; text-align: center; }
.db-bud-now:disabled { opacity: 0.5; cursor: default; }

.db-bud-meter {
  --bud: var(--accent);
  position: relative; height: 10px; border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--text) 9%, var(--bg));
}
.db-bud-meter.is-warn { --bud: var(--gold); }
.db-bud-meter.is-over { --bud: var(--danger); }
.db-bud-meter.is-none { --bud: var(--text-faint); }
.db-bud-fill { height: 100%; border-radius: inherit; background: var(--bud); transition: width var(--dur-2) var(--ease-out); }
.db-bud-tick {
  position: absolute; top: -4px; bottom: -4px; width: 2px; margin-inline-start: -1px;
  border-radius: 1px; background: var(--text);
}

.db-bud-hero {
  padding: 20px 22px; border: 1px solid var(--border); border-radius: var(--radius-card);
  background: var(--bg-modal); box-shadow: var(--shadow-card);
}
.db-bud-hero-top { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 14px 24px; margin-bottom: 16px; }
.db-bud-k { display: block; font-size: 12px; font-weight: 500; color: var(--text-light); }
.db-bud-spentbox .db-bud-k { margin-bottom: 4px; }
.db-bud-big { font-family: var(--font-display); font-size: 38px; line-height: 1.1; font-weight: 600; color: var(--text); font-variant-numeric: tabular-nums; }
.db-bud-hero.is-over .db-bud-big { color: var(--danger); }
.db-bud-totalbox { display: flex; flex-direction: column; gap: 4px; align-items: flex-end; text-align: end; }
.db-bud-hint { max-width: 240px; font-size: 12px; color: var(--text-faint); }
.db-bud-hero .db-bud-meter { height: 14px; }
.db-bud-status { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 16px; margin-top: 12px; font-size: 13.5px; color: var(--text-light); }
.db-bud-status .db-bud-left { font-size: 15px; font-weight: 600; color: var(--text); }
.db-bud-hero.is-over .db-bud-status .db-bud-left { color: var(--danger); }
.db-bud-forecast { margin-top: 10px; padding: 9px 12px; border-radius: var(--radius-ctl); background: var(--bg-hover); font-size: 13px; color: var(--text-light); }
.db-bud-forecast.is-over { background: var(--danger-bg); color: var(--text); }

.db-bud-limit {
  width: 132px; height: 34px; padding: 0 10px;
  border: 1px solid var(--border); border-radius: var(--radius-ctl);
  background: var(--bg-input); color: var(--text); font: inherit; font-size: 14px;
  text-align: end; font-variant-numeric: tabular-nums;
}
.db-bud-limit::placeholder { color: var(--text-placeholder); }
.db-bud-limit:hover { border-color: var(--border-strong); }
.db-bud-limit:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.db-bud-limit:disabled { opacity: 0.7; }
.db-bud-totalbox .db-bud-limit { width: 170px; height: 40px; font-size: 17px; font-weight: 600; }

.db-bud-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 26px 0 8px; }
.db-bud-h { margin: 0; font-size: 14px; font-weight: 600; color: var(--text); }
.db-bud-list { border: 1px solid var(--border); border-radius: var(--radius-card); overflow: hidden; background: var(--bg); }
.db-bud-item + .db-bud-item { border-top: 1px solid var(--border); }
.db-bud-row {
  display: grid; grid-template-columns: minmax(150px, 1.1fr) minmax(90px, 1.4fr) 250px 132px;
  align-items: center; gap: 8px 16px; padding: 10px 14px;
}
.db-bud-name {
  display: flex; align-items: center; gap: 8px; min-width: 0; padding: 4px 6px; margin-inline-start: -6px;
  border: 0; border-radius: var(--radius-ctl); background: none; color: var(--text); font: inherit; text-align: start; cursor: pointer;
}
.db-bud-name:hover { background: var(--bg-hover); }
.db-bud-name:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.db-bud-caret { flex: none; width: 10px; font-size: 10px; color: var(--text-faint); }
.db-bud-plain { font-size: 13.5px; color: var(--text-light); }
.db-bud-count { flex: none; font-size: 12px; color: var(--text-faint); white-space: nowrap; }
.db-bud-nums { display: flex; align-items: center; justify-content: flex-end; gap: 8px; font-variant-numeric: tabular-nums; }
.db-bud-spent { font-size: 14px; font-weight: 600; color: var(--text); white-space: nowrap; }
.db-bud-of { font-size: 12.5px; color: var(--text-faint); }
.db-bud-row > .db-bud-left { font-size: 13px; color: var(--text-light); text-align: end; white-space: nowrap; font-variant-numeric: tabular-nums; }
.db-bud-item.is-warn .db-bud-row > .db-bud-left { color: var(--text); font-weight: 600; }
.db-bud-item.is-over .db-bud-row > .db-bud-left { color: var(--danger); font-weight: 600; }

.db-bud-entries { padding: 2px 14px 10px 34px; background: color-mix(in srgb, var(--text) 3%, var(--bg)); }
.db-bud-entry {
  display: grid; grid-template-columns: 64px 1fr auto; align-items: center; gap: 12px; width: 100%;
  padding: 7px 8px; border: 0; border-radius: var(--radius-ctl); background: none;
  color: var(--text); font: inherit; font-size: 13.5px; text-align: start; cursor: pointer;
}
.db-bud-entry:hover { background: var(--bg-hover); }
.db-bud-entry:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.db-bud-entry-day { color: var(--text-light); font-size: 12.5px; }
.db-bud-entry-title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.db-bud-entry-amt { font-variant-numeric: tabular-nums; font-weight: 500; }
.db-bud-more { padding: 6px 8px; font-size: 12.5px; color: var(--text-faint); }

/* the budget, beside the calendar's total */
.db-cal-budget {
  display: inline-grid; grid-template-columns: auto auto; align-items: baseline; gap: 2px 8px;
  padding: 5px 12px; border: 0; border-radius: var(--radius-ctl);
  background: var(--bg-active); color: var(--text); font: inherit; cursor: pointer;
}
.db-cal-budget:hover { background: var(--bg-hover); }
.db-cal-budget:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.db-cal-budget-v { font-size: 13.5px; font-weight: 600; font-variant-numeric: tabular-nums; }
.db-cal-budget.is-over .db-cal-budget-v { color: var(--danger); }
.db-cal-budget .db-bud-meter { grid-column: 1 / -1; height: 4px; }

/* Home: every budget, this period */
.wg-card[data-widget="budgets"] { flex: 1.2 1 300px; }
.hb-list { display: flex; flex-direction: column; gap: 4px; }
.hb-item {
  display: flex; flex-direction: column; gap: 7px; width: 100%; padding: 10px;
  border: 0; border-radius: var(--radius-ctl); background: none; color: var(--text); font: inherit; text-align: start; cursor: pointer;
}
.hb-item:hover { background: var(--bg-hover); }
.hb-item:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.hb-top { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.hb-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 14px; font-weight: 600; }
.hb-period { flex: none; font-size: 12px; color: var(--text-faint); }
.hb-item .db-bud-meter { display: block; height: 8px; }
.hb-nums { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 2px 12px; font-size: 12.5px; color: var(--text-light); font-variant-numeric: tabular-nums; }
.hb-item.is-over .hb-status { color: var(--danger); font-weight: 600; }
.hb-lines { display: flex; flex-wrap: wrap; gap: 4px 6px; }
.hb-line { display: inline-flex; align-items: center; gap: 5px; padding: 2px 8px; border-radius: var(--radius-pill); background: var(--bg-hover); font-size: 12px; color: var(--text-light); }
.hb-line.is-over { color: var(--danger); }
.hb-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--oc, var(--text-faint)); }

@media (max-width: 720px) {
  .db-bud-nav .db-pivot-field { flex: 1 1 calc(50% - 12px); }
  .db-bud-nav .db-pivot-sel { max-width: none; width: 100%; }
  .db-bud-nav > .db-tool-spacer { flex-basis: 100%; height: 0; }
  .db-bud-hero { padding: 16px; }
  .db-bud-big { font-size: 30px; }
  .db-bud-totalbox { align-items: flex-start; text-align: start; width: 100%; }
  .db-bud-totalbox .db-bud-limit { width: 100%; }
  .db-bud-row { grid-template-columns: 1fr auto; gap: 8px 12px; }
  .db-bud-row > .db-bud-meter { grid-column: 1 / -1; grid-row: 2; }
  .db-bud-row > .db-bud-left { grid-column: 1 / -1; text-align: start; }
  .db-bud-limit { width: 104px; }
  .db-bud-entries { padding-inline-start: 14px; }
}
@media (prefers-reduced-motion: reduce) { .db-bud-fill { transition: none; } }

/* ============================================================
   22 · HOME — MY DAY. Today's events lead the day card.
   ============================================================ */

/* ============================================================
   Home — "My Day" (js/hometoday.js, widget id `tasks`)
   Today's calendar events lead the card: a colour dot, the time, the
   title. They are buttons (one click opens the calendar), not `.ht-task`
   rows, so nothing that counts reminders counts them.
   ============================================================ */
.ht-evs {
  display: flex; flex-direction: column; gap: 2px;
  padding-bottom: 6px; margin-bottom: 4px;
  border-bottom: 1px solid var(--border);
}
/* events with nothing under them need no divider */
.ht-evs:last-child { padding-bottom: 0; margin-bottom: 0; border-bottom: 0; }
.ht-ev {
  display: flex; align-items: center; gap: 9px;
  width: 100%; min-width: 0;
  padding: 6px;
  border: 0; border-radius: var(--radius-ctl);
  background: none; color: var(--text);
  font: inherit; font-size: 13px; text-align: start; cursor: pointer;
  transition: background .12s ease;
}
.ht-ev:hover { background: var(--bg-hover); }
.ht-ev:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
/* the dot sits in the 16px column the reminder checkboxes use, so titles line up */
.ht-ev-dot {
  flex: 0 0 auto; width: 8px; height: 8px; margin: 0 4px;
  border-radius: 50%; background: var(--accent);
}
.ht-ev-time {
  flex: 0 0 auto; min-width: 62px;
  font-size: 12px; font-weight: 500; color: var(--text-light);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.ht-ev-allday .ht-ev-time { color: var(--text-faint); }
.ht-ev-tx {
  flex: 1 1 auto; min-width: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ht-ev:hover .ht-ev-tx { color: var(--accent); }

/* week ribbon: a day that holds a calendar event carries a ring, so it reads
   apart from the filled marks (reminder / note / database row) */
.wk-m-ev { background: transparent; box-shadow: inset 0 0 0 1.5px var(--text-light); }
.wk-today .wk-m-ev { box-shadow: inset 0 0 0 1.5px currentColor; }

@media (max-width: 720px) {
  /* a comfortable touch target, and "2 events · 1 due" fits an 86px tile */
  .ht-ev { min-height: 40px; padding: 8px 6px; }
  .ht-ev-time { min-width: 58px; }
  .wk-cap { font-size: 11px; letter-spacing: -0.1px; }
}

@media (prefers-reduced-motion: reduce) {
  .ht-ev { transition: none; }
}

/* ============================================================
   23 · CALENDAR EDITOR — saving state, errors, custom repeats.
   ============================================================ */
/* ─────────────────────────────────────────────────────────────────────────
   Plan calendar — event editor save state · custom-schedule events
   (appended by the calendar wave; classes only, existing tokens only)

   .cal-ee-err      the editor's "couldn't save" line (role="alert")
   .cal-ee-save.is-busy   Save while the write is out ("Saving…")
   .cal-ev-custom   an event whose repeat rule the calendar keeps but cannot
                    expand: drawn on its first date only, and marked as such
   ───────────────────────────────────────────────────────────────────────── */
.cal-ee-err {
  font-size: 12.5px; line-height: 1.4;
  color: var(--danger); background: var(--danger-bg);
  border-radius: var(--radius-ctl); padding: 8px 10px;
}
.cal-ee-err[hidden] { display: none; }
.cal-ee-save.is-busy { opacity: .72; cursor: progress; }
.cal-ee-save.is-busy:hover { filter: none; }
/* the card takes focus only as a fallback (tabindex -1) — no ring around the whole sheet */
.cal-ee-card:focus { outline: none; }
.cal-ee-series { line-height: 1.4; }
/* a dashed edge says "this is not the whole schedule" without another glyph in a narrow chip */
.cal-ev.cal-ev-custom,
.cal-tg-evt.cal-ev-custom { outline: 1px dashed var(--border-strong); outline-offset: -1px; }
.cal-ev.cal-ev-custom:focus-visible,
.cal-tg-evt.cal-ev-custom:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* ============================================================
   24 · DATABASES — personal view settings, the budget year strip,
   income against spending.
   ============================================================ */
/* ============================================================
   DATABASES — personal view settings · budget at a glance ·
   income beside spending.
   ------------------------------------------------------------
   (append to css/features/elevate.css, after "21 · BUDGETS")

   · .db-personal-note — under the toolbar of a database someone
     else owns, while the viewer's own filter / sort / grouping
     is laid over the owner's view. Quiet: one line and a Reset.
   · .db-bud-strip — the periods around the viewed one, a small
     bar each, in the budget meter's own colours (accent, gold
     from 80%, danger past the limit). The label under the bar
     and its aria-label carry the meaning; colour never does
     alone.
   · .db-bud-net / .db-bud-kind / .is-income — money in.
   ============================================================ */
.db-personal-note {
  display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px;
  margin: 2px 0 8px; font-size: 12.5px; color: var(--text-faint);
}
.db-personal-reset {
  padding: 2px 8px; border: 0; border-radius: var(--radius-ctl); background: none;
  color: var(--text-light); font: inherit; font-size: 12.5px; text-decoration: underline; cursor: pointer;
}
.db-personal-reset:hover { background: var(--bg-hover); color: var(--text); }
.db-personal-reset:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* the year (or the last weeks / years) at a glance */
.db-bud-strip { margin-top: 22px; }
.db-bud-strip > .db-bud-h { margin-bottom: 8px; }
.db-bud-bars {
  display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: 4px;
  padding: 10px; border: 1px solid var(--border); border-radius: var(--radius-card); background: var(--bg);
}
.db-bud-bar {
  --bud: var(--accent);
  display: flex; flex-direction: column; align-items: stretch; gap: 6px; min-width: 0;
  padding: 6px 4px 5px; border: 0; border-radius: var(--radius-ctl); background: none;
  color: var(--text-light); font: inherit; cursor: pointer;
}
.db-bud-bar.is-warn { --bud: var(--gold); }
.db-bud-bar.is-over { --bud: var(--danger); }
.db-bud-bar.is-none { --bud: var(--text-faint); }
.db-bud-bar:hover { background: var(--bg-hover); }
.db-bud-bar:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.db-bud-bar.is-viewed { background: var(--bg-active); color: var(--text); }
.db-bud-bar.is-future { opacity: 0.6; }
.db-bud-bar-track {
  position: relative; display: block; height: 56px; overflow: hidden;
  border-radius: 4px; background: color-mix(in srgb, var(--text) 9%, var(--bg));
}
.db-bud-bar-fill {
  position: absolute; inset-inline: 0; bottom: 0; display: block; min-height: 0;
  border-radius: 4px 4px 0 0; background: var(--bud);
  transition: height var(--dur-2) var(--ease-out);
}
.db-bud-bar-l {
  display: block; overflow: hidden; font-size: 11.5px; line-height: 1.2; text-align: center;
  white-space: nowrap; text-overflow: clip; font-variant-numeric: tabular-nums;
}
.db-bud-bar.is-viewed .db-bud-bar-l { font-weight: 600; }

/* income beside spending */
.db-bud-net {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 20px;
  margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--border);
  font-size: 13.5px; color: var(--text-light); font-variant-numeric: tabular-nums;
}
.db-bud-net-part { display: inline-flex; align-items: baseline; gap: 6px; }
.db-bud-net-v { font-weight: 600; color: var(--text); }
.db-bud-net.is-neg .db-bud-net-net .db-bud-net-v { color: var(--danger); }

.db-bud-namecell { display: flex; align-items: center; gap: 6px; min-width: 0; }
.db-bud-namecell > .db-bud-name { flex: 0 1 auto; }
.db-bud-kind {
  flex: none; padding: 2px 8px; border: 1px solid var(--border); border-radius: var(--radius-pill);
  background: none; color: var(--text-faint); font: inherit; font-size: 11.5px; line-height: 1.5; cursor: pointer;
}
.db-bud-kind:hover { border-color: var(--border-strong); color: var(--text); background: var(--bg-hover); }
.db-bud-kind:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.db-bud-kind.is-on { border-color: var(--accent); color: var(--text); background: var(--bg-active); font-weight: 600; }
/* with a pointer that can hover, the unpressed toggle waits for the line to be pointed at */
@media (hover: hover) {
  .db-bud-kind:not(.is-on) { opacity: 0; }
  .db-bud-item:hover .db-bud-kind, .db-bud-item:focus-within .db-bud-kind { opacity: 1; }
}
.db-bud-inc-note { min-width: 0; font-size: 12.5px; color: var(--text-faint); }
.db-bud-item.is-income .db-bud-row > .db-bud-left { color: var(--text-light); font-weight: 500; }

@media (max-width: 720px) {
  .db-bud-bars { gap: 2px; padding: 8px 6px; }
  .db-bud-bar { padding: 5px 1px 4px; }
  .db-bud-bar-track { height: 44px; }
  .db-bud-bar-l { font-size: 10px; }
  .db-bud-row > .db-bud-inc-note { grid-column: 1 / -1; grid-row: 2; }
  /* no hover on a phone: the toggle is always there, and big enough to press */
  .db-bud-kind { padding: 6px 12px; font-size: 12.5px; }
  .db-bud-kind:not(.is-on) { opacity: 1; }
  .db-personal-reset { padding: 6px 10px; }
  .db-personal-note { margin-bottom: 10px; }
}
@media (prefers-reduced-motion: reduce) { .db-bud-bar-fill { transition: none; } }

/* ============================================================
   25 · SYNC STATUS — saved, offline, or not saved, in one place.
   ============================================================ */
/* ============================================================
   SYNC STATUS — one account-wide "is my work saved?" control.
   ------------------------------------------------------------
   Markup + logic: js/syncstatus.js (mounted into #sync-status in
   the top bar). A glyph and a short label; the glyph's SHAPE
   changes with the state, so colour is never the only signal.
   The popover is position:fixed and placed by the module, so it
   works the same in the top bar on a desktop and on a phone, where
   it stops short of the tab bar.
   While it is mounted it speaks for the page too, so the old
   per-page text chip steps aside (it stays in the DOM).
   ============================================================ */
body.has-sync-status #save-indicator { display: none; }

.sync-host { position: relative; display: inline-flex; align-items: center; margin-inline-end: 4px; }
.sync-host[hidden] { display: none; }

.sync-btn {
  display: inline-flex; align-items: center; gap: 6px;
  height: 26px; padding: 0 9px 0 7px; min-width: 0; max-width: 260px;
  border: 0; border-radius: var(--radius-pill);
  background: none; color: var(--text-faint);
  font: inherit; font-size: 11.5px; font-weight: 500; letter-spacing: 0.2px;
  white-space: nowrap; cursor: pointer;
  transition: background 0.14s ease, color 0.14s ease;
}
.sync-btn:hover, .sync-host.is-open .sync-btn { background: var(--bg-hover); color: var(--text-light); }
.sync-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.sync-label { overflow: hidden; text-overflow: ellipsis; }

.sync-glyph { display: inline-flex; flex: none; }
.sync-glyph svg {
  display: block; fill: none; stroke: currentColor;
  stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round;
}

.sync-btn[data-state="saving"], .sync-btn[data-state="syncing"] { color: var(--text-light); }
.sync-btn[data-state="saving"] .sync-glyph svg,
.sync-btn[data-state="syncing"] .sync-glyph svg { animation: sync-turn 1.1s linear infinite; }
.sync-btn[data-state="offline"] {
  color: var(--text);
  background: color-mix(in srgb, var(--gold) 18%, transparent);
}
.sync-btn[data-state="offline"] .sync-glyph { color: var(--gold); }
.sync-btn[data-state="error"] {
  color: var(--danger); font-weight: 600;
  background: color-mix(in srgb, var(--danger) 12%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--danger) 40%, transparent);
}
@keyframes sync-turn { to { transform: rotate(360deg); } }

/* read out, never seen */
.sync-sr {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}

.sync-pop {
  position: fixed; z-index: 120;
  max-height: calc(100vh - 80px); overflow: hidden auto;
  padding: 12px 14px;
  background: var(--bg-modal); color: var(--text);
  border-radius: 12px; box-shadow: var(--shadow-popover);
  font-size: 13px; line-height: 1.45; text-align: start; white-space: normal;
}
.sync-pop[hidden] { display: none; }
.sync-pop:focus { outline: none; }
.sync-pop:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.sync-pop-head { display: flex; align-items: center; gap: 8px; margin-bottom: 4px; }
.sync-pop-title { font-size: 13.5px; font-weight: 600; }
.sync-host[data-state="error"] .sync-pop-head { color: var(--danger); }
.sync-host[data-state="offline"] .sync-pop-head .sync-glyph { color: var(--gold); }
.sync-pop-line { margin: 4px 0 0; color: var(--text-light); }
.sync-pop-foot { margin: 8px 0 0; font-size: 12px; color: var(--text-faint); }

.sync-fails { list-style: none; margin: 8px 0 0; padding: 0; display: grid; gap: 6px; }
.sync-fail {
  display: grid; gap: 2px; padding: 8px 10px;
  border-radius: var(--radius-ctl); border: 1px solid var(--border);
  background: var(--bg);
}
.sync-fail-what { font-weight: 600; overflow-wrap: anywhere; }
.sync-fail-why { color: var(--text-light); overflow-wrap: anywhere; }
.sync-fail-note { font-size: 12px; color: var(--text-faint); }
.sync-fail-code { font-size: 11px; color: var(--text-faint); }

.sync-acts { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.sync-act {
  min-height: 30px; padding: 0 12px;
  border: 1px solid var(--border); border-radius: var(--radius-ctl);
  background: none; color: var(--text);
  font: inherit; font-size: 12.5px; font-weight: 500; cursor: pointer;
}
.sync-act:hover { background: var(--bg-hover); }
.sync-act:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.sync-act-primary { border-color: var(--accent); color: var(--accent); font-weight: 600; }
.sync-act[aria-disabled="true"] { opacity: 0.6; cursor: default; }

@media (max-width: 720px) {
  /* The top bar is tight on a phone: the calm states are the glyph alone
     (the label is still the button's accessible name); a state that needs
     attention keeps its words. */
  .sync-host { margin-inline-end: 0; }
  .sync-btn { height: 30px; padding: 0 8px; max-width: 46vw; }
  .sync-btn[data-state="saved"] .sync-label,
  .sync-btn[data-state="saving"] .sync-label,
  .sync-btn[data-state="syncing"] .sync-label { display: none; }
  /* stop above the bottom tab bar (58px + the home-indicator inset) */
  .sync-pop { max-height: calc(100dvh - 132px - env(safe-area-inset-bottom, 0px)); }
  .sync-act { min-height: 38px; flex: 1 1 auto; }
}

@media (prefers-reduced-motion: reduce) {
  .sync-btn { transition: none; }
  .sync-btn[data-state="saving"] .sync-glyph svg,
  .sync-btn[data-state="syncing"] .sync-glyph svg { animation: none; }
}

/* ============================================================
   26 · CALENDAR — an event's time zone.
   ============================================================ */
/* ============================================================
   CALENDAR — time zones (event editor).
   The picker sits under the times and is QUIET until it is used:
   "Floating" is the default and the simple case, so it reads as a
   line of secondary text, not as one more boxed field. Once a zone
   is chosen (.is-zoned) it takes the look of the other controls,
   and the note beside it says what that time is on this clock.
   Markup: js/calendar.js openEventEditor —
     .cal-ee-row.cal-ee-tz > select.cal-ee-tzsel + span.cal-ee-tznote
   ============================================================ */
.cal-ee-tz { flex-wrap: wrap; gap: 6px 10px; margin-top: -4px; }
.cal-ee-tz .cal-ee-tzsel {
  flex: 0 1 auto; min-width: 0; max-width: 100%; height: 30px; padding: 0 6px;
  border: 1px solid transparent; border-radius: var(--radius-ctl);
  background: transparent; color: var(--text-light);
  font: inherit; font-size: 12.5px; cursor: pointer;
}
.cal-ee-tz .cal-ee-tzsel:hover { background: var(--bg-hover); color: var(--text); }
.cal-ee-tz .cal-ee-tzsel:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.cal-ee-tz.is-zoned .cal-ee-tzsel {
  border-color: var(--border); background: var(--bg-input); color: var(--text);
}
/* the native list itself must stay readable in dark mode */
.cal-ee-tz .cal-ee-tzsel option { background: var(--bg-modal); color: var(--text); }
.cal-ee-tznote { font-size: 12.5px; color: var(--text-light); font-variant-numeric: tabular-nums; }
.cal-ee-tznote:empty { display: none; }
@media (max-width: 640px) {
  /* a finger needs the full control height, and the note goes under it */
  .cal-ee-tz .cal-ee-tzsel { height: 40px; flex: 1 1 100%; font-size: 14px; }
}

/* ============================================================
   27 · BUDGETS — savings goals and repeating entries.
   ============================================================ */

/* ============================================================
   Budgets, wave 2 — goals, entries that repeat  (js/database.js)
   ------------------------------------------------------------
   · .db-bud-goals / .db-bud-goal — a card per savings goal, with
     the budget meter in the accent colour (.is-goal): a goal
     filling up is good news, so it never turns gold or red. A
     reached goal says so in words; the colour is not the message.
   · .db-bud-rules / .db-bud-rule — the "Repeating" list: what,
     when next, how much, Pause and Delete.
   · .db-bud-entry-row / .db-bud-rep — an entry with its "repeat"
     button beside it; .is-repeat and .db-tr-repeat mark, quietly,
     a row that a repeating entry made.
   · .db-bud-form — the small popover forms (repeat, goal).
   ============================================================ */
.db-bud-none { margin: 2px 0 4px; font-size: 13px; color: var(--text-faint); }

.db-bud-goals { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 12px; }
.db-bud-goal {
  display: flex; flex-direction: column; gap: 10px; min-width: 0; padding: 14px 16px;
  border: 1px solid var(--border); border-radius: var(--radius-card); background: var(--bg-modal);
}
.db-bud-goal-top { display: flex; align-items: center; gap: 8px; min-width: 0; }
.db-bud-goal-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 14.5px; font-weight: 600; color: var(--text); }
.db-bud-goal-top > .db-chip { flex: none; max-width: 45%; }
.db-bud-goal-edit, .db-bud-rule-btn {
  flex: none; padding: 3px 9px; border: 1px solid var(--border); border-radius: var(--radius-ctl);
  background: none; color: var(--text-light); font: inherit; font-size: 12.5px; cursor: pointer;
}
.db-bud-goal-edit:hover, .db-bud-rule-btn:hover { border-color: var(--border-strong); background: var(--bg-hover); color: var(--text); }
.db-bud-goal-edit:focus-visible, .db-bud-rule-btn:focus-visible, .db-bud-rep:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.db-bud-goal-nums { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 4px 8px; font-variant-numeric: tabular-nums; }
.db-bud-goal-nums > .db-bud-spent { font-size: 20px; line-height: 1.2; }
.db-bud-goal-nums > .db-bud-of, .db-bud-goal-target { padding-bottom: 2px; }
.db-bud-goal-target { font-size: 13.5px; color: var(--text-light); }
.db-bud-goal-pct { margin-inline-start: auto; padding-bottom: 2px; font-size: 13.5px; font-weight: 600; color: var(--text); }
.db-bud-goal-saved { display: flex; flex-direction: column; gap: 3px; }
.db-bud-goal-saved .db-bud-limit { width: 124px; }
.db-bud-meter.is-goal { --bud: var(--accent); }
.db-bud-goal-note { font-size: 13px; color: var(--text-light); }
.db-bud-goal.is-reached .db-bud-goal-note { color: var(--text); font-weight: 600; }
.db-bud-goal.is-overdue .db-bud-goal-note { color: var(--danger); }

.db-bud-rule {
  display: grid; grid-template-columns: minmax(120px, 1.2fr) minmax(140px, 1.6fr) auto auto;
  align-items: center; gap: 6px 16px; padding: 10px 14px;
}
.db-bud-rule-title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13.5px; color: var(--text); }
.db-bud-rule-when { font-size: 13px; color: var(--text-light); }
.db-bud-rule-amt { font-size: 14px; font-weight: 600; color: var(--text); text-align: end; white-space: nowrap; font-variant-numeric: tabular-nums; }
.db-bud-rule-acts { display: inline-flex; gap: 6px; justify-content: flex-end; }
.db-bud-rule.is-paused .db-bud-rule-title, .db-bud-rule.is-paused .db-bud-rule-amt { color: var(--text-faint); }
.db-bud-rule-del:hover { border-color: var(--danger); color: var(--danger); background: var(--danger-bg); }

.db-bud-entry-row { display: flex; align-items: center; gap: 4px; }
.db-bud-entry-row > .db-bud-entry { flex: 1 1 auto; min-width: 0; width: auto; }
.db-bud-entry.is-repeat .db-bud-entry-title::after { content: " \21BB"; color: var(--text-faint); }
.db-bud-rep {
  flex: none; width: 28px; height: 28px; padding: 0; border: 0; border-radius: var(--radius-ctl);
  background: none; color: var(--text-faint); font: inherit; font-size: 15px; line-height: 1; cursor: pointer;
}
.db-bud-rep:hover { background: var(--bg-hover); color: var(--text); }
.db-bud-rep.is-on { color: var(--accent); }
/* with a pointer that can hover, the button waits for its line to be pointed at */
@media (hover: hover) {
  .db-bud-rep:not(.is-on) { opacity: 0; }
  .db-bud-entry-row:hover .db-bud-rep, .db-bud-entry-row:focus-within .db-bud-rep { opacity: 1; }
}
/* a row a repeating entry made: a hairline at the row's leading edge */
.db-tr.db-tr-repeat > .db-td-gutter { box-shadow: inset 2px 0 0 color-mix(in srgb, var(--accent) 45%, transparent); }

.db-pop.db-bud-form { display: flex; flex-direction: column; gap: 10px; padding: 12px; }
.db-pop.db-bud-form > .db-pop-label { padding: 0; }
.db-bud-form .db-pivot-sel { width: 100%; max-width: none; }
.db-bud-input {
  width: 100%; height: 32px; padding: 0 9px; box-sizing: border-box;
  border: 1px solid var(--border); border-radius: var(--radius-ctl);
  background: var(--bg-input); color: var(--text); font: inherit; font-size: 13.5px;
}
.db-bud-input:hover { border-color: var(--border-strong); }
.db-bud-input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.db-bud-form-note { font-size: 12.5px; line-height: 1.4; color: var(--text-light); }
.db-bud-form-go { justify-content: center; border-color: var(--accent); color: var(--text); font-weight: 600; }
.db-bud-form-danger { justify-content: center; color: var(--danger); }
.db-bud-form-danger:hover { border-color: var(--danger); background: var(--danger-bg); }

@media (max-width: 720px) {
  .db-bud-goals { grid-template-columns: 1fr; }
  .db-bud-rule { grid-template-columns: 1fr auto; }
  .db-bud-rule-when { grid-column: 1; grid-row: 2; }
  .db-bud-rule-acts { grid-column: 1 / -1; justify-content: flex-start; }
  .db-bud-rule-btn, .db-bud-goal-edit { padding: 7px 12px; }
  .db-bud-rep { width: 36px; height: 36px; }
  .db-bud-goal-saved .db-bud-limit { width: 124px; }
}

/* ============================================================
   28 · PHONE CAPTURE — a note or a task from the "+", where you are.
   ------------------------------------------------------------
   The centre "+" of the phone tab bar made an empty page and
   dropped you into it, so jotting one line cost you your place.
   It now opens this sheet (js/app.js openPhoneCapture): a note or
   a task, saved without leaving the screen you were on, with a
   blank page and a voice note one tap away. A bottom sheet on the
   shared scrim (§9), thumb-sized targets, 16px text (no iOS zoom
   on focus), logical properties so it mirrors in RTL. Nothing
   opens it on desktop.
   ============================================================ */
.pcap-layer {
  position: fixed; inset: 0; z-index: 150;
  display: flex; align-items: flex-end; justify-content: center;
  opacity: 0; transition: opacity .16s ease;
}
.pcap-layer.open { opacity: 1; }
.pcap-sheet {
  width: 100%; max-width: 520px; box-sizing: border-box;
  max-height: calc(100dvh - 24px); overflow: auto;
  padding: 8px 16px calc(14px + env(safe-area-inset-bottom, 0px));
  background: var(--bg-modal); color: var(--text);
  border: 1px solid var(--border); border-bottom: 0;
  border-radius: 18px 18px 0 0;
  box-shadow: var(--shadow-popover);
  transform: translateY(16px); transition: transform .18s ease;
}
.pcap-layer.open .pcap-sheet { transform: none; }
.pcap-grip { width: 36px; height: 4px; margin: 2px auto 8px; border-radius: 999px; background: var(--border-strong); }
.pcap-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 10px; }
.pcap-title { margin: 0; font-family: var(--font-display); font-size: 17px; font-weight: 600; color: var(--text); }
.pcap-close { width: 40px; height: 40px; }
.pcap-kinds {
  display: grid; grid-template-columns: 1fr 1fr; gap: 4px;
  margin-bottom: 10px; padding: 3px; border-radius: 12px; background: var(--bg-hover);
}
.pcap-kind {
  display: flex; align-items: center; justify-content: center; gap: 6px;
  height: 40px; border: 0; border-radius: 9px; background: none; cursor: pointer;
  color: var(--text-light); font: inherit; font-size: 14px; font-weight: 500;
}
.pcap-kind[aria-pressed="true"] { background: var(--bg-modal); color: var(--text); box-shadow: var(--shadow-card); }
.pcap-text {
  display: block; width: 100%; box-sizing: border-box; min-height: 52px; resize: none;
  padding: 11px 12px; border: 1px solid var(--border); border-radius: 12px;
  background: var(--bg-input); color: var(--text);
  font: inherit; font-size: 16px; line-height: 1.45; text-align: start;
}
.pcap-text::placeholder { color: var(--text-placeholder); }
.pcap-text:focus-visible { outline: var(--ring-w) solid var(--accent); outline-offset: 1px; }
.pcap-due { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.pcap-due[hidden] { display: none; }
.pcap-chip {
  height: 34px; padding: 0 14px; border: 1px solid var(--border); border-radius: 999px;
  background: none; color: var(--text-light); font: inherit; font-size: 13.5px; cursor: pointer;
}
.pcap-chip[aria-pressed="true"] {
  border-color: var(--accent); color: var(--accent);
  background: color-mix(in srgb, var(--accent) 10%, transparent);
}
.pcap-hint { margin-top: 8px; font-size: 12.5px; color: var(--text-faint); }
.pcap-foot { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 12px; }
.pcap-alt {
  display: inline-flex; align-items: center; gap: 6px; min-width: 0;
  height: 44px; padding: 0 12px; border: 0; border-radius: 10px;
  background: none; color: var(--text-light); font: inherit; font-size: 14px; cursor: pointer;
}
.pcap-alt[hidden] { display: none; }
.pcap-alt:active, .pcap-kind:active, .pcap-chip:active { background: var(--bg-active); }
.pcap-save {
  margin-inline-start: auto; height: 44px; padding: 0 22px; border: 0; border-radius: 12px;
  background: var(--accent); color: var(--accent-foreground);
  font: inherit; font-size: 15px; font-weight: 600; cursor: pointer;
}
.pcap-save:disabled { opacity: .6; cursor: default; }
.pcap-sheet button:focus-visible { outline: var(--ring-w) solid var(--accent); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) {
  .pcap-layer, .pcap-sheet { transition: none; }
}
