/* ============================================================
   TrackerBloom — a warm, soft board that changes with the time of day.
   Palettes are swapped by [data-daypart] on <html>, set in JS.
   ============================================================ */

:root {
  /*
   * Tell the browser which way round we are.
   *
   * Without this, everything the BROWSER draws rather than the page stays
   * light-themed: the mm/dd/yyyy and --:-- text inside date and time fields,
   * the calendar and clock popups, a <select>'s dropdown list, checkbox and
   * radio glyphs, scrollbars, and autofilled fields. On the evening palette
   * that is black text on a dark card — unreadable, and on exactly the
   * controls used most, because the page's own `color` never reaches inside a
   * native widget.
   *
   * It is set per palette rather than from prefers-color-scheme, because the
   * palette is not always the phone's choice: "Shift through the day" turns
   * the app dark in the evening on a phone that is still in light mode.
   */
  color-scheme: light;
  /* The hour ruler down the timeline, and the ink on a filled accent button.
     Both need to move with the palette rather than being fixed: the evening
     accent is LIGHTER than the day one, so white on it washes out. */
  /*
   * --ink-soft and --ink-faint are set to clear WCAG AA (4.5:1 for body text)
   * against EVERY ground they land on — --card, --bg and --bg-2 — not just the
   * lightest one. Measured, not eyeballed:
   *
   *   day      --ink-faint #76675D → 5.34 / 4.93 / 4.53   (was #A9968B, 2.36 at worst)
   *            --ink-soft  #776759 → 5.34 / 4.93 / 4.53   (was #7A6A5C, 4.33 at worst)
   *   evening  --ink-faint #9D90A0 → 4.52 / 5.42 / 4.86   (was #8F8296, 3.78 at worst)
   *            --ink-soft  #BFB0BE → 6.63 / 7.95 / 7.14   (already clear)
   *
   * Darken further and the app stops looking like itself; these are the
   * lightest values in the same hue family that pass. Anything using them at
   * partial alpha is a NON-text thing and needs 3:1, which is why the two
   * color-mix uses are at 80% rather than 55%.
   */
  /* Measured: 65% of --ink-faint gave the hour ruler 1.77:1 in daylight and
     2.71:1 in the evening — visible, not readable. --ink-soft is the palette's
     own secondary ink and clears 4.6 in all three. */
  --tick-ink: var(--ink-soft);
  --on-accent: #fff;
  --bg:        #FBF3E7;
  --bg-2:      #F5E9D7;
  --card:      #FFFDF8;
  --ink:       #3B3028;
  --ink-soft:  #776759;
  --ink-faint: #76675D;
  --line:      #E8DAC6;
  /*
   * A shade deeper than the terracotta it started as (#C4703F), because white
   * on that measured 3.66:1 and every filled button in the app is white on
   * accent. Terracotta is a mid-tone: it cannot carry AA text in EITHER
   * direction, so the fill had to move. White on this is 4.55.
   */
  --accent:    #AB643B;   /* terracotta */
  --accent-2:  #7A9A6B;   /* sage */
  --accent-3:  #D9A441;   /* honey */
  /*
   * The accents as TEXT, which is a different job from filling a shape.
   * #AB643B reads 3.79 at worst on --bg-2; honey as text is 2.21. These clear
   * 4.5 on --card, --bg and --bg-2. Fills, borders, rings and the colour
   * swatches keep the accents themselves, so nothing changes shape or hue.
   */
  --accent-ink:   #975B37;
  --accent-3-ink: #846534;
  --danger:    #B4553F;
  --done:      #7A9A6B;
  --shadow:    0 2px 0 rgba(59,48,40,.06), 0 8px 24px -12px rgba(59,48,40,.28);
  --radius:    18px;
  --ease:      cubic-bezier(.22,1,.36,1);
}

html[data-daypart="morning"] {
  --bg: #FDF1E6; --bg-2: #F9E3D2; --card: #FFFCF7;
  --accent: #D2794A; --accent-2: #86A473; --line: #F0DCC9;
}

html[data-daypart="evening"] {
  color-scheme: dark;
  /* White on the evening accent measured 2.46:1 — it is a paler accent than
     the daytime one, so the ink on it has to go the other way. 6.22:1. The
     ruler needs no override: --ink-soft moves with the palette. */
  --on-accent: #2A2233;
  --bg:        #221D2B;
  --bg-2:      #2C2536;
  --card:      #322A3D;
  --ink:       #F2E9DC;
  --ink-soft:  #BFB0BE;
  --ink-faint: #9D90A0;
  --line:      #423854;
  --accent:    #E2926A;
  --accent-2:  #9BBE87;
  --accent-3:  #E7BC63;
  /* Light accents on dark grounds already clear AA (5.58 and 7.67 at worst),
     so evening's text inks are the accents unchanged. */
  --accent-ink:   #E2926A;
  --accent-3-ink: #E7BC63;
  --danger:    #E08A72;
  --done:      #9BBE87;
  --shadow:    0 2px 0 rgba(0,0,0,.18), 0 10px 28px -14px rgba(0,0,0,.7);
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: Nunito, ui-rounded, "SF Pro Rounded", system-ui, sans-serif;
  font-size: 16px;
  -webkit-text-size-adjust: 100%;
  overscroll-behavior-y: contain;
}

body {
  min-height: 100dvh;
  background-image:
    radial-gradient(1200px 500px at 12% -10%, var(--bg-2), transparent 60%),
    radial-gradient(900px 420px at 100% 0%, color-mix(in oklab, var(--accent) 12%, transparent), transparent 62%);
  background-attachment: fixed;
  transition: background-color .6s var(--ease), color .3s var(--ease);
}

h1, h2, .daylabel, .pick-card h2 {
  font-family: Fraunces, Georgia, serif;
  font-weight: 700;
  letter-spacing: -.01em;
}

/*
 * Off-screen until focused. Not display:none and not visibility:hidden —
 * either takes it out of the focus order entirely, which is the one thing it
 * must stay in.
 */
.skip-link {
  position: absolute; left: -9999px; top: 0;
  z-index: 100;
  background: var(--card); color: var(--ink);
  border: 1px solid var(--line); border-radius: 0 0 12px 0;
  padding: 12px 18px;
  font-weight: 800; font-size: .9rem;
  text-decoration: none;
}
/*
 * :focus, not only :focus-visible.
 *
 * focus-visible is a heuristic and does not match a programmatic .focus() —
 * so the link stayed off-screen in testing. It is reachable by keyboard and
 * by nothing else, so every focus is a keyboard focus and there is no case
 * where showing it is wrong.
 */
.skip-link:focus,
.skip-link:focus-visible {
  left: 0;
  outline: 2.5px solid var(--accent);
  outline-offset: 2px;
}
/* Focused by the skip link, so it must not draw a ring around the whole page. */
#main:focus { outline: none; }

.wrap {
  max-width: 760px;
  margin: 0 auto;
  padding: 18px 16px calc(72px + env(safe-area-inset-bottom));
}

.hidden { display: none !important; }

.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  /* Pinned to the left edge because a native date input ignores width:1px and
     reports its intrinsic width — sitting at its parent's content edge, that
     pushed the document 14px wider than the viewport and let the page jiggle
     sideways on a phone. Invisible either way; this stops it extending the
     scrollable area. */
  left: 0; top: 0;
  clip: rect(0 0 0 0); clip-path: inset(50%);
  overflow: hidden; white-space: nowrap;
}

/* ---------------- header ---------------- */

.mast {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-bottom: 18px;
}

.mast-left { display: flex; align-items: center; gap: 12px; flex: 1; min-width: 0; }
.mast-right { display: flex; align-items: center; gap: 8px; }


.greeting { min-width: 0; }
.greeting h1 { margin: 0; font-size: 1.5rem; line-height: 1.15; }
.greeting .sub { margin: 3px 0 0; color: var(--ink-soft); font-size: .92rem; }


/* Everything that grows starts hidden and scaled down at its own base. */




/* A droop at stage 0-1 that straightens as things get done. No history is
   involved — it reads today only, so yesterday can never make it wilt. */


@keyframes twinkle {
  0%, 100% { opacity: .25; transform: scale(.7); }
  50%      { opacity: 1;   transform: scale(1.1); }
}

/* ---------------- progress ring ---------------- */

.ring-holder { position: relative; width: 52px; height: 52px; display: grid; place-items: center; }
.ring { width: 52px; height: 52px; transform: rotate(-90deg); position: absolute; inset: 0; }
.ring-bg { fill: none; stroke: var(--line); stroke-width: 5; }
.ring-fg {
  fill: none; stroke: var(--accent-2); stroke-width: 5; stroke-linecap: round;
  stroke-dasharray: 119.4;
  stroke-dashoffset: 119.4;
  transition: stroke-dashoffset .55s var(--ease), stroke .3s var(--ease);
}
.ring-holder.complete .ring-fg { stroke: var(--accent-3); }
.ring-text {
  position: relative;
  font-size: .72rem; font-weight: 800; color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
}

/* ---------------- buttons & chips ---------------- */

button { font: inherit; color: inherit; cursor: pointer; }

.icon-btn {
  width: 38px; height: 38px;
  border: 1px solid var(--line);
  background: var(--card);
  border-radius: 12px;
  font-size: 1.1rem; line-height: 1;
  display: grid; place-items: center;
  box-shadow: var(--shadow);
  transition: transform .15s var(--ease), background .2s;
}
.icon-btn:active { transform: scale(.94); }

.tabs {
  display: flex; gap: 6px;
  background: color-mix(in oklab, var(--card) 70%, transparent);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 4px;
  margin-bottom: 16px;
}
.tab {
  flex: 1;
  border: 0; background: transparent;
  padding: 9px 8px;
  border-radius: 10px;
  font-weight: 700; font-size: .9rem;
  color: var(--ink-soft);
  transition: background .2s, color .2s;
}
.tab[aria-current="page"] {
  background: var(--card);
  color: var(--ink);
  box-shadow: var(--shadow);
}

/* The chore day/week/all switch. Deliberately lighter than .tabs — it's a
   switch within a section, not a section of its own. */
.subtabs {
  display: flex;
  gap: 4px;
  margin: -8px 0 14px;
  padding: 0 2px;
}
.subtab {
  border: 0;
  background: transparent;
  padding: 5px 12px;
  border-radius: 999px;
  font-size: .82rem;
  font-weight: 700;
  color: var(--ink-faint);
  transition: background .18s, color .18s;
}
.subtab:hover { color: var(--ink-soft); }
.subtab[aria-current="page"] {
  background: color-mix(in oklab, var(--accent) 16%, transparent);
  /* A selected sub-tab tints its OWN ground, which lifts it and left
     --accent-ink at 4.06:1. Measured against the composited tint, not the card
     underneath it — a translucent background is a colour over what is behind
     it, and treating it as opaque is how this read 2.56 at one point. */
  color: color-mix(in oklab, var(--accent-ink) 87%, var(--ink));
}

/*
 * A GRID, not a flex row.
 *
 * The arrows sit in fixed outer columns so they never move, whatever else is on
 * screen. As a flex row with a "Back to today" button on the end, that button
 * appearing pushed the next arrow ~195px left — right out from under the finger
 * of someone tapping it repeatedly to get three days ahead. Reported 2026-09-01.
 *
 * It also keeps the label genuinely centred, which the flex version did not:
 * the label centred in the space LEFT OVER after the back button, so it sat
 * off-centre on every day except today.
 */
.daybar {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 10px;
  margin-bottom: 12px;
}

/* Buttons that come and go, on their own line under the arrows. */
.daynav-extra {
  display: flex; align-items: center; gap: 8px;
  margin: -4px 0 12px;
}
.daynav-extra:empty { display: none; }

.daylabel {
  text-align: center;
  font-size: 1.05rem;
  border: 0; background: transparent;
  padding: 6px;
  border-radius: 10px;
}
.daylabel:hover { background: color-mix(in oklab, var(--card) 80%, transparent); }

.toolbar { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }

.chip {
  border: 1px solid var(--line);
  background: var(--card);
  border-radius: 999px;
  padding: 8px 14px;
  font-size: .87rem; font-weight: 700;
  color: var(--ink-soft);
  box-shadow: var(--shadow);
  transition: transform .15s var(--ease), background .2s, color .2s, border-color .2s;
  display: inline-flex; align-items: center; gap: 6px;
}
.chip:active { transform: scale(.95); }
.chip[aria-pressed="true"] {
  background: color-mix(in oklab, var(--accent-3) 24%, var(--card));
  border-color: color-mix(in oklab, var(--accent-3) 50%, var(--line));
  color: var(--ink);
}
.chip-add { color: var(--accent-ink); border-color: color-mix(in oklab, var(--accent) 34%, var(--line)); }
.chip-check { cursor: pointer; }
.chip-check input { accent-color: var(--accent); }

.ghost {
  border: 1px solid var(--line);
  background: transparent;
  border-radius: 10px;
  padding: 9px 14px;
  font-weight: 700; font-size: .88rem;
  color: var(--ink-soft);
}
.primary {
  border: 0;
  background: var(--accent);
  color: var(--on-accent);
  border-radius: 10px;
  padding: 10px 18px;
  font-weight: 800; font-size: .9rem;
  box-shadow: var(--shadow);
}
.primary:active { transform: scale(.97); }
.danger { color: var(--danger); border-color: color-mix(in oklab, var(--danger) 34%, var(--line)); }

.hint { color: var(--ink-faint); font-size: .84rem; margin: 0 0 10px; }

.bare-note {
  background: color-mix(in oklab, var(--accent-3) 16%, var(--card));
  border: 1px solid color-mix(in oklab, var(--accent-3) 34%, var(--line));
  border-radius: var(--radius);
  padding: 10px 14px;
  font-size: .88rem;
  color: var(--ink-soft);
  margin-bottom: 14px;
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
}
.bare-note button { border: 0; background: transparent; font-weight: 800; color: var(--accent-ink); text-decoration: underline; padding: 0; }

/* ---------------- touch targets ----------------
   Apple's floor is 44px; an audit found nearly every control under it, the
   28px checkbox and 21px step checkbox worst of all — and those are the two
   things tapped most in the whole app.

   The fix extends the HIT area, not the drawing. A transparent overlay grows
   the tappable region while the control keeps the size the design wants, so
   nothing gets visually chunkier. */

.box, .box-sm, .step-star, .more, .steps-toggle, .gmore, .tc-grip, .icon-btn, .swatch {
  position: relative;
}
.box::after, .box-sm::after, .step-star::after, .more::after,
.steps-toggle::after, .gmore::after, .icon-btn::after, .swatch::after {
  content: "";
  position: absolute;
  left: 50%; top: 50%;
  width: 44px; height: 44px;
  transform: translate(-50%, -50%);
  /* Behind the control so it never covers an icon or a tick. */
  z-index: -1;
}
/* The tick is drawn with ::after on a selected swatch, so that one gets its
   hit area from padding on the row instead. */
.swatch::after { display: none; }
.swatches { padding: 6px 0; }
.swatch { margin: 3px; }

/* Rows tall enough that a 44px target can't spill into its neighbour. */
.step { padding: 11px 0; }
.gitem { padding: 12px 13px; }

/* Explicit minimums for the pill-shaped controls: padding alone didn't get
   them there, and these are primary actions. */
.icon-btn { width: 42px; height: 42px; }
.tab, .chip, .ghost, .primary, .slot, .staple, .tgapcard, .daylabel { min-height: 44px; }
.tab, .chip, .ghost, .primary, .staple {
  display: inline-flex; align-items: center; justify-content: center;
}
.chip { justify-content: flex-start; }
/* The sub-nav is secondary and sits right under the tabs, so it stays lighter
   at 40 rather than pushing the whole header down. */
.subtab { min-height: 40px; padding: 8px 14px; }

/* ---------------- lists ---------------- */

.group { margin-bottom: 20px; }
.group-head {
  margin: 0 0 8px 2px;
  font-size: .78rem;
  font-family: Nunito, sans-serif;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--ink-faint);
  display: flex; align-items: center; gap: 6px;
}
.overdue .group-head { color: var(--accent-ink); }

.list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }

.chore {
  position: relative;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  overflow: hidden;
  touch-action: pan-y;
  transition: transform .18s var(--ease), opacity .3s, box-shadow .2s;
}

/* Revealed under the row as you swipe it to the right.
   pointer-events:none is REQUIRED, not cosmetic: this spans the whole row
   (steps included) and, being absolutely positioned, hit-tests above the
   in-flow step list. Without it every step checkbox is unclickable — the
   overlay is invisible at opacity 0 but still swallows the pointer. */
.chore::before {
  content: "✓";
  position: absolute; inset: 0;
  display: flex; align-items: center;
  padding-left: 22px;
  font-size: 1.3rem; font-weight: 800;
  color: #fff;
  background: var(--done);
  opacity: 0;
  pointer-events: none;
}
.chore.swiping::before { opacity: 1; }

.chore-inner {
  position: relative;
  display: flex; align-items: center; gap: 12px;
  padding: 13px 14px;
  background: var(--card);
  transition: transform .25s var(--ease);
}

.box {
  flex: none;
  width: 28px; height: 28px;
  border-radius: 9px;
  /* Full token: at 80% this measured 3.03:1, which clears the 3:1 a UI
     component's boundary needs and clears it by nothing. An unticked box is the
     main affordance on the whole screen; it can afford to be visible. */
  border: 2px solid var(--ink-faint);
  background: transparent;
  display: grid; place-items: center;
  padding: 0;
  transition: background .2s var(--ease), border-color .2s var(--ease), transform .2s var(--ease);
}
.box svg { width: 16px; height: 16px; }
.box path {
  stroke: #fff; stroke-width: 3.4; fill: none;
  stroke-linecap: round; stroke-linejoin: round;
  stroke-dasharray: 22; stroke-dashoffset: 22;
  transition: stroke-dashoffset .28s var(--ease) .05s;
}
/*
 * A ticked box is styled from its OWN aria-pressed, not from an ancestor's
 * "done" class. Per-context rules (.chore.done .box, .step.done .box-sm) meant
 * every new place a checkbox appeared started out looking unticked — the
 * timeline shipped with struck-through text next to an empty box.
 */
.box[aria-pressed="true"] { background: var(--done); border-color: var(--done); }
.box[aria-pressed="true"] path { stroke-dashoffset: 0; }

.chore-emoji { flex: none; font-size: 1.25rem; width: 1.6rem; text-align: center; }

.chore-text { flex: 1; min-width: 0; }
.chore-title {
  font-weight: 700;
  line-height: 1.25;
  position: relative;
  display: inline;
  background-image: linear-gradient(currentColor, currentColor);
  background-repeat: no-repeat;
  background-size: 0% 2px;
  background-position: 0 62%;
  transition: background-size .32s var(--ease), color .32s var(--ease);
}
.chore.done .chore-title { background-size: 100% 2px; color: var(--ink-faint); }
.chore.done .chore-emoji, .chore.done .chore-meta { opacity: .5; }

.chore-meta {
  display: flex; flex-wrap: wrap; gap: 8px;
  margin-top: 2px;
  font-size: .76rem; color: var(--ink-faint);
}
.chore-meta .tag {
  background: color-mix(in oklab, var(--accent-2) 16%, transparent);
  /* 78% sage read 3.59:1 on its own 16% sage pill; 60% clears at 4.54. */
  color: color-mix(in oklab, var(--accent-2) 60%, var(--ink));
  border-radius: 999px;
  padding: 1px 8px;
  font-weight: 700;
}
.chore-meta .from { color: var(--accent-ink); font-weight: 700; }

.chore .more {
  flex: none;
  border: 0; background: transparent;
  color: var(--ink-faint);
  font-size: 1.2rem; line-height: 1;
  padding: 6px 4px;
  border-radius: 8px;
}
.chore .more:hover { background: var(--bg-2); color: var(--ink); }

.chore.pop { animation: pop .42s var(--ease); }
@keyframes pop {
  0%   { transform: scale(1); }
  35%  { transform: scale(1.035); }
  100% { transform: scale(1); }
}

.chore.dragging {
  opacity: .92;
  box-shadow: 0 18px 36px -12px rgba(0,0,0,.4);
  transform: scale(1.03);
  z-index: 5;
}
.chore.drag-ghost { opacity: .28; }

.empty {
  text-align: center;
  padding: 34px 20px;
  color: var(--ink-soft);
}
.empty .big { font-size: 2.6rem; display: block; margin-bottom: 10px; }
.empty p { margin: 0; font-size: 1.02rem; }

/* ---------------- steps inside a chore ---------------- */

.steps-toggle {
  flex: none;
  display: inline-flex; align-items: center; gap: 4px;
  border: 1px solid var(--line);
  background: var(--bg);
  border-radius: 999px;
  padding: 4px 8px 4px 10px;
  color: var(--ink-soft);
  transition: background .18s, border-color .18s, color .18s;
}
.steps-toggle:hover {
  background: var(--bg-2);
  border-color: color-mix(in oklab, var(--accent) 40%, var(--line));
  color: var(--ink);
}
.steps-count {
  font-size: .74rem; font-weight: 800;
  font-variant-numeric: tabular-nums;
  letter-spacing: .01em;
}
.steps-toggle .chev {
  font-size: 1rem; line-height: 1;
  transform: rotate(90deg);
  transition: transform .25s var(--ease);
}
.chore.open .steps-toggle .chev { transform: rotate(-90deg); }

/* A finished group keeps its count legible rather than fading with the title. */
.chore.done .steps-toggle {
  border-color: color-mix(in oklab, var(--done) 45%, var(--line));
  color: color-mix(in oklab, var(--done) 75%, var(--ink));
}

/* Collapsed by default. Height animates via grid-template-rows, which — unlike
   max-height — needs no guessed pixel value, so it never clips a long list or
   leaves dead space under a short one. The wrapper holds exactly ONE child,
   because grid-template-rows only sizes the first row. */
.steps-wrap {
  position: relative;   /* keeps the steps above the swipe overlay */
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows .28s var(--ease);
}
.chore.open .steps-wrap { grid-template-rows: 1fr; }

.steps {
  list-style: none;
  margin: 0;
  padding: 0 14px 0 52px;
  /* overflow:hidden is what lets the 0fr track clip this. Do NOT add
     min-height:0 here — it zeroes the track's content contribution on some
     engines and the list expands to nothing. */
  overflow: hidden;
}
.chore.open .steps { padding-bottom: 12px; }

.step {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 0;
  border-top: 1px solid color-mix(in oklab, var(--line) 60%, transparent);
}
.step:first-child { border-top: 0; }

.box-sm {
  width: 21px; height: 21px;
  border-radius: 7px;
  border-width: 2px;
}
.box-sm svg { width: 12px; height: 12px; }
.box-sm path { stroke-width: 4; }

.step-star {
  flex: none;
  margin-left: auto;
  border: 0;
  background: transparent;
  /* The full token, not a fraction of it. At 80% this glyph measured 3.03:1 —
     enough for a UI icon under 1.4.11 and not enough to call AA clean, and it
     is the only marker of "essential". The pressed state is --accent-3, so it
     still reads as off. */
  color: var(--ink-faint);
  font-size: .95rem;
  line-height: 1;
  padding: 6px 2px 6px 8px;
  border-radius: 8px;
  transition: color .18s, transform .15s var(--ease);
}
.step-star:hover { color: var(--accent-3-ink); }
.step-star:active { transform: scale(.85); }
.step-star[aria-pressed="true"] { color: var(--accent-3-ink); }
.step.done .step-star { opacity: .5; }

.step-title {
  flex: 1;
  min-width: 0;
  font-size: .88rem;
  line-height: 1.3;
  background-image: linear-gradient(currentColor, currentColor);
  background-repeat: no-repeat;
  background-size: 0% 1.5px;
  background-position: 0 62%;
  transition: background-size .3s var(--ease), color .3s var(--ease);
}
.step.done .step-title { background-size: 100% 1.5px; color: var(--ink-faint); }


@media (max-width: 480px) {
  .steps { padding-left: 46px; }
  .step { padding: 9px 0; }        /* a comfortable thumb target */
}

/* ---------------- week ---------------- */

.week { display: flex; flex-direction: column; gap: 10px; }

.day-col {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 12px 14px;
  box-shadow: var(--shadow);
  transition: border-color .2s, background .2s;
}
.day-col.today { border-color: color-mix(in oklab, var(--accent) 55%, var(--line)); }
.day-col.drop-target {
  border-color: var(--accent-2);
  background: color-mix(in oklab, var(--accent-2) 12%, var(--card));
}
.day-head {
  display: flex; align-items: baseline; gap: 8px;
  margin-bottom: 8px;
  font-size: .82rem; font-weight: 800;
  text-transform: uppercase; letter-spacing: .07em;
  color: var(--ink-faint);
}
.day-col.today .day-head { color: var(--accent-ink); }
.day-head .count { margin-left: auto; font-weight: 700; letter-spacing: 0; text-transform: none; }
.day-items { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 5px; min-height: 12px; }
.wk-item {
  display: flex; align-items: center; gap: 8px;
  padding: 7px 10px;
  border-radius: 11px;
  background: var(--bg-2);
  font-size: .87rem; font-weight: 600;
  touch-action: pan-y;
  transition: transform .15s var(--ease), opacity .2s;
}
.wk-item.done { opacity: .45; text-decoration: line-through; }
.wk-item .wk-emoji { flex: none; }
.wk-item.dragging { box-shadow: 0 14px 28px -10px rgba(0,0,0,.45); transform: scale(1.04); z-index: 5; }
.day-col .none { color: var(--ink-faint); font-size: .82rem; font-style: italic; }

/*
 * Seven columns need more room than a reading-width column gives them, so a week
 * grid widens the page. Everything else stays narrow.
 *
 * Keyed on data-wide rather than the tab: this rule used to list
 * data-view="schedule", which widened the WHOLE Schedule tab — including its Day
 * view, a vertical timeline that wants reading width — so the Schedule sat 360px
 * wider than Chores and Shop. paintPageWidth() sets the flag from the actual
 * layout on screen, not from which tab you are on.
 */
@media (min-width: 1000px) {
  body[data-wide="1"] .wrap { max-width: 1120px; }
}

@media (min-width: 720px) {
  .week { display: grid; grid-template-columns: repeat(7, 1fr); gap: 8px; align-items: start; }
  /* Grid items default to min-width:auto, so a long chore title would push the
     seventh column off the edge. These three let the columns actually shrink. */
  .day-col { padding: 10px 8px; min-width: 0; }
  .day-items { min-width: 0; }
  .day-head { flex-direction: column; gap: 0; align-items: flex-start; font-size: .68rem; }
  .day-head .count { margin-left: 0; }
  .wk-item { font-size: .76rem; padding: 6px 7px; gap: 5px; min-width: 0; }
  .wk-item .wk-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
}

/* ---------------- all chores ---------------- */

.all-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.all-item {
  display: flex; align-items: center; gap: 12px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 12px 14px;
  box-shadow: var(--shadow);
}
.all-item.archived { opacity: .55; }
.all-item .grow { flex: 1; min-width: 0; }
.all-item .sched { font-size: .78rem; color: var(--ink-faint); margin-top: 2px; }
.all-item .star { color: var(--accent-3-ink); }

/* ---------------- schedule ---------------- */

:root {
  --c-sage:       #7A9A6B;
  --c-terracotta: #C4703F;
  --c-honey:      #D9A441;
  --c-plum:       #8A6E9E;
  --c-sky:        #5E8CA8;
  --c-rose:       #C4737F;
  --c-moss:       #5E8055;
  --c-clay:       #A9764F;
}

/* ---------------- the timeline ----------------
   A rail with a node per item, in the same card language as the chore list.
   Deliberately NOT a pixel-proportional grid: an hour of nothing shouldn't
   cost an inch of screen, and 24 hours of empty rows is what made the old
   version a chore to read. Duration is stated, not drawn. */

.timeline { position: relative; padding-left: 4px; }

.trow {
  position: relative;
  display: grid;
  grid-template-columns: 46px 1fr;
  gap: 10px;
  padding-bottom: 8px;
}

/* the rail, drawn behind each row so it joins up continuously */
.trow::before {
  content: "";
  position: absolute;
  left: 21px; top: 26px; bottom: -4px;
  width: 2px;
  background: var(--line);
}
.trow:last-child::before { display: none; }

/*
 * Things happening at the same time, side by side.
 *
 * The group owns the rail and the node — the node shows how many things share
 * the moment — and the cards split the remaining width evenly. `flex: 1 1 0`
 * with min-width 0 is what makes it an exact half, third or quarter rather than
 * content-sized columns that drift.
 *
 * Members stay whole .trow elements so every handler keeps working; they just
 * give up their own rail and node to the group, and their sub-item list moves
 * to the only column they have left.
 */
.trow-group {
  position: relative;
  display: grid;
  grid-template-columns: 46px 1fr;
  gap: 10px;
  padding-bottom: 8px;
}
.trow-group::before {
  content: "";
  position: absolute;
  left: 21px; top: 26px; bottom: -4px;
  width: 2px;
  background: var(--line);
}
.trow-group:last-child::before { display: none; }
.trow-group > .tnode {
  font-family: var(--font-ui);
  font-size: 1rem; font-weight: 800;
  font-variant-numeric: tabular-nums;
  color: var(--ink-faint);
}

/*
 * A concurrency group, laid out like a calendar: columns across, time down.
 *
 * Rows are fifteen minutes each — the grid everything already snaps to — so
 * height means duration. A 45-minute block is three rows tall and a 15-minute
 * block is one, which is what makes a quarter-hour chore look like a quarter of
 * a three-quarter-hour one instead of its twin.
 *
 * minmax(slot, auto) rather than a fixed row: at rest every card fits its slot
 * and the proportions are exact, but opening a card's sub-items lets that row
 * grow instead of the list spilling out of a box it cannot leave.
 *
 * The timeline as a whole is still NOT pixel-proportional — an empty afternoon
 * must not cost half a screen of scrolling. This applies only inside a group,
 * where the span is short by definition and proportion is the whole point.
 */
.tconc {
  --slot: 38px;
  display: grid;
  grid-template-columns: repeat(var(--cols, 2), minmax(0, 1fr));
  /* Fixed, not minmax: a row that can grow is a row whose height stops meaning
     duration the moment anything inside it needs more space. */
  grid-auto-rows: var(--slot, 34px);
  column-gap: 8px;
  /* No row gap: any constant between slots makes a 45-minute block 3.1 times a
     15-minute one instead of exactly 3. Cards meet on their borders, which is
     what a calendar does anyway. */
  row-gap: 0;
  min-width: 0;
}
.trow.member {
  position: relative;
  display: grid;
  grid-template-columns: 1fr;
  /* ONE row, all of it the card. The sub-item list is taken out of the flow
     below, so a card with a checklist is exactly as tall as one without —
     both are simply as tall as their duration. */
  grid-template-rows: 1fr;
  min-width: 0;
  gap: 0;
  padding-bottom: 0;
}
.trow.member::before { display: none; }
.trow.member > .tnode { display: none; }
/*
 * No inline list inside a group.
 *
 * A card's height here IS its duration, so a checklist has nowhere to push and
 * nothing to push into. Tapping the count opens the block's sheet, which has
 * the same list with room to show it. Solo rows keep expanding in place, where
 * height is free.
 */
.trow.member > .steps-wrap { display: none; }

/* Fill the slots it was given, so the height carries the meaning. */
.trow.member > .tcard {
  height: 100%;
  align-items: flex-start;
  align-content: flex-start;
  overflow: hidden;
}

/*
 * Narrow columns.
 *
 * The checkbox stays BESIDE the text rather than above it. Wrapping it onto its
 * own line cost a whole line of height, and half an hour is only 68px — the
 * card then could not fit a box, a time and a name, and clipped the name.
 */
.trow.member .tcard { padding: 6px 8px 6px 9px; gap: 7px; }
/*
 * The drag handle stays — it was hidden here and that took sliding away
 * entirely for anything sharing a time, which is the one place you most want
 * to rearrange.
 *
 * Out of the flow, so it costs the text no width, and it keeps the
 * touch-action: none from the base rule. That declaration is what makes
 * dragging work with a finger at all: the row claims pan-y for scrolling, so
 * the handle has to claim the gesture for itself.
 */
.trow.member .tc-grip {
  position: absolute;
  right: 1px; bottom: 1px;
  width: 30px;
  min-height: 30px;
  align-self: auto;
  z-index: 3;
  opacity: .75;
}
.trow.member.brief .tc-grip {
  top: 50%; bottom: auto;
  transform: translateY(-50%);
  width: 26px; min-height: 26px;
}
/* Keep the name clear of it. The handle sits bottom-right, so the text needs
   that corner reserved or a second line runs underneath it. */
.trow.member .tc-body { padding-right: 26px; }
.trow.member.brief .tc-body { padding-right: 24px; }
.trow.member .tc-when { font-size: .68rem; }
/* The height already says how long it is — printing it again costs the width
   that the name needs, and a wrapped name is what overflowed the card. */
.trow.member .tc-dur { display: none; }
/* Pills and badges do not fit a column this narrow, and they were what tipped
   a card with sub-items over its own height. */
.trow.member .tc-meta { display: none; }
/* No forced hyphenation: with the chip out of the flow there is room for real
   words, and breaking one mid-syllable was worse than wrapping it. */
.trow.member .tc-title { overflow-wrap: break-word; font-size: .9rem; line-height: 1.2; }
.trow.member .box { flex: 0 0 auto; transform: scale(.85); }
/*
 * The sub-items button sits in the corner rather than in the row.
 *
 * As a flex child it took width from the text column, and a title with nowhere
 * left to go broke mid-word — "Standup" came out as "Standu / p". Out of flow
 * it costs neither width nor height.
 */
.trow.member .steps-toggle {
  position: absolute;
  top: 5px; right: 6px;
  z-index: 2;
  padding: 1px 5px;
  font-size: .68rem;
  gap: 2px;
}
.trow.member .steps-toggle .steps-count { font-size: .68rem; }
/* Keep the start time clear of it. */
.trow.member.has-steps .tc-when { padding-right: 42px; }

/*
 * One slot — fifteen minutes. There is no room for a duration line as well as
 * a name, so the name wins and the time moves onto the same line as the box.
 */
.trow.member.brief .tcard { padding: 3px 6px 3px 8px; gap: 5px; align-items: center; align-content: center; }
.trow.member.brief .tc-when { display: none; }
/* One line, and an ellipsis rather than a name sliced in half. */
.trow.member.brief .tc-title {
  font-size: .8rem;
  line-height: 1.1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.trow.member.brief .tc-body { min-width: 0; }
.trow.member.brief .tc-meta { display: none; }
.trow.member.brief .box { transform: scale(.75); }

/* The card you are about to join, while dragging over its middle. */
.trow.drop-same > .tcard,
.trow.drop-same .tcard {
  outline: 2px dashed var(--block, var(--ink-faint));
  outline-offset: 2px;
}

/*
 * Sub-items, inline under the card.
 *
 * .trow is a two-column grid (rail, card), so the list goes in as a THIRD child
 * pinned to column 2. That puts it under the card without touching .tcard,
 * which is a centred flex row and would have to become a column to hold it —
 * a change that would have rippled into the drag clone and the swipe overlay.
 *
 * The collapse reuses the .steps-wrap grid-template-rows trick from the Chores
 * list, keyed off .open on the row.
 */
.trow > .steps-wrap { grid-column: 2; }
.trow.open > .steps-wrap { grid-template-rows: 1fr; }
/* NO vertical padding: a 0fr track still renders the child's padding, so any
   padding-block here leaves the list 8px tall when it is meant to be shut. The
   rows carry their own spacing. Same reason .steps uses `padding: 0 … 0 …`. */
.steps.tsteps { padding: 0 10px 0 14px; }
.trow .steps-toggle { flex: 0 0 auto; }
.trow.gap::before { background: none; border-left: 2px dashed var(--line); left: 20px; }

.tnode {
  position: relative;
  z-index: 2;
  width: 44px; height: 44px;
  border-radius: 50%;
  display: grid; place-items: center;
  font-size: 1.15rem;
  background: var(--card);
  border: 2px solid var(--block, var(--line));
  box-shadow: var(--shadow);
}
.trow.is-done .tnode {
  background: var(--done);
  border-color: var(--done);
  color: var(--on-accent);
}
.trow.is-protected .tnode {
  border-style: dashed;
  border-color: var(--c-plum);
}

.tcard {
  background: var(--card);
  border: 1px solid var(--line);
  border-left: 4px solid var(--block, var(--line));
  border-radius: 14px;
  box-shadow: var(--shadow);
  padding: 8px 4px 8px 11px;
  display: flex; align-items: center; gap: 8px;
  min-width: 0;
  transition: box-shadow .18s, opacity .25s var(--ease), transform .15s var(--ease);
}
.tcard .tc-body { flex: 1; min-width: 0; }
.tcard .tc-when {
  font-size: .72rem; font-weight: 800;
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
  letter-spacing: .01em;
}
.tcard .tc-title {
  display: block;
  font-weight: 700;
  line-height: 1.25;
  background-image: linear-gradient(currentColor, currentColor);
  background-repeat: no-repeat;
  background-size: 0% 2px;
  background-position: 0 62%;
  transition: background-size .3s var(--ease), color .3s var(--ease);
}
.trow.is-done .tc-title { background-size: 100% 2px; color: var(--ink-faint); }
.trow.is-done .tcard { opacity: .62; }
.tcard .tc-meta {
  display: flex; flex-wrap: wrap; gap: 7px;
  margin-top: 2px;
  font-size: .74rem; color: var(--ink-faint);
}
.tcard .tc-meta .pill {
  background: color-mix(in oklab, var(--block, var(--accent-2)) 16%, transparent);
  color: color-mix(in oklab, var(--block, var(--accent-2)) 76%, var(--ink));
  border-radius: 999px; padding: 1px 8px; font-weight: 800;
  font-variant-numeric: tabular-nums;
}

/* One tap to tick a chore straight off the timeline. */
.tcard .box { flex: none; }
.tcard .tc-grip {
  flex: none;
  width: 40px;
  align-self: stretch;
  min-height: 44px;
  border: 0;
  background: transparent;
  border-radius: 10px;
  display: grid;
  place-items: center;
  /* THE fix for drag: this handle owns the gesture. With touch-action:pan-y the
     browser claimed every vertical move and cancelled the drag before it could
     start — which is why dragging a block never worked with a finger. */
  touch-action: none;
  cursor: grab;
  transition: background .18s;
}
.tcard .tc-grip svg { width: 10px; height: 16px; }
.tcard .tc-grip circle { fill: var(--ink-faint); transition: fill .18s; }
.tcard .tc-grip:hover { background: var(--bg-2); }
.tcard .tc-grip:hover circle { fill: var(--ink); }
.tcard .tc-grip:active { cursor: grabbing; background: var(--bg-2); }
.trow.lifted .tc-grip circle { fill: var(--accent); }

.tc-meta .ranover {
  color: var(--ink-faint);
  font-style: italic;
}

/* Free time. The one place with real height, so you can slide to a time
   inside it rather than only picking the start. */
.trow.gap .tgapcard {
  position: relative;
  border: 1px dashed var(--line);
  background: transparent;
  border-radius: 12px;
  padding: 9px 12px;
  font-size: .78rem; font-weight: 700;
  color: var(--ink-faint);
  text-align: left;
  width: 100%;
  overflow: hidden;
  transition: border-color .18s, color .18s, background .18s;
}
.trow.gap .tgapcard:hover {
  border-style: solid;
  border-color: color-mix(in oklab, var(--accent) 40%, var(--line));
  color: var(--accent-ink);
  background: var(--card);
}
.tgap-label { display: block; }
.tgap-hint {
  display: block;
  margin-top: 2px;
  font-size: .7rem; font-weight: 600;
  opacity: .75;
}

/* Hour marks, so sliding has something to read against. Confined to the right
   half: full-width ticks landed on top of the "free" label. */
.ttick {
  position: absolute;
  left: 52%; right: 10px;
  text-align: right;
  font-size: .6rem; font-weight: 800;
  /* A token, so the evening palette can lift it. 65% of an already-faint ink
     measured 1.28:1 against the dark ground — present, but not readable. */
  color: var(--tick-ink);
  border-top: 1px dashed color-mix(in oklab, var(--line) 85%, transparent);
  padding-top: 1px;
  pointer-events: none;
}

/* While dragging over a gap: shade EXACTLY the slice the block will occupy,
   so the landing spot is a visible region rather than a point to interpret. */
.trow.gap.targeted .tgapcard {
  border-style: solid;
  border-color: var(--accent);
  background: var(--card);
  color: var(--accent-ink);
}
.trow.gap.targeted .tgapcard::after {
  content: attr(data-mark);
  position: absolute;
  left: 4px; right: 4px;
  top: var(--mark, 0%);
  height: var(--span, 20%);
  min-height: 22px;
  border-radius: 9px;
  background: color-mix(in oklab, var(--accent) 88%, transparent);
  color: var(--on-accent);
  font-size: .7rem; font-weight: 800;
  font-variant-numeric: tabular-nums;
  display: flex; align-items: center;
  padding-left: 9px;
  box-shadow: 0 2px 10px -2px rgba(0,0,0,.35);
}

/* The time rides on the card you're holding — the one thing guaranteed to be
   where you're looking. */
.tstamp {
  position: absolute;
  top: -11px; left: 10px;
  background: var(--ink);
  color: var(--bg);
  border-radius: 999px;
  padding: 3px 10px;
  font-size: .7rem; font-weight: 800;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  box-shadow: 0 3px 10px -2px rgba(0,0,0,.45);
}
.tstamp[hidden] { display: none; }
.tdragclone, .tchip.dragging { position: fixed; }

.trow.gap .tnode {
  width: 44px; height: 20px;
  border: 0; background: none; box-shadow: none;
  font-size: .62rem; font-weight: 800; color: var(--ink-faint);
  align-self: start;
}

/* the sequence marker gains a label when it means "an exact time" */
.tinsert.at-time {
  height: auto;
  background: var(--accent);
  color: var(--on-accent);
  border-radius: 999px;
  padding: 3px 10px;
  font-size: .72rem; font-weight: 800;
  width: max-content;
}

/* now */
.trow.now .tnode {
  border-color: var(--accent);
  box-shadow: 0 0 0 4px color-mix(in oklab, var(--accent) 22%, transparent), var(--shadow);
}
.tnow-label {
  display: inline-block;
  margin-left: 6px;
  font-size: .64rem; font-weight: 800;
  text-transform: uppercase; letter-spacing: .07em;
  color: var(--accent-ink);
}

.trow.overdue .tcard { border-color: color-mix(in oklab, var(--accent) 50%, var(--line)); }
.trow.asleep .tcard { opacity: .7; }

/* dragging a card up or down the sequence */
.trow.lifted { opacity: .35; }
.tdragclone {
  position: fixed;
  z-index: 900;
  pointer-events: none;
  box-shadow: 0 18px 36px -10px rgba(0,0,0,.5);
  transform: scale(1.03);
  border-radius: 14px;
}
.tinsert {
  height: 3px;
  margin: 2px 0 2px 56px;
  border-radius: 3px;
  background: var(--accent);
}

.usual-note {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin: 4px 0 0;
  padding: 9px 12px;
  border-radius: 11px;
  background: color-mix(in oklab, var(--accent-2) 14%, transparent);
  font-size: .8rem; font-weight: 600;
  color: color-mix(in oklab, var(--accent-2) 76%, var(--ink));
}
.usual-note button {
  border: 0; background: transparent; padding: 0;
  font-weight: 800; color: var(--accent-ink);
  text-decoration: underline;
}
.field-hint {
  display: block;
  margin-top: 4px;
  font-style: normal;
  font-size: .76rem; font-weight: 600;
  color: var(--ink-faint);
  line-height: 1.35;
}
/* A chore that shows up on its own gets a quiet marker. */
.trow.usual .tnode::after {
  content: "";
  position: absolute;
  right: -1px; bottom: -1px;
  width: 12px; height: 12px;
  border-radius: 50%;
  background: var(--card);
  border: 2px solid var(--block, var(--line));
}

/* ---------------- placing something by tapping ---------------- */

.place-exact { align-items: flex-end; margin-bottom: 6px; }
.place-exact .primary { flex: none; height: 44px; border-radius: 12px; }

.slot-group { margin-bottom: 12px; }
.slot-group h3 {
  margin: 0 0 6px;
  font-family: Nunito, sans-serif;
  font-size: .66rem; font-weight: 800;
  text-transform: uppercase; letter-spacing: .07em;
  color: var(--ink-faint);
}
.slot-grid { display: flex; flex-wrap: wrap; gap: 6px; }
.slot {
  border: 1px solid var(--line);
  background: var(--bg);
  border-radius: 999px;
  padding: 8px 13px;
  font-size: .82rem; font-weight: 700;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  min-height: 40px;
  transition: background .15s, border-color .15s, transform .12s var(--ease);
}
.slot:hover { background: var(--card); border-color: color-mix(in oklab, var(--accent) 45%, var(--line)); }
.slot:active { transform: scale(.94); }
#place-slots { max-height: 46vh; overflow-y: auto; }

/* ---------------- icon picker & suggestions ---------------- */

.emoji-btn {
  flex: none;
  width: 52px; height: 52px;
  border: 1px solid var(--line);
  background: var(--bg);
  border-radius: 14px;
  font-size: 1.5rem; line-height: 1;
  display: grid; place-items: center;
  transition: border-color .18s, transform .15s var(--ease);
}
.emoji-btn:hover { border-color: color-mix(in oklab, var(--accent) 40%, var(--line)); }
.emoji-btn:active { transform: scale(.94); }
.emoji-btn.empty { color: var(--ink-faint); font-size: 1.1rem; }
.emoji-btn:disabled { opacity: .5; cursor: default; }
/* The icon button sits beside a labelled field, so nudge it off the label. */
.field-row > .emoji-btn { margin-top: 20px; }

.emoji-groups { max-height: 54vh; overflow-y: auto; }
.emoji-group { margin-bottom: 14px; }
.emoji-group h3 {
  margin: 0 0 6px;
  font-family: Nunito, sans-serif;
  font-size: .68rem; font-weight: 800;
  text-transform: uppercase; letter-spacing: .07em;
  color: var(--ink-faint);
}
.emoji-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(46px, 1fr)); gap: 5px; }
.emoji-cell {
  aspect-ratio: 1;
  border: 1px solid transparent;
  background: var(--bg);
  border-radius: 11px;
  font-size: 1.35rem; line-height: 1;
  transition: background .15s, transform .12s var(--ease), border-color .15s;
}
.emoji-cell:hover { background: var(--card); border-color: var(--line); }
.emoji-cell:active { transform: scale(.88); }
.emoji-cell[aria-pressed="true"] { border-color: var(--accent); background: var(--card); }

/* the "we guessed this" chip */
.suggestion {
  display: inline-flex; align-items: center; gap: 8px;
  margin: -4px 0 12px;
  border: 1px dashed color-mix(in oklab, var(--accent) 45%, var(--line));
  background: color-mix(in oklab, var(--accent) 7%, transparent);
  border-radius: 999px;
  padding: 6px 14px 6px 10px;
  font-size: .82rem; font-weight: 700;
  color: var(--accent-ink);
  transition: background .18s, transform .15s var(--ease);
}
.suggestion:hover { background: color-mix(in oklab, var(--accent) 14%, transparent); }
.suggestion:active { transform: scale(.96); }
.sug-emoji { font-size: 1.05rem; }
.sug-dot { width: 14px; height: 14px; border-radius: 50%; background: var(--sug, var(--c-sage)); }

.swatches.compact { gap: 7px; margin-bottom: 12px; }
.swatches.compact .swatch { width: 28px; height: 28px; }
.swatches.compact .swatch[aria-pressed="true"]::after { font-size: .78rem; }

/* fold the optional half of the editor away */
.more-toggle {
  display: inline-flex; align-items: center; gap: 6px;
  border: 0; background: transparent;
  padding: 6px 2px;
  font-size: .84rem; font-weight: 800;
  color: var(--ink-soft);
}
/* What is inside the fold, so the fold is worth opening. Lighter than the
   label and wrapping on a narrow screen rather than pushing the chevron. */
.more-toggle em {
  font-style: normal; font-weight: 600; font-size: .78rem;
  color: var(--ink-faint);
}

/* The one-line way in to Steps, out in front of the fold. */
.pre-more { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; margin: 4px 0 10px; }
.pre-more-why { font-size: .78rem; color: var(--ink-faint); }

.more-toggle .chev { transition: transform .2s var(--ease); display: inline-block; }
.more-toggle[aria-expanded="true"] .chev { transform: rotate(90deg); }
.more-toggle:hover { color: var(--ink); }
.more { padding-top: 6px; }

/* Colour shows on the chore row itself, so a list reads as groups. */
.chore[data-color] { border-left: 4px solid var(--rowc, var(--line)); }
.all-item[data-color] { border-left: 4px solid var(--rowc, var(--line)); }
.wk-item[data-color] { border-left: 3px solid var(--rowc, var(--line)); }

/* ---------------- colour picker ---------------- */

.swatches { display: flex; flex-wrap: wrap; gap: 9px; margin-bottom: 6px; }
.swatch {
  position: relative;
  width: 34px; height: 34px;
  border-radius: 50%;
  border: 2px solid transparent;
  background: var(--sw, var(--line));
  transition: transform .15s var(--ease), box-shadow .18s;
}
/* A ring AND a tick: a border alone was too subtle to tell which was chosen. */
.swatch[aria-pressed="true"] {
  box-shadow: 0 0 0 3px var(--card), 0 0 0 5px var(--ink);
  transform: scale(1.06);
}
.swatch[aria-pressed="true"]::after {
  content: "✓";
  position: absolute; inset: 0;
  display: grid; place-items: center;
  color: #fff;
  font-size: .9rem; font-weight: 800;
  text-shadow: 0 1px 2px rgba(0,0,0,.35);
}
.swatch:active { transform: scale(.92); }

/* ---------------- the schedule week ---------------- */

.week-sched { display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px; }
.wsday {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 8px 6px;
  box-shadow: var(--shadow);
  min-width: 0;
}
.wsday.today { border-color: color-mix(in oklab, var(--accent) 55%, var(--line)); }
.wsday h3 {
  margin: 0 0 6px;
  font-family: Nunito, sans-serif;
  font-size: .66rem; font-weight: 800;
  text-transform: uppercase; letter-spacing: .06em;
  color: var(--ink-faint);
}
.wsday.today h3 { color: var(--accent-ink); }
.wsblocks { display: flex; flex-direction: column; gap: 4px; }
.wsblock {
  border-radius: 8px;
  padding: 4px 6px;
  font-size: .66rem; font-weight: 700;
  color: #fff;
  background: var(--block, var(--c-sage));
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.wsblock.done { opacity: .5; text-decoration: line-through; }
.wsday .none { color: var(--ink-faint); font-size: .68rem; font-style: italic; }

@media (max-width: 719px) {
  .week-sched { grid-template-columns: 1fr; }
  .wsday { padding: 10px 12px; }
  .wsblocks { flex-direction: row; flex-wrap: wrap; }
  .wsblock { font-size: .74rem; }
}

/* ---------------- my day (settings) ---------------- */

.prot-list { list-style: none; margin: 8px 0 10px; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.prot-list li {
  display: flex; align-items: center; gap: 10px;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 11px;
  padding: 9px 11px;
  font-size: .86rem; font-weight: 600;
}
.prot-list .pwhen { margin-left: auto; font-size: .76rem; color: var(--ink-faint); font-weight: 700; }
.prot-list button {
  border: 0; background: transparent; color: var(--danger);
  font-size: 1.05rem; line-height: 1; padding: 2px 4px; border-radius: 6px;
}
.settings-group {
  margin: 20px 0 8px;
  font-family: Nunito, sans-serif;
  font-size: .74rem; font-weight: 800;
  text-transform: uppercase; letter-spacing: .08em;
  color: var(--ink-faint);
}

/* ---------------- the tray of unplaced chores ---------------- */

.tray { margin-bottom: 14px; }
.tray-hint { margin: -4px 0 8px; }
.tray-chips {
  display: flex;
  gap: 7px;
  overflow-x: auto;
  padding: 2px 2px 6px;
  scrollbar-width: thin;
  /* Horizontal panning belongs to the strip; the long press takes over for a
     drag, so a vertical swipe still scrolls the page. */
  touch-action: pan-x;
}
.tchip {
  flex: none;
  display: inline-flex; align-items: center; gap: 6px;
  border: 1px solid var(--line);
  background: var(--card);
  border-radius: 999px;
  padding: 8px 13px;
  font-size: .84rem; font-weight: 700;
  color: var(--ink);
  box-shadow: var(--shadow);
  white-space: nowrap;
  touch-action: pan-x;
  transition: transform .15s var(--ease), border-color .18s;
}
.tchip:active { transform: scale(.95); }
.tchip .tc-mins { font-size: .72rem; font-weight: 800; color: var(--ink-faint); }
.tchip .tc-star { color: var(--accent-3); font-size: .72rem; }
.tchip.is-essential { border-color: color-mix(in oklab, var(--accent-3) 50%, var(--line)); }
.tchip.dragging {
  position: fixed;
  z-index: 900;
  pointer-events: none;
  box-shadow: 0 16px 32px -10px rgba(0,0,0,.5);
  transform: scale(1.06);
}

/* Where a chore dragged out of the tray would land. */
.tdrop {
  display: flex; align-items: center;
  gap: 8px;
  margin: 4px 0 4px 56px;
  border-radius: 12px;
  border: 2px dashed var(--accent);
  background: color-mix(in oklab, var(--accent) 12%, transparent);
  padding: 8px 12px;
  font-size: .76rem; font-weight: 800;
  color: var(--accent-ink);
  pointer-events: none;
}
.timeline.drop-active { background: color-mix(in oklab, var(--accent) 4%, transparent); border-radius: 14px; }

/* ---------------- a chore block's sheet ---------------- */

.cb-head { display: flex; align-items: flex-start; gap: 12px; margin-bottom: 14px; }
.cb-head .grow { flex: 1; min-width: 0; }
.cb-head h2 { margin: 0; }
.cb-head .sub { margin: 2px 0 0; }
.cb-head .box { flex: none; margin-top: 4px; }
#chorblock.done-state .cb-head h2 { text-decoration: line-through; color: var(--ink-faint); }

/* The step list here is always open — seeing the steps is the point of
   opening this sheet, so it gets no collapse. */
.cb-steps {
  padding: 0;
  margin: 4px 0 14px;
  max-height: 46vh;
  overflow-y: auto;
}
.cb-steps .step { padding: 9px 0; }

/* ---------------- shop ---------------- */

.add-row { display: flex; gap: 8px; margin-bottom: 10px; }
.add-row input {
  flex: 1;
  font-size: 1rem;          /* 16px+ or iOS zooms the page on focus */
  padding: 12px 14px;
  border-radius: 14px;
}
.add-row button { flex: none; width: 48px; font-size: 1.2rem; padding: 0; }

.shop-section { margin-bottom: 18px; }
.shop-section .group-head { justify-content: flex-start; }
.shop-section .count { margin-left: auto; font-weight: 700; letter-spacing: 0; text-transform: none; }

.shop-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }

.gitem {
  display: flex; align-items: center; gap: 12px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 11px 13px;
  box-shadow: var(--shadow);
  transition: opacity .25s var(--ease), transform .15s var(--ease);
}
.gitem .gname {
  flex: 1; min-width: 0;
  font-weight: 600;
  background-image: linear-gradient(currentColor, currentColor);
  background-repeat: no-repeat;
  background-size: 0% 1.5px;
  background-position: 0 62%;
  transition: background-size .3s var(--ease), color .3s var(--ease);
}
.gitem.done .gname { background-size: 100% 1.5px; color: var(--ink-faint); }
.gitem.done { opacity: .6; }
.gitem .gqty {
  flex: none;
  font-size: .78rem; font-weight: 800;
  color: var(--accent-ink);
  background: color-mix(in oklab, var(--accent) 14%, transparent);
  border-radius: 999px;
  padding: 2px 9px;
  font-variant-numeric: tabular-nums;
}
.gitem .gmore {
  flex: none; border: 0; background: transparent;
  color: var(--ink-faint); font-size: 1.15rem; line-height: 1;
  padding: 6px 4px; border-radius: 8px;
}
.gitem .gmore:hover { background: var(--bg-2); color: var(--ink); }

.staples { margin: 22px 0 6px; }
.staple-chips { display: flex; flex-wrap: wrap; gap: 7px; }
.staple {
  border: 1px dashed var(--line);
  background: transparent;
  border-radius: 999px;
  padding: 7px 13px;
  font-size: .85rem; font-weight: 700;
  color: var(--ink-soft);
  transition: background .18s, border-color .18s, color .18s, transform .15s var(--ease);
}
.staple:hover {
  background: var(--card);
  border-style: solid;
  border-color: color-mix(in oklab, var(--accent) 40%, var(--line));
  color: var(--ink);
}
.staple:active { transform: scale(.94); }

.shop-actions {
  position: sticky;
  bottom: calc(12px + env(safe-area-inset-bottom));
  display: flex; align-items: center; gap: 12px;
  margin-top: 20px;
  padding: 10px;
  border-radius: 999px;
  background: color-mix(in oklab, var(--card) 92%, transparent);
  border: 1px solid var(--line);
  box-shadow: var(--shadow);
  backdrop-filter: blur(8px);
}
.shop-actions .primary { border-radius: 999px; }
.shop-count { font-size: .85rem; font-weight: 700; color: var(--ink-soft); }

/* ---------------- dialogs ---------------- */

.sheet {
  border: 0;
  padding: 0;
  background: transparent;
  max-width: 520px;
  width: calc(100% - 24px);
  margin: auto;
}
.sheet::backdrop { background: rgba(30,22,18,.45); backdrop-filter: blur(3px); }
.sheet > form, .sheet > *:not(form) { }
.sheet form, .sheet:not(:has(form)) {
  background: var(--card);
  border-radius: 22px;
  padding: 20px;
  box-shadow: 0 24px 60px -20px rgba(0,0,0,.5);
}
.sheet form, .sheet.compact, .sheet.pick { max-width: 100%; }
.sheet.compact, .sheet.pick {
  background: var(--card);
  border-radius: 22px;
  padding: 20px;
  box-shadow: 0 24px 60px -20px rgba(0,0,0,.5);
}
.sheet h2 { margin: 0 0 4px; font-size: 1.25rem; }
.sheet .sub { margin: 0 0 14px; color: var(--ink-soft); font-size: .88rem; }
.sheet[open] { animation: rise .28s var(--ease); }
@keyframes rise { from { opacity: 0; transform: translateY(14px); } }

.field { display: block; margin-bottom: 12px; }
.field > span { display: block; font-size: .76rem; font-weight: 800; text-transform: uppercase; letter-spacing: .07em; color: var(--ink-faint); margin-bottom: 4px; }
.field > span em { font-style: normal; text-transform: none; letter-spacing: 0; font-weight: 600; opacity: .8; }
/* Aligned at the TOP so the labels line up even when a time input and a select
   report slightly different intrinsic heights. */
.field-row { display: flex; gap: 10px; align-items: flex-start; flex-wrap: wrap; }
.field-row > .field { min-width: 0; flex: 1 1 120px; }
.field-row > .field { margin-bottom: 12px; }
.field-row input, .field-row select { height: 44px; }
.field-row textarea { min-height: 44px; }
.field-row .grow { flex: 1; }
.emoji-field input { width: 62px; text-align: center; font-size: 1.2rem; }

/* The arrow has to be drawn by us, because a reset select has none — and it
   has to follow the palette, hence a token rather than a baked-in colour. */
:root {
  --select-arrow: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'><path d='M1 1.5 L6 6.5 L11 1.5' fill='none' stroke='%237A6A5C' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/></svg>");
}
html[data-daypart="evening"] {
  --select-arrow: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'><path d='M1 1.5 L6 6.5 L11 1.5' fill='none' stroke='%23BFB0BE' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/></svg>");
}

select {
  appearance: none;
  -webkit-appearance: none;
  background-image: var(--select-arrow);
  background-repeat: no-repeat;
  background-position: right 12px center;
  background-size: 11px 7px;
  /* Room for the arrow, but not so much that a short value like "1h" gets
     squeezed out of a narrow field. */
  padding-right: 28px !important;
  text-overflow: ellipsis;
}
/* Safari on iOS ignores a plain reset on date/time inputs too. */
input[type="date"], input[type="time"] {
  appearance: none;
  -webkit-appearance: none;
  min-height: 44px;
}

input[type="text"], input[type="number"], input[type="date"], input[type="time"],
input[type="password"], textarea, select {
  width: 100%;
  font: inherit;
  color: var(--ink);
  /* background-color, NOT the shorthand: `background` resets background-image
     and would wipe out the select's custom arrow declared above. */
  background-color: var(--bg);
  border: 1px solid var(--line);
  border-radius: 11px;
  padding: 10px 12px;
}
input:focus-visible, textarea:focus-visible, select:focus-visible, button:focus-visible, .chore:focus-visible {
  outline: 2.5px solid var(--accent);
  outline-offset: 2px;
}
textarea { resize: vertical; }

.check-line { display: flex; align-items: flex-start; gap: 10px; margin: 10px 0; font-size: .9rem; }
.check-line input { accent-color: var(--accent); width: 18px; height: 18px; margin-top: 1px; flex: none; }

.check-line em {
  display: block;
  font-style: normal;
  font-size: .78rem;
  font-weight: 600;
  color: var(--ink-faint);
  margin-top: 3px;
  line-height: 1.35;
}
.check-line.unavailable { opacity: .62; }
.check-line.unavailable input { pointer-events: none; }

.rule { border: 1px solid var(--line); border-radius: 14px; padding: 12px; margin: 14px 0 8px; }
.rule legend { font-size: .76rem; font-weight: 800; text-transform: uppercase; letter-spacing: .07em; color: var(--ink-faint); padding: 0 6px; }
.rule-part { margin-top: 12px; }
.inline { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: .9rem; font-weight: 600; }
.inline input[type="number"] { width: 84px; }
.inline select { width: auto; min-width: 96px; }
.micro { font-size: .78rem; color: var(--ink-faint); margin: 8px 0 0; }
.months-label { display: block; font-size: .76rem; font-weight: 800; text-transform: uppercase; letter-spacing: .07em; color: var(--ink-faint); margin-bottom: 6px; }
.months-label em { font-style: normal; text-transform: none; letter-spacing: 0; font-weight: 600; }

.dow, .months { display: flex; flex-wrap: wrap; gap: 6px; }
.dow button, .months button {
  border: 1px solid var(--line);
  background: var(--bg);
  border-radius: 10px;
  padding: 8px 0;
  width: 42px;
  font-size: .8rem; font-weight: 700;
  color: var(--ink-soft);
  transition: background .18s, color .18s, border-color .18s, transform .15s var(--ease);
}
.months button { width: 46px; }
.dow button[aria-pressed="true"], .months button[aria-pressed="true"] {
  background: var(--accent); border-color: var(--accent); color: var(--on-accent);
}
.dow button:active, .months button:active { transform: scale(.92); }

.preset-row { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.mini {
  border: 1px dashed var(--line); background: transparent;
  border-radius: 999px; padding: 5px 11px;
  font-size: .76rem; font-weight: 700; color: var(--ink-soft);
  transition: background .18s, border-color .18s, color .18s;
}
.mini[aria-pressed="true"] {
  background: var(--accent);
  border-style: solid;
  border-color: var(--accent);
  color: var(--on-accent);
}

.schedule-preview {
  margin: 12px 0 0;
  padding: 9px 12px;
  border-radius: 11px;
  background: color-mix(in oklab, var(--accent-2) 14%, transparent);
  /* 72% sage read 3.94:1 on its own 14% sage ground; 60% clears at 4.73.
     Measured with the tint composited over the sheet, not treated as opaque —
     an earlier "fix" here was made on an oklab-naive reading and had to be
     reverted, so this one was checked twice and against a screenshot. */
  color: color-mix(in oklab, var(--accent-2) 60%, var(--ink));
  font-size: .87rem; font-weight: 700;
}

.err {
  background: color-mix(in oklab, var(--danger) 14%, transparent);
  color: var(--danger);
  border-radius: 11px; padding: 9px 12px;
  font-size: .87rem; font-weight: 700;
  margin: 12px 0 0;
}

.sheet-actions {
  display: flex; align-items: center; gap: 8px;
  margin: 18px 0 0; padding: 0;
  list-style: none;
}
.sheet-actions .spacer { flex: 1; }

.stack { display: flex; flex-direction: column; gap: 8px; }
.stack button {
  text-align: left;
  border: 1px solid var(--line);
  background: var(--bg);
  border-radius: 13px;
  padding: 12px 14px;
  font-weight: 700; font-size: .93rem;
  transition: background .18s, border-color .18s, transform .15s var(--ease);
}
.stack button:hover { background: var(--bg-2); border-color: color-mix(in oklab, var(--accent) 34%, var(--line)); }
.stack button:active { transform: scale(.985); }
.stack button b { display: block; font-weight: 800; }
.stack button em { display: block; font-style: normal; font-weight: 600; font-size: .8rem; color: var(--ink-faint); margin-top: 2px; }

/* A suggestion, not a warning. It sits inline above the fold it is talking
   about, so it must read as a note in the form rather than an alert. */
.step-nudge {
  border: 1px solid color-mix(in oklab, var(--accent) 30%, var(--line));
  background: color-mix(in oklab, var(--accent) 8%, var(--card));
  border-radius: 13px;
  padding: 12px 13px;
  margin: 4px 0 12px;
  display: flex; flex-direction: column; gap: 10px;
}
.sn-text { margin: 0; font-size: .87rem; line-height: 1.5; color: var(--ink-soft); }
.sn-text #step-nudge-why { color: var(--ink); font-weight: 700; }
.sn-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
/*
 * Plainly a way out, and plainly not the main action.
 *
 * --ink-soft, not --ink-faint: this sits on the nudge's own accent tint, and a
 * tint that is its own ground needs more ink than the same colour on a plain
 * card. Faint measured 3.99:1 here in the evening palette — underlined small
 * text is still text, so it owes 4.5. Found by scripts/contrast-audit.js.
 */
.sn-off {
  border: 0; background: none; padding: 4px 2px;
  font: inherit; font-size: .8rem;
  color: var(--ink-soft); text-decoration: underline;
}
.sn-off:hover { color: var(--ink-soft); }

/* The "Custom…" pair, revealed under a duration select. Small and inline: it
   is a refinement of the control above it, not a new question. */
.dur-custom { display: flex; align-items: center; gap: 10px; margin: -4px 0 12px; }
.dur-part { display: flex; align-items: center; gap: 5px; }
.dur-part input {
  width: 66px; padding: 9px 10px;
  border: 1px solid var(--line); border-radius: 11px;
  background: var(--bg); color: var(--ink);
  font: inherit; font-size: .95rem;
}
.dur-part span { font-size: .82rem; color: var(--ink-soft); font-weight: 700; }

/* The time-box list. Tickable in place, so it reads as a short list of tasks
   rather than a report about them. */
.fits-row { display: flex; align-items: center; gap: 11px; padding: 9px 0; }
.fits-row + .fits-row { border-top: 1px solid var(--line); }
.fits-row .grow b { display: block; font-size: .95rem; }
.fits-row .sched { font-size: .78rem; color: var(--ink-faint); }

/* Day groups in the catch-up question. Only shown when there is more than one
   day to ask about; one day needs no heading. */
.catchup-day + .catchup-day { margin-top: 14px; }
.catchup-day .months-label { display: block; margin-bottom: 4px; }

/* ================= reflect =================
 *
 * Quiet on purpose. Nothing here is a count, a target or a warning colour:
 * this is the one screen where being behind is not a thing that exists.
 */
/*
 * There is no generic .card class in this stylesheet — every card names its own
 * tokens. These two use the same set as a task row (.chore), so the screen
 * belongs to the app rather than looking like a different one.
 */
.reflect-intro, .reflect-card {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 16px;
}
.reflect-intro h2 { margin: 0 0 6px; font-size: 1.3rem; }
/* `.sub` is scoped to .sheet, so it does nothing out here — hence its own
   rule rather than borrowing a class that only works inside a dialog. */
.reflect-intro .sub, .reflect-intro .hint {
  margin: 0; color: var(--ink-soft); font-size: .9rem; line-height: 1.55;
}
/* margin:0 above means two paragraphs in a row would butt together into one
   block of text, which is exactly how it read. */
.reflect-intro .sub + .sub { margin-top: 9px; }
.reflect-intro .hint { color: var(--ink-faint); font-size: .82rem; margin-top: 12px; }
.reflect-intro .stack { margin-top: 14px; }

.reflect-card { display: flex; flex-direction: column; gap: 12px; }

/* The questions. A list even when there is one of them, so daily and weekly
   are the same shape and only the length differs. */
.reflect-prompts {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: 7px;
}
.reflect-prompts li {
  /* The display face, the same one h1/h2/.daylabel use — there is no token for
     it, it is named literally in the rule at the top of this file. A question
     is the closest thing this screen has to a heading. */
  font-family: Fraunces, Georgia, serif;
  font-size: 1.05rem; font-weight: 700; line-height: 1.35;
  color: var(--ink);
  text-wrap: balance;
}
/* Several questions read as a set rather than a queue when they are marked. */
.reflect-prompts li:not(:only-child) { padding-left: 14px; position: relative; font-size: .99rem; }
.reflect-prompts li:not(:only-child)::before {
  content: ""; position: absolute; left: 0; top: .55em;
  width: 5px; height: 5px; border-radius: 50%;
  background: color-mix(in oklab, var(--accent) 55%, transparent);
}

#reflect-body { min-height: 150px; line-height: 1.6; }
#reflect-another { align-self: flex-start; }

.reflect-moods { display: flex; gap: 8px; flex-wrap: wrap; }
.mood {
  border: 1px solid var(--line);
  background: var(--bg);
  border-radius: 12px;
  /* Comfortably tappable: these are the smallest targets on the screen. */
  min-width: 46px; padding: 8px 0;
  font-size: 1.5rem; line-height: 1;
  transition: background .18s, border-color .18s, transform .15s var(--ease);
}
.mood:hover { background: var(--bg-2); }
.mood:active { transform: scale(.94); }
/* aria-pressed IS the state, so there is no second class to keep in step. */
.mood[aria-pressed="true"] {
  background: color-mix(in oklab, var(--accent) 16%, var(--card));
  border-color: color-mix(in oklab, var(--accent) 55%, var(--line));
}

/* "Saved" and nothing louder. It has to be visible enough to trust and quiet
   enough to ignore, and it must not move the layout when it appears. */
.reflect-saved { margin: 0; min-height: 1.1em; font-size: .8rem; color: var(--ink-faint); }

.reflect-foot { display: flex; margin-top: 12px; }

.reflect-log { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.reflect-log button {
  width: 100%; text-align: left;
  border: 1px solid var(--line); background: var(--bg);
  border-radius: 13px; padding: 11px 13px;
  display: flex; flex-direction: column; gap: 3px;
}
.reflect-log button:hover { background: var(--bg-2); }
.rl-head { display: flex; align-items: center; gap: 8px; }
.rl-head b { font-weight: 800; font-size: .95rem; }
.rl-face { font-size: 1.05rem; }
.rl-body { color: var(--ink-soft); font-size: .86rem; line-height: 1.45; }

/* The sky panel's footnote sits under the button it explains, so it needs the
   gap .hint's margin-bottom-only does not give it. */
#sky-note { margin-top: 10px; }

/* pick one */
.pick { text-align: center; }
.pick-kicker { margin: 0; font-size: .74rem; font-weight: 800; text-transform: uppercase; letter-spacing: .12em; color: var(--ink-faint); }
.pick-card { padding: 20px 10px 6px; }
.pick-emoji { font-size: 3.4rem; line-height: 1; margin-bottom: 10px; animation: bob 2.6s var(--ease) infinite; }
@keyframes bob { 0%,100% { transform: translateY(0) rotate(-2deg); } 50% { transform: translateY(-7px) rotate(2deg); } }
.pick-card h2 { font-size: 1.6rem; margin: 0 0 4px; }
.pick-actions { flex-wrap: wrap; }

/* ---------------- confetti ---------------- */

.confetti-layer {
  position: fixed; inset: 0;
  pointer-events: none;
  overflow: hidden;
  z-index: 999;
}
.confetto {
  position: absolute;
  top: -20px;
  will-change: transform, opacity;
  animation: fall var(--dur, 2.4s) linear forwards;
}
.confetto.round { border-radius: 50%; }
.confetto.ribbon { border-radius: 2px; }
.confetto.glyph { font-size: 20px; line-height: 1; }
@keyframes fall {
  0%   { transform: translate3d(0,0,0) rotate(0deg); opacity: 1; }
  85%  { opacity: 1; }
  100% { transform: translate3d(var(--dx, 0px), 105vh, 0) rotate(var(--spin, 540deg)); opacity: 0; }
}

/* ---------------- toast ---------------- */

.toast {
  position: fixed;
  left: 50%;
  bottom: calc(20px + env(safe-area-inset-bottom));
  transform: translateX(-50%);
  background: var(--ink);
  color: var(--bg);
  border-radius: 999px;
  padding: 11px 20px;
  font-size: .88rem; font-weight: 700;
  box-shadow: 0 12px 30px -10px rgba(0,0,0,.5);
  z-index: 1000;
  display: flex; align-items: center; gap: 12px;
  max-width: calc(100% - 32px);
  animation: toast-in .3s var(--ease);
}
.toast button { border: 0; background: transparent; color: var(--accent-3); font-weight: 800; padding: 0; text-decoration: underline; }
@keyframes toast-in { from { opacity: 0; transform: translate(-50%, 14px); } }

/* ---------------- calm mode & reduced motion ---------------- */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .08s !important;
  }
}
html[data-calm="1"] .confetti-layer { display: none; }
html[data-calm="1"] .pick-emoji,
html[data-calm="1"] .chore.pop { animation: none; }

/* ---------------- small screens ----------------
   On a phone the header is pure chrome, so it gets out of the way: the list
   should start above the fold, not below a three-line greeting. */

@media (max-width: 480px) {
  .wrap { padding: 12px 14px calc(64px + env(safe-area-inset-bottom)); }
  .mast { gap: 10px; margin-bottom: 14px; }

  .greeting h1 { font-size: 1.15rem; }
  .greeting .sub { font-size: .83rem; }
  .ring-holder, .ring { width: 44px; height: 44px; }
  .icon-btn { width: 34px; height: 34px; }
  .tabs { margin-bottom: 12px; }
  .tab { font-size: .85rem; padding: 8px 4px; }
  .daybar { margin-bottom: 10px; }
  .chip { padding: 7px 12px; font-size: .82rem; }
  .toolbar { gap: 6px; margin-bottom: 12px; }
  .chore-inner { padding: 12px; gap: 10px; }
}

/* ============================================================
   Streaks, search, recap, saved lists, and the settings additions.
   ============================================================ */

/* ---------- segmented control (theme, and anywhere two-way) ---------- */
.seg {
  display: flex; gap: 4px; padding: 4px;
  background: var(--bg-2); border: 1px solid var(--line);
  border-radius: 14px;
}
.seg-btn {
  flex: 1; padding: 9px 10px; border: 0; border-radius: 10px;
  background: transparent; color: var(--ink-soft);
  font: inherit; font-weight: 700; font-size: .86rem;
  cursor: pointer; transition: background .18s var(--ease), color .18s var(--ease);
}
.seg-btn[aria-checked="true"] { background: var(--card); color: var(--ink); box-shadow: var(--shadow); }

/* ---------- streaks ---------- */
.streaks { display: flex; gap: 8px; overflow-x: auto; padding: 2px 0 6px; scrollbar-width: none; }
.streaks::-webkit-scrollbar { display: none; }

.streak {
  flex: 0 0 auto; display: flex; align-items: center; gap: 8px;
  padding: 8px 12px; border-radius: 14px;
  background: var(--card); border: 1px solid var(--line);
  box-shadow: var(--shadow);
}
.streak .n {
  font-family: Fraunces, Georgia, serif; font-weight: 700;
  font-size: 1.25rem; line-height: 1; color: var(--accent-ink);
  font-variant-numeric: tabular-nums;
}
.streak .lbl { display: flex; flex-direction: column; gap: 1px; }
.streak .lbl b { font-size: .74rem; font-weight: 700; letter-spacing: .02em; }
.streak .lbl em { font-size: .66rem; color: var(--ink-faint); font-style: normal; }

/* A frozen streak is holding, not lost. It reads quieter, never alarming. */
.streak.frozen { opacity: .72; }
.streak.frozen .n { color: var(--ink-soft); }
.streak.best .n { color: var(--accent-3); }

/* ---------- search ---------- */
.searchbar { display: flex; gap: 8px; align-items: center; margin: 0 0 10px; }
.searchbar input {
  flex: 1; padding: 11px 14px; border-radius: 14px;
  border: 1px solid var(--line); background-color: var(--card); color: var(--ink);
  font: inherit; font-size: 1rem;
}
.searchbar input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.icon-btn {
  display: inline-grid; place-items: center;
  width: 34px; height: 34px; padding: 0;
  border: 1px solid var(--line); border-radius: 11px;
  background: var(--card); color: var(--ink-soft); cursor: pointer;
}
.icon-btn svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; }
.icon-btn[aria-pressed="true"] { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }

.search-results { display: flex; flex-direction: column; gap: 8px; margin-bottom: 12px; }
.sr {
  display: flex; gap: 10px; align-items: flex-start; width: 100%;
  padding: 11px 13px; border-radius: 14px; text-align: left;
  background: var(--card); border: 1px solid var(--line); box-shadow: var(--shadow);
  font: inherit; color: var(--ink); cursor: pointer;
}
.sr .em { font-size: 1.1rem; line-height: 1.3; }
.sr .body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.sr .t { font-weight: 700; }
.sr .meta { font-size: .74rem; color: var(--ink-faint); margin-top: 1px; }
.sr .hits { font-size: .76rem; color: var(--ink-soft); margin-top: 4px; }
.sr .hits span { display: block; }
.sr mark { background: color-mix(in oklab, var(--accent-3) 45%, transparent); color: inherit; border-radius: 3px; padding: 0 2px; }
.sr.archived { opacity: .6; }
.search-empty { text-align: center; color: var(--ink-faint); padding: 20px 0; font-size: .9rem; }

/* ---------- the week in review ---------- */
.recap-head { text-align: center; margin-bottom: 4px; }
.recap-head .big { font-size: 2.4rem; display: block; line-height: 1.1; }
.recap-head h2 { margin: 6px 0 2px; }
.recap-head .sub { color: var(--ink-soft); font-size: .84rem; }

.recap-bars { display: flex; align-items: flex-end; gap: 6px; height: 92px; margin: 18px 0 6px; }
.recap-bars .col { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 5px; height: 100%; }
.recap-bars .track {
  flex: 1; width: 100%; border-radius: 7px; background: var(--bg-2);
  display: flex; align-items: flex-end; overflow: hidden;
}
.recap-bars .fill { width: 100%; background: var(--done); border-radius: 7px; min-height: 3px; transition: height .5s var(--ease); }
.recap-bars .col.none .track { opacity: .45; }
.recap-bars .col.future { opacity: .3; }
.recap-bars .d { font-size: .64rem; color: var(--ink-faint); font-weight: 700; letter-spacing: .04em; }

.recap-stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin: 14px 0; }
.recap-stats div {
  text-align: center; padding: 10px 6px; border-radius: 12px;
  background: var(--bg-2); border: 1px solid var(--line);
}
.recap-stats b {
  display: block; font-family: Fraunces, Georgia, serif; font-size: 1.3rem;
  color: var(--accent-ink); font-variant-numeric: tabular-nums;
}
.recap-stats span {
  font-size: .62rem; color: var(--ink-faint); letter-spacing: .03em;
  line-height: 1.25; display: block; text-wrap: balance;
}

.recap-note { font-size: .84rem; color: var(--ink-soft); margin: 10px 0 0; }
.chronic { margin-top: 14px; }
.chronic li { display: flex; align-items: center; gap: 8px; padding: 7px 0; font-size: .86rem; }
.chronic li .n { font-size: .72rem; color: var(--ink-faint); margin-left: auto; }

/* ---------- saved lists ---------- */
.shop-shortcuts { display: flex; gap: 8px; flex-wrap: wrap; margin: 0 0 14px; }

.list-pick { display: flex; flex-direction: column; gap: 8px; margin: 10px 0; }
.list-row {
  display: flex; align-items: center; gap: 10px;
  padding: 11px 13px; border-radius: 14px;
  background: var(--card); border: 1px solid var(--line);
}
.list-row.on { border-color: var(--accent); background: color-mix(in oklab, var(--accent) 8%, var(--card)); }
.list-row .em { font-size: 1.15rem; }
.list-row .body { flex: 1; min-width: 0; }
.list-row .t { font-weight: 700; font-size: .92rem; }
.list-row .meta { font-size: .72rem; color: var(--ink-faint); }
.list-row .edit {
  border: 0; background: transparent; color: var(--ink-faint);
  font: inherit; font-size: .76rem; cursor: pointer; padding: 6px; text-decoration: underline;
}

/* ---------- odds and ends ---------- */
.row-gap { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 10px 0; }
.dow.wrap { flex-wrap: wrap; }
.how { margin-top: 10px; font-size: .84rem; }
.how summary { cursor: pointer; color: var(--accent-ink); font-weight: 700; }
.how ol { margin: 8px 0 0; padding-left: 20px; color: var(--ink-soft); line-height: 1.6; }
.how code { background: var(--bg-2); padding: 1px 5px; border-radius: 5px; font-size: .86em; }

/* A radio row that explains itself, for the streak rules. */
.pick-line {
  display: flex; gap: 10px; align-items: flex-start;
  padding: 10px 12px; border-radius: 12px; cursor: pointer;
  border: 1px solid var(--line); background: var(--card);
}
.pick-line.on { border-color: var(--accent); background: color-mix(in oklab, var(--accent) 8%, var(--card)); }
.pick-line input { margin-top: 3px; accent-color: var(--accent); }
.pick-line b { font-size: .88rem; }
.pick-line em { display: block; font-style: normal; font-size: .74rem; color: var(--ink-faint); margin-top: 1px; }
/*
 * A SELECTED row tints its own ground with 8% accent, which is a fourth surface
 * the palette tokens were never measured against — --ink-faint landed at 3.99:1
 * on it. --ink-soft is the same family and clears on all four.
 */
.pick-line.on em { color: var(--ink-soft); }

/* The offline banner. Stays out of the way; the app keeps working. */
.offline-bar {
  position: fixed; left: 50%; transform: translateX(-50%);
  bottom: calc(14px + env(safe-area-inset-bottom));
  z-index: 60; display: flex; align-items: center; gap: 8px;
  padding: 9px 15px; border-radius: 999px;
  background: var(--ink); color: var(--bg);
  font-size: .82rem; font-weight: 700; box-shadow: var(--shadow);
}

/* The usual-time row: a time input that can be emptied again without
   fighting the browser's own clear affordance, which iOS Safari lacks. */
.usual-row { display: flex; gap: 8px; align-items: center; }
.usual-row input[type="time"] { flex: 1; }

/* A streak at zero is an invitation, not a score. It reads quiet — never red,
   never a big fat 0 staring back on a bad week. */
.streak.fresh { opacity: .8; }
.streak.fresh .n { color: var(--ink-faint); }

/* A multi-select chip that has room for a word and a line of explanation.
   Distinct from .dow-btn, which is a square built for one letter. */
.pick-chips { display: flex; flex-direction: column; gap: 8px; }
.pick-chip {
  display: flex; align-items: flex-start; gap: 10px; width: 100%;
  padding: 10px 12px; border-radius: 12px; text-align: left;
  border: 1px solid var(--line); background: var(--card); color: var(--ink);
  font: inherit; cursor: pointer;
  transition: border-color .16s var(--ease), background .16s var(--ease);
}
.pick-chip .pc-body { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.pick-chip b { font-size: .88rem; }
.pick-chip em { font-style: normal; font-size: .74rem; color: var(--ink-faint); }
.pick-chip[aria-pressed="true"] {
  border-color: var(--accent);
  background: color-mix(in oklab, var(--accent) 10%, var(--card));
}
.pick-chip[aria-pressed="true"] em { color: var(--ink-soft); }

/*
 * The `hidden` attribute must actually hide.
 *
 * The UA sheet says [hidden] { display: none }, but that is the weakest rule
 * in the cascade — any class that sets `display` silently defeats it. That is
 * exactly what happened to the "Same as last time" chip, which advertised a
 * shopping trip that had never happened. Making it !important here means
 * `el.hidden = true` can be trusted anywhere in the app.
 */
[hidden] { display: none !important; }

/* ---------- the block sheet's checklist folds away ---------- */

.cb-steps-toggle {
  display: flex; align-items: center; gap: 6px; width: 100%;
  padding: 4px 0; margin: 0; border: 0; background: none;
  font: inherit; color: inherit; cursor: pointer; text-align: left;
}
.cb-steps-toggle .months-label { margin: 0; }
.cb-steps-toggle .chev {
  color: var(--ink-faint); font-size: 1.05rem; line-height: 1;
  transition: transform .22s var(--ease);
}
.cb-steps-toggle[aria-expanded="true"] .chev { transform: rotate(90deg); }

/*
 * The checklist folds away, so the time, colour and pin controls below it are
 * reachable without scrolling past a list you weren't there for.
 *
 * It animates .cb-steps ITSELF rather than a wrapper. The wrapper version
 * fought the max-height that rule already carries and rendered zero-height
 * whether open or shut; the list was always the scrollable box, so it is the
 * thing to collapse.
 */
.cb-steps { transition: max-height .26s var(--ease), margin .26s var(--ease); }
.cb-steps-toggle[aria-expanded="false"] + .cb-steps {
  max-height: 0;
  margin-top: 0;
  margin-bottom: 0;
}

/* ---------- select mode ---------- */

.selbar {
  position: fixed; left: 50%; transform: translateX(-50%);
  bottom: calc(14px + env(safe-area-inset-bottom));
  z-index: 55; display: flex; align-items: center; gap: 8px;
  width: min(520px, calc(100vw - 24px));
  padding: 9px 10px 9px 14px; border-radius: 18px;
  background: var(--card); border: 1px solid var(--line);
  box-shadow: 0 8px 30px -8px rgba(0,0,0,.35), var(--shadow);
}
.selcount { font-size: .82rem; font-weight: 700; color: var(--ink-soft); }
.selbar .chip { padding: 8px 13px; font-size: 1rem; line-height: 1; }
.selbar .chip[disabled] { opacity: .4; pointer-events: none; }

/* In select mode the grips step aside — a tap is for picking, not dragging. */
.timeline.selecting .tc-grip { display: none; }
.timeline.selecting .tcard { cursor: pointer; }

/* The tick sits where the emoji node is, so nothing shifts as you pick. */
.timeline.selecting .trow .tnode { position: relative; }
.timeline.selecting .trow.picked .tnode {
  background: var(--accent); color: var(--on-accent); border-color: var(--accent);
}
.timeline.selecting .trow.picked .tcard {
  border-color: var(--accent);
  background: color-mix(in oklab, var(--accent) 9%, var(--card));
}
.timeline.selecting .trow.unpickable { opacity: .45; }
.timeline.selecting .trow.unpickable .tcard { cursor: not-allowed; }

/* ---------- a shopping list, peeking out of its schedule block ---------- */

.grocery-peek {
  margin: 14px 0 4px; padding: 12px 14px;
  border-radius: 14px;
  background: var(--bg-2); border: 1px solid var(--line);
}
.grocery-peek .months-label { margin: 0 0 4px; }
.grocery-peek .micro { margin: 0 0 10px; line-height: 1.5; }
.grocery-peek .chip { margin: 0; }

/* A hint that teaches something, rather than describing what you can see. */
.hint.tip {
  display: flex; gap: 8px; align-items: flex-start;
  padding: 10px 12px; margin-top: 10px;
  border-radius: 12px;
  background: var(--bg-2); border: 1px dashed var(--line);
}
.hint.tip b { color: var(--ink); }

/* ---------- households ---------- */

.hh {
  padding: 12px 14px; margin: 10px 0;
  border-radius: 14px; background: var(--card); border: 1px solid var(--line);
}
.hh h3 { margin: 0 0 2px; font-size: .96rem; }
.hh .code {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 1.15rem; letter-spacing: .14em; font-weight: 600;
  padding: 7px 12px; margin: 8px 0;
  border-radius: 10px; background: var(--bg-2); border: 1px dashed var(--line);
  color: var(--ink);
}
.hh .people { display: flex; flex-wrap: wrap; gap: 6px; margin: 6px 0 10px; }
.hh .person {
  font-size: .76rem; padding: 3px 9px; border-radius: 999px;
  background: var(--bg-2); border: 1px solid var(--line); color: var(--ink-soft);
}
.hh .person.me { border-color: var(--accent); color: var(--ink); }

/* A shared thing is marked, quietly — you should be able to tell at a glance
   whose eyes are on it without the list turning into a wall of badges. */
.share-mark { font-size: .74rem; color: var(--ink-faint); }
/* The meta row packs several small pills together; without a gap they read as
   one word ("husband✓ husband"). */
.chore-meta, .tc-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.chore.is-shared .chore-inner::after,
.trow.is-shared .tcard::after {
  content: "🏠"; position: absolute; top: 6px; right: 8px;
  font-size: .7rem; opacity: .5; pointer-events: none;
}
.chore.is-shared .chore-inner, .trow.is-shared .tcard { position: relative; }
.assigned-pill {
  font-size: .68rem; padding: 2px 7px; border-radius: 999px;
  background: color-mix(in oklab, var(--accent) 14%, transparent);
  color: var(--ink-soft);
}

/* Who ticked a shared thing. Quiet — it is information, not an alert. */
.ticked-by {
  font-size: .72rem; color: var(--done);
  padding: 1px 7px; border-radius: 999px;
  background: color-mix(in oklab, var(--done) 14%, transparent);
}

/* ---------- the list drag handle ---------- */

/*
 * The same fix the schedule cards got. `.chore` and `.wk-item` keep
 * touch-action: pan-y so the page scrolls normally under a finger; this handle
 * declares touch-action: none and owns the gesture, so a drag survives.
 *
 * Dragging the whole row worked with a mouse and NEVER with a finger, which is
 * why it went unnoticed through several rounds of testing. A synthetic
 * PointerEvent bypasses touch-action arbitration entirely, so it cannot catch
 * this either — only a real finger, or reading the computed touch-action.
 */
.row-grip {
  flex: none;
  width: 30px; height: 34px; padding: 0;
  border: 0; background: transparent;
  border-radius: 9px;
  display: grid; place-items: center;
  touch-action: none;
  cursor: grab;
  opacity: .55;
  transition: opacity .18s, background .18s;
}
.row-grip svg { width: 10px; height: 16px; }
.row-grip circle { fill: var(--ink-faint); }
.row-grip:hover { opacity: 1; background: var(--bg-2); }
.row-grip:hover circle { fill: var(--ink); }
.row-grip:active { cursor: grabbing; background: var(--bg-2); }
.chore.drag-ghost .row-grip circle,
.wk-item.drag-ghost .row-grip circle { fill: var(--accent); }

/* 44px of hit area without inflating the visual, matching every other control. */
.row-grip::after {
  content: ""; position: absolute;
  left: 50%; top: 50%;
  width: 44px; height: 44px;
  transform: translate(-50%, -50%);
  z-index: -1;
}
.row-grip { position: relative; }

/* The week column is narrow, so the handle only appears on a wider screen —
   there it is a tap target, not a squeeze. */
.wk-item .row-grip { width: 22px; height: 26px; }
.wk-item .row-grip svg { width: 8px; height: 13px; }

/* ---------------- projects ---------------- */

/*
 * The progress bar is the whole point of the row: "4 of 10" is a fact, the bar
 * is the feeling. Colour comes from --rowc so a project reads the same here as
 * it does on the schedule; --accent when it has no colour of its own.
 */
.prj-bar {
  display: block;
  height: 6px;
  margin-top: 8px;
  border-radius: 999px;
  background: var(--bg-2);
  overflow: hidden;
}
.prj-bar span {
  display: block;
  height: 100%;
  min-width: 0;
  border-radius: 999px;
  background: var(--rowc, var(--accent));
  transition: width .4s var(--ease);
}
:root[data-calm="1"] .prj-bar span { transition: none; }

.prj-item { cursor: pointer; align-items: flex-start; }
.prj-item .chore-emoji { margin-top: 1px; }
.prj-item:active { transform: scale(.995); }
.prj-done { opacity: .68; }
.prj-late { color: var(--danger); font-weight: 700; }

/* The Today line is quieter than a task row on purpose: it is context for the
   day, not work to do in it, and nothing about it is tickable. */
.prj-line {
  display: flex; align-items: flex-start; gap: 12px;
  padding: 10px 12px;
  border-radius: var(--radius);
  border: 1px dashed var(--line);
  border-left: 4px solid var(--rowc, var(--line));
  background: transparent;
  cursor: pointer;
}
.prj-line + .prj-line { margin-top: 8px; }
.prj-line:hover { background: var(--card); }
.prj-line .grow { flex: 1; min-width: 0; }
.prj-line .sched { font-size: .78rem; color: var(--ink-faint); margin-top: 2px; }

.prj-task { margin-top: 8px; cursor: pointer; }
.prj-task-done { opacity: .6; }
.prj-task-done b { text-decoration: line-through; }

/*
 * These toggle rows carry words, not two-letter weekdays, so they size to their
 * labels instead of the .dow grid's fixed 42px — which clipped "Nearer the
 * time" to a sliver.
 */
#prj-show button, #prj-lead button {
  width: auto;
  padding: 8px 12px;
}
#prj-lead-note { font-style: normal; color: var(--ink-faint); font-weight: 500; }
#prj-progress .prj-bar { margin-top: 6px; }

/*
 * The inline add row. It sits at the end of the project's task list and looks
 * like part of it, because it is — adding used to open the task editor on top
 * of this sheet, for a title and a date.
 */
.prj-add {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 10px;
  padding: 12px;
  border: 1px dashed var(--line);
  border-radius: var(--radius);
  /* --bg-2, not --bg: against the sheet's --card panel, --bg is a 2% shift in
     daylight and the row read as another floating white task card rather than
     a place to type. --bg-2 is a real step in both palettes. */
  background: var(--bg-2);
}
.prj-add input[type="text"] {
  width: 100%;
  box-sizing: border-box;
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 11px 12px;
  font: inherit;
  color: var(--ink);
  background: var(--card);
}
.prj-add input[type="text"]::placeholder { color: var(--ink-faint); }
.prj-add-row { display: flex; gap: 8px; align-items: stretch; }
.prj-add-row input[type="date"] {
  flex: 1; min-width: 0;
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 10px 12px;
  font: inherit;
  color: var(--ink);
  background: var(--card);
}
.prj-add-row .primary { flex: 0 0 auto; padding-inline: 20px; }
.prj-add-row .primary:disabled { opacity: .6; }

/*
 * What a typed command would do, before it does it.
 *
 * A plain list in tabular figures, because most of what it shows is
 * "10:00am → 11:00am" and columns of times that don't line up are hard to scan.
 */
.cmd-detail {
  list-style: none;
  margin: 10px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-height: 46vh;
  overflow-y: auto;
}
.cmd-detail li {
  font-size: .9rem;
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
  padding: 8px 10px;
  border-radius: 10px;
  background: var(--bg-2);
}
.cmd-detail:empty { display: none; }
#cmd-summary { margin-bottom: 2px; }
#cmd-warn { margin-top: 10px; }

/* ---------------- the starter library ---------------- */

.lib-groups { max-height: 62vh; overflow-y: auto; margin-top: 6px; }
.lib-group + .lib-group { margin-top: 18px; }
.lib-group h3 {
  font-size: .82rem; letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink-soft); margin: 0 0 2px;
}
.lib-group .hint { margin: 0 0 8px; }

.lib-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }

/*
 * A grid, not a row.
 *
 * Side by side at 375px the frequency took 42% and "Wipe the kitchen counters"
 * broke over three lines. The title gets the full width and the frequency sits
 * under it, lined up with the text rather than the tick; on a wider sheet they
 * go back to sharing one line.
 */
.lib-item {
  display: grid;
  grid-template-columns: auto auto minmax(0, 1fr);
  grid-template-areas:
    "tick emoji title"
    ".    .     freq";
  align-items: center;
  gap: 6px 10px;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--bg);
}
.lib-tick  { grid-area: tick; }
.lib-emoji { grid-area: emoji; }
.lib-item .grow { grid-area: title; min-width: 0; }
.lib-freq, .lib-added { grid-area: freq; justify-self: start; }

@media (min-width: 430px) {
  .lib-item {
    grid-template-columns: auto auto minmax(0, 1fr) auto;
    grid-template-areas: "tick emoji title freq";
  }
  .lib-freq, .lib-added { justify-self: end; }
}
.lib-item b { font-size: .95rem; }
.lib-item .sched { font-size: .74rem; color: var(--ink-faint); margin-top: 1px; }
.lib-emoji { font-size: 1.1rem; }

/* Ticked rows read at a glance, so a long scroll still shows what you picked. */
.lib-item.lib-on { border-color: var(--accent); background: var(--bg-2); }
.lib-have { opacity: .55; }

/*
 * A real checkbox is 13px and unmissable only to someone with a mouse. This is
 * a button styled as one, at a size a thumb can actually hit.
 */
.lib-tick {
  flex: 0 0 auto;
  width: 26px; height: 26px;
  border-radius: 8px;
  border: 2px solid var(--line);
  background: var(--card);
  position: relative;
  transition: background .15s, border-color .15s;
}
.lib-tick[aria-checked="true"] { background: var(--accent); border-color: var(--accent); }
.lib-tick[aria-checked="true"]::after {
  content: "✓";
  position: absolute; inset: 0;
  display: grid; place-items: center;
  color: var(--on-accent); font-size: .82rem; font-weight: 800;
}
.lib-tick:disabled { opacity: .4; }
/* 44px of hit area without inflating the box, as every other control here. */
.lib-tick::before {
  content: ""; position: absolute;
  left: 50%; top: 50%; width: 44px; height: 44px;
  transform: translate(-50%, -50%);
}

.lib-freq {
  max-width: 100%;
  font: inherit; font-size: .8rem;
  color: var(--ink);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 9px;
  padding: 6px 8px;
}
.lib-added { font-size: .74rem; color: var(--ink-faint); white-space: nowrap; }

#empty-library { margin-top: 12px; }

/*
 * A settings row whose control sits inline on the right rather than on a line
 * of its own. The explainer takes the width it needs; the button never shrinks
 * below its label.
 */
.set-row {
  display: flex;
  align-items: center;
  gap: 14px;
  justify-content: space-between;
}
.set-row .micro { margin: 0; flex: 1; min-width: 0; }
.set-row .chip { flex: 0 0 auto; }

/* ---------------- settings as a menu ---------------- */

.set-menu { list-style: none; margin: 8px 0 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.set-cat {
  display: flex; align-items: center; gap: 12px;
  width: 100%;
  padding: 14px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--card);
  text-align: left;
  color: var(--ink);
  transition: background .15s, border-color .15s, transform .15s var(--ease);
}
.set-cat:hover { background: var(--bg-2); }
.set-cat:active { transform: scale(.99); }
.set-cat .grow { flex: 1; min-width: 0; display: block; }
.set-cat b { display: block; font-size: .98rem; }
.set-cat .sched { display: block; font-size: .78rem; color: var(--ink-faint); margin-top: 2px; }
.set-icon { font-size: 1.3rem; flex: 0 0 auto; }
.set-go { color: var(--ink-faint); font-size: 1.3rem; flex: 0 0 auto; }

/* The back arrow sits on the same line as the panel's own heading. */
.panel-head { display: flex; align-items: center; gap: 8px; margin-bottom: 4px; }
.panel-head h2 { margin: 0; }
.set-back { flex: 0 0 auto; }

/* ---------------- everything that repeats ---------------- */

.rec-group + .rec-group { margin-top: 16px; }
.rec-group h3 {
  display: flex; align-items: center; gap: 8px;
  font-size: .82rem; letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink-soft); margin: 0 0 6px;
}
.rec-count {
  font-size: .72rem; letter-spacing: 0;
  background: var(--bg-2); color: var(--ink-soft);
  border-radius: 999px; padding: 1px 7px;
}
.rec-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.rec-item {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 11px;
  border: 1px solid var(--line);
  border-left: 4px solid var(--rowc, var(--line));
  border-radius: 12px;
  background: var(--card);
  cursor: pointer;
}
.rec-item:hover { background: var(--bg-2); }
.rec-item .grow { flex: 1; min-width: 0; }
.rec-item .sched { font-size: .74rem; color: var(--ink-faint); margin-top: 1px; }

/* ---------------- pinned morning & evening ---------------- */

/*
 * Set apart from the middle of the day without shouting: a tinted ground and a
 * dashed edge, so it reads as "this is a routine" rather than "this is urgent".
 */
.daypart {
  border: 1px dashed var(--line);
  border-radius: var(--radius);
  /* Barely any side padding: the rows inside are the same rows as the main
     list, and squeezing them wraps titles that fit fine two lines above. */
  padding: 10px 5px 8px;
  background: var(--bg-2);
}
.daypart .group-head { margin-top: 0; }
#morning-strip { margin-bottom: 14px; }
#evening-strip { margin-top: 14px; }

.strip-add { display: flex; gap: 8px; margin-top: 8px; }
.strip-add input {
  flex: 1; min-width: 0;
  font: inherit; font-size: .88rem;
  color: var(--ink);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 9px 11px;
}
.strip-add input::placeholder { color: var(--ink-faint); }
.strip-add .chip { flex: 0 0 auto; }

/* A routine is one decision, so it gets a card rather than a row in a list. */
.lib-routine {
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--bg);
  padding: 10px 12px;
  margin-bottom: 8px;
}
.lib-routine-head { display: flex; align-items: center; gap: 10px; }
.lib-routine-head .grow { flex: 1; min-width: 0; }
.lib-routine-head b { font-size: .95rem; }
.lib-routine-head .sched { font-size: .74rem; color: var(--ink-faint); margin-top: 1px; }
.lib-routine-head .chip { flex: 0 0 auto; }
.lib-steps {
  margin: 8px 0 0;
  font-size: .76rem;
  color: var(--ink-faint);
  line-height: 1.5;
}

/* ---------------- the building ---------------- */

/*
 * Every layer is the same canvas, so they stack with no positioning of their
 * own — `inset: 0` on all of them and the box decides the size. The aspect
 * ratio is set from the manifest, not written here, because the art owns it.
 */
.scene-wrap { position: relative; margin: 0 auto; max-width: 460px; }
.scene {
  position: relative;
  width: 100%;
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--bg-2);
  box-shadow: var(--shadow);
}
.scene-layer {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  /* Window tiles carry their own inset from the manifest, so they must not
     inherit the full-canvas one. `inset: auto` lets left/top/width/height win. */
  object-fit: fill;
  pointer-events: none;
}
.scene-layer[style*="left"] { inset: auto; }

/* The temperature, in the corner she asked for. Legible over either sky, so it
   carries its own ground rather than trusting the paint behind it. */
.scene-temp {
  position: absolute;
  top: 10px; right: 12px;
  padding: 4px 10px;
  border-radius: 999px;
  background: color-mix(in oklab, var(--card) 82%, transparent);
  color: var(--ink);
  font-size: .9rem; font-weight: 800;
  font-variant-numeric: tabular-nums;
  backdrop-filter: blur(3px);
}

.scene-caption {
  margin: 10px 0 0;
  text-align: center;
  font-size: .84rem;
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
}
#scene-next { text-align: center; margin-top: 4px; }

.scene-rows { margin-top: 14px; display: flex; flex-direction: column; gap: 8px; }
.scene-row {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 12px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--card);
}
.scene-row .grow { flex: 1; min-width: 0; }
.scene-row b { font-size: .92rem; }
.scene-row .sched { display: block; font-size: .74rem; color: var(--ink-faint); }
.scene-lamps { display: flex; gap: 5px; flex: 0 0 auto; }
.scene-lamps i {
  width: 12px; height: 12px;
  border-radius: 3px;
  border: 1px solid var(--line);
  background: var(--bg-2);
}
/* A lit lamp is the accent's FILL, not its text ink: this is a shape, not
   type, so 3:1 is the bar and the fill clears it comfortably. */
.scene-lamps i.on { background: var(--accent); border-color: var(--accent); }

/* The way in, where the plant used to be. */
.scene-peek {
  border: 0; background: transparent; padding: 0;
  width: 52px; height: 68px;
  display: grid; place-items: center;
  border-radius: 12px;
  transition: transform .15s var(--ease), background .15s;
}
.scene-peek:hover { background: var(--bg-2); }
.scene-peek:active { transform: scale(.94); }
.scene-peek img { width: 44px; height: auto; opacity: .9; }
