:root {
  --ink: #14142b;
  --ink-soft: #3d3d5c;
  --paper: #fbf9f6;
  --surface: #ffffff;
  --accent: #ff5d73;
  --accent-dark: #e8465c;
  --accent-2: #2fc4b2;
  --accent-3: #7c5cff;
  --sun: #ffc75f;
  --muted: #75758c;
  --border: #e8e5e0;
  --border-strong: #d6d2cb;
  --good: #17a86b;
  --good-bg: #e4f7ee;
  --bad: #d64550;
  --bad-bg: #fdeaec;
  --radius: 14px;
  --radius-lg: 20px;
  --shadow-sm: 0 1px 2px rgba(20, 20, 43, 0.04), 0 2px 8px rgba(20, 20, 43, 0.04);
  --shadow-md: 0 4px 12px rgba(20, 20, 43, 0.07), 0 12px 32px rgba(20, 20, 43, 0.06);
  --shadow-lg: 0 20px 60px rgba(20, 20, 43, 0.18);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Inter", sans-serif;
  color: var(--ink);
  background:
    radial-gradient(900px 500px at 12% -10%, rgba(124, 92, 255, 0.07), transparent 60%),
    radial-gradient(800px 500px at 100% 0%, rgba(47, 196, 178, 0.08), transparent 55%),
    var(--paper);
  background-attachment: fixed;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 { letter-spacing: -0.02em; line-height: 1.2; }
h1 { font-size: 2rem; }
h2 { font-size: 1.35rem; }
h3 { font-size: 1.05rem; }

a { color: var(--accent-3); text-decoration-thickness: 1.5px; text-underline-offset: 2px; }
a:hover { color: var(--accent); }

/* ---------- Header ---------- */

.site-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
  padding: 0.9rem 1.75rem;
  background: rgba(255, 255, 255, 0.82);
  backdrop-filter: saturate(180%) blur(12px);
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
  z-index: 10;
}

.brand {
  font-weight: 800;
  font-size: 1.02rem;
  color: var(--ink);
  text-decoration: none;
  letter-spacing: -0.03em;
}

.site-header nav {
  display: flex;
  align-items: center;
  gap: 0.15rem;
  flex-wrap: wrap;
}

.site-header nav a {
  color: var(--ink-soft);
  text-decoration: none;
  font-size: 0.9rem;
  font-weight: 550;
  padding: 0.4rem 0.7rem;
  border-radius: 999px;
  transition: background 0.15s ease, color 0.15s ease;
}

.site-header nav a:hover { background: rgba(20, 20, 43, 0.05); color: var(--ink); }

/* Where you are. Without this every page's header looks identical, which is
   most of what makes a set of screens feel like an old admin panel. */
.site-header nav a.active {
  background: var(--accent-soft, rgba(226, 90, 106, 0.12));
  color: var(--ink);
  font-weight: 700;
}
.site-header nav a.active:hover { background: var(--accent-soft, rgba(226, 90, 106, 0.18)); }

/* Account-level links sit back so the sections you actually work in lead. */
.site-header nav a.nav-quiet { color: var(--muted); font-weight: 500; }
.site-header nav a.nav-quiet:hover { color: var(--ink); }

.nav-divider {
  width: 1px;
  align-self: stretch;
  margin: 0.25rem 0.45rem;
  background: rgba(20, 20, 43, 0.1);
}
@media (max-width: 640px) {
  /* Stacked on a phone, a vertical rule just adds noise. */
  .nav-divider { display: none; }
  .site-header nav { gap: 0.1rem; }
  .site-header nav a { padding: 0.35rem 0.55rem; font-size: 0.85rem; }
}

/* ---------- Buttons ---------- */

/* display on .button beats the browser's own [hidden] { display: none }, so a
   button the template marked hidden was drawing anyway — Undo sat in the
   sorting bar with nothing to undo, offering to reverse a change nobody had
   made. Same guard .tour-tip and .assign-counter already carry. */
.button[hidden] { display: none; }

.button,
.site-header nav a.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  background: linear-gradient(135deg, var(--accent) 0%, var(--accent-dark) 100%);
  color: white;
  padding: 0.6rem 1.1rem;
  border-radius: 999px;
  border: none;
  cursor: pointer;
  font-size: 0.92rem;
  font-weight: 600;
  font-family: inherit;
  text-decoration: none;
  box-shadow: 0 2px 8px rgba(255, 93, 115, 0.28);
  transition: transform 0.12s ease, box-shadow 0.12s ease, filter 0.12s ease;
}

.button:hover {
  transform: translateY(-1px);
  box-shadow: 0 6px 18px rgba(255, 93, 115, 0.34);
  filter: brightness(1.03);
  color: white;
}

.button:active { transform: translateY(0); }

.button:disabled,
.button[disabled] {
  opacity: 0.45;
  cursor: not-allowed;
  box-shadow: none;
  transform: none;
  filter: none;
}

.button-ghost {
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--border-strong);
  box-shadow: none;
}

.button-ghost:hover { background: #f4f2ef; color: var(--ink); box-shadow: none; }

.button-sm { padding: 0.35rem 0.75rem; font-size: 0.82rem; }

/* ---------- Layout ---------- */

main {
  max-width: 860px;
  margin: 0 auto;
  padding: 2.25rem 1.25rem 4rem;
}

.hero {
  text-align: center;
  padding: 3rem 0 1.5rem;
}

.hero h1 {
  font-size: clamp(2.1rem, 5.5vw, 3.1rem);
  margin: 0 0 0.75rem;
  background: linear-gradient(120deg, var(--ink) 30%, var(--accent-3) 75%, var(--accent) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}

.hero p { color: var(--ink-soft); font-size: 1.08rem; max-width: 34rem; margin: 0.6rem auto; }
/* One line on a laptop, and still allowed to wrap on anything narrower — the
   34rem measure is right for the paragraphs around it and too tight for this
   one sentence. */
.hero p.hero-line { max-width: 48rem; }

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 1.75rem;
  margin-top: 1.25rem;
  box-shadow: var(--shadow-sm);
}

.card h1:first-child, .card h2:first-child { margin-top: 0; }

/* ---------- Forms ---------- */

form label { display: block; margin-top: 1rem; font-weight: 600; font-size: 0.88rem; }

input[type="text"], input[type="email"], input[type="password"],
input[type="number"], input[type="date"], input[type="file"], select, textarea {
  width: 100%;
  padding: 0.6rem 0.75rem;
  margin-top: 0.35rem;
  border: 1px solid var(--border-strong);
  border-radius: 10px;
  font-size: 0.95rem;
  font-family: inherit;
  color: var(--ink);
  background: var(--surface);
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--accent-3);
  box-shadow: 0 0 0 3px rgba(124, 92, 255, 0.14);
}

table select, table input[type="number"], table input[type="text"], .bulk-action-bar select {
  width: auto;
  margin-top: 0;
  padding: 0.35rem 0.5rem;
  font-size: 0.86rem;
  border-radius: 8px;
}

form .checkbox-row {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  margin-top: 1.1rem;
  font-size: 0.9rem;
}

form .checkbox-row input { margin-top: 0.25rem; width: auto; }
input[type="checkbox"] { accent-color: var(--accent-3); width: 1rem; height: 1rem; cursor: pointer; }

.admin-form label { font-size: 0.85rem; }

form.inline { display: inline; }

/* ---------- Flash ---------- */

.flash-messages { margin-bottom: 1rem; }

.flash {
  padding: 0.8rem 1.1rem;
  border-radius: 12px;
  margin-bottom: 0.5rem;
  font-size: 0.9rem;
  font-weight: 500;
  border: 1px solid transparent;
}

.flash-error { background: var(--bad-bg); color: #a1222d; border-color: #f6ccd1; }
.flash-success { background: var(--good-bg); color: #0d7a4c; border-color: #bfe9d5; }
.flash-notice { background: #fff8e8; color: #6b4e00; border-color: #f3e0b0; }

/* ---------- Tables ---------- */

table { width: 100%; border-collapse: collapse; margin-top: 1rem; font-size: 0.92rem; }

th, td { text-align: left; padding: 0.7rem 0.65rem; border-bottom: 1px solid var(--border); }

th {
  color: var(--muted);
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-weight: 700;
}

th a { color: var(--muted); text-decoration: none; }
th a:hover { color: var(--accent-3); }

/* Sortable column headings. The arrow sits there faintly so it reads as
   clickable before anyone clicks it. */
.sort-button {
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  font: inherit;
  color: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  white-space: nowrap;
}
.sort-button:hover { color: var(--accent-3); }
.sort-button::after { content: " ↕"; opacity: 0.35; }
.sort-button[data-dir="asc"] { color: var(--accent-3); }
.sort-button[data-dir="asc"]::after { content: " ↑"; opacity: 1; }
.sort-button[data-dir="desc"] { color: var(--accent-3); }
.sort-button[data-dir="desc"]::after { content: " ↓"; opacity: 1; }

tbody tr { transition: background 0.12s ease; }
tbody tr:hover { background: #faf9ff; }
tbody tr:last-child td { border-bottom: none; }

/* ---------- Badges ---------- */

.badge {
  display: inline-block;
  padding: 0.15rem 0.55rem;
  border-radius: 999px;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  background: #eeecff;
  color: var(--accent-3);
  vertical-align: middle;
}

.badge-active { background: var(--good-bg); color: #0d7a4c; }
.badge-inactive { background: #f1f0ee; color: var(--muted); }

/* ---------- Callouts & banners ---------- */

.callout {
  margin-top: 1.25rem;
  padding: 1.1rem 1.25rem;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  background: linear-gradient(135deg, #f8f7ff 0%, #f4fdfb 100%);
  font-size: 0.92rem;
}

.callout p { margin: 0.4rem 0; }
.callout p:first-child { margin-top: 0; }
.callout p:last-child { margin-bottom: 0; }

.disclaimer-banner {
  background: #fff8e8;
  border: 1px solid #f3e0b0;
  border-radius: var(--radius);
  padding: 0.9rem 1.15rem;
  font-size: 0.85rem;
  color: #7a5c00;
  margin-bottom: 1.5rem;
}

.price-line { font-size: 1.9rem; font-weight: 800; margin: 0.4rem 0; letter-spacing: -0.03em; }
.price-line small { font-size: 0.85rem; font-weight: 500; color: var(--muted); letter-spacing: 0; }

/* ---------- Stat tiles ---------- */

.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 0.85rem;
  margin-top: 1.15rem;
}

.stat {
  padding: 1rem 1.1rem;
  border-radius: var(--radius);
  background: #faf9fc;
  border: 1px solid var(--border);
}

.stat-label {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
  font-weight: 700;
}

.stat-value { font-size: 1.5rem; font-weight: 800; letter-spacing: -0.02em; margin-top: 0.2rem; }
.stat-sub { font-size: 0.8rem; color: var(--muted); }
.stat-good .stat-value { color: var(--good); }
.stat-bad .stat-value { color: var(--bad); }

/* ---------- Segmented toggle ---------- */

.segmented {
  display: inline-flex;
  background: #f1f0ee;
  border-radius: 999px;
  padding: 0.2rem;
  gap: 0.15rem;
}

.segmented a {
  padding: 0.35rem 0.9rem;
  border-radius: 999px;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--ink-soft);
  text-decoration: none;
}

.segmented a.active { background: var(--surface); color: var(--ink); box-shadow: var(--shadow-sm); }

/* ---------- Lessons ---------- */

.lesson-section { margin-top: 2rem; }
.lesson-section h2 { font-size: 1.2rem; margin-bottom: 0.5rem; }
.lesson-section p { margin: 0.6rem 0; }

.animation-frame {
  min-height: 180px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(135deg, #f8f7ff 0%, #f2fbfa 100%);
  border-radius: var(--radius);
  padding: 1.25rem;
  margin: 0.9rem 0;
  overflow: hidden;
}

.animation-frame svg { max-width: 100%; height: auto; }

/* ---------- Lesson carousel ---------- */

/* Every step renders visible. The script adds .carousel-active once it has
   taken over, so a reader without JS still gets the whole lesson. */
.lesson-carousel .carousel-nav,
.lesson-carousel .carousel-progress,
.lesson-carousel .carousel-count { display: none; }

.carousel-active .carousel-nav { display: flex; }
.carousel-active .carousel-progress { display: flex; }
.carousel-active .carousel-count { display: block; }

.carousel-progress {
  gap: 0.4rem;
  margin-top: 1.5rem;
}

.progress-dot {
  height: 6px;
  flex: 1;
  border-radius: 3px;
  background: var(--border);
  transition: background 0.25s ease;
}

.progress-dot.is-seen { background: var(--accent-2); }
.progress-dot.is-current { background: var(--accent-3); }

.carousel-count {
  margin: 0.5rem 0 0;
  font-size: 0.8rem;
  color: var(--muted);
}

.carousel-step { min-height: 240px; }
.carousel-step > h2 { font-size: 1.45rem; margin: 1.25rem 0 0.25rem; }
.carousel-step .lesson-section:first-of-type { margin-top: 1rem; }

.carousel-nav {
  gap: 0.75rem;
  justify-content: space-between;
  margin-top: 2rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--border);
}

/* ---------- Checkpoints ---------- */

.checkpoint {
  margin-top: 1.75rem;
  padding: 1.25rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  background: var(--paper);
}

.checkpoint-question { margin: 0 0 0.9rem; font-size: 0.98rem; }

.checkpoint-options {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.checkpoint-option {
  text-align: left;
  padding: 0.7rem 0.9rem;
  font-size: 0.92rem;
  font-family: inherit;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: 10px;
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease;
}

.checkpoint-option:hover { border-color: var(--accent-3); background: #faf9ff; }

/* Chosen, not "wrong". A checkpoint explains; it never marks a failure. */
.checkpoint-option.is-chosen { border-color: var(--accent-3); background: #f4f1ff; }
.checkpoint-option.is-right { border-color: var(--good); background: var(--good-bg); }

.checkpoint-response {
  margin-top: 0.9rem;
  padding: 0.8rem 0.95rem;
  border-radius: 10px;
  font-size: 0.9rem;
  background: #f1f0ee;
  color: var(--ink-soft);
}

.checkpoint-response.is-right { background: var(--good-bg); color: #0d7a4c; }

/* ---------- Sticky bulk bar ---------- */

.bulk-action-bar {
  position: sticky;
  top: 3.6rem;
  z-index: 4;
  background: rgba(255, 255, 255, 0.94);
  backdrop-filter: blur(10px);
  padding: 0.75rem 0.9rem;
  margin: 0 -0.9rem;
  border-radius: 12px;
  border: 1px solid var(--border);
  box-shadow: var(--shadow-sm);
}

/* The mirror of the bar above: it sticks to the bottom instead, so the Save
   button stays in reach however far down the table you've edited. */
.save-bar {
  position: sticky;
  bottom: 0;
  z-index: 4;
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
  background: rgba(255, 255, 255, 0.94);
  backdrop-filter: blur(10px);
  padding: 0.75rem 0.9rem;
  margin: 0.5rem -0.9rem 0;
  border-radius: 12px;
  border: 1px solid var(--border);
  box-shadow: var(--shadow-sm);
}

/* A row that's been typed into but not saved yet. Same colour the app uses for
   "heads up" elsewhere, so it reads as pending rather than broken. */
tbody tr.row-changed td { background: #fff8e8; }

/* ---------- Modal ---------- */

.modal-overlay {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(20, 20, 43, 0.5);
  backdrop-filter: blur(3px);
  align-items: center;
  justify-content: center;
  z-index: 50;
  padding: 1rem;
}

.modal-overlay.open { display: flex; animation: overlay-in 0.18s ease-out; }

@keyframes overlay-in { from { opacity: 0; } to { opacity: 1; } }

.modal-box {
  background: var(--surface);
  border-radius: var(--radius-lg);
  padding: 1.9rem;
  width: 100%;
  max-width: 520px;
  max-height: 88vh;
  overflow-y: auto;
  box-shadow: var(--shadow-lg);
  animation: modal-in 0.22s cubic-bezier(0.34, 1.4, 0.64, 1);
}

@keyframes modal-in {
  from { opacity: 0; transform: translateY(14px) scale(0.97); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}

.modal-box h2 { margin-top: 0; }

/* ---------- Learn walkthrough ---------- */

.learn-step { display: none; }
.learn-step.active { display: block; animation: step-in 0.25s ease-out; }

@keyframes step-in {
  from { opacity: 0; transform: translateX(10px); }
  to { opacity: 1; transform: translateX(0); }
}

.learn-emoji { font-size: 2.6rem; line-height: 1; display: block; margin-bottom: 0.5rem; }

.learn-step h2 { font-size: 1.5rem; margin: 0 0 0.5rem; }
.learn-step p { color: var(--ink-soft); margin: 0.55rem 0; }
.learn-step .lead { font-size: 1.05rem; color: var(--ink); }

.learn-examples {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 0.9rem 0;
  padding: 0;
  list-style: none;
}

.learn-examples li {
  background: #f4f3f8;
  border-radius: 999px;
  padding: 0.28rem 0.7rem;
  font-size: 0.82rem;
  color: var(--ink-soft);
}

.learn-verdict {
  margin-top: 1rem;
  padding: 0.85rem 1rem;
  border-radius: 12px;
  background: linear-gradient(135deg, #f8f7ff, #f2fbfa);
  border: 1px solid var(--border);
  font-weight: 600;
}

.learn-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  margin-top: 1.6rem;
  padding-top: 1.1rem;
  border-top: 1px solid var(--border);
}

.learn-dots { display: flex; gap: 0.35rem; }

.learn-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--border-strong);
  transition: background 0.2s ease, transform 0.2s ease;
}

.learn-dot.active { background: var(--accent); transform: scale(1.35); }

/* ---------- Wizard progress ---------- */

.wizard-progress {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  margin-bottom: 1.5rem;
  flex-wrap: wrap;
}

.wizard-step { display: flex; align-items: center; gap: 0.45rem; }

.wizard-dot {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.75rem;
  font-weight: 700;
  background: #eeecea;
  color: var(--muted);
  flex-shrink: 0;
  transition: all 0.2s ease;
}

.wizard-step.done .wizard-dot { background: var(--good-bg); color: var(--good); }
.wizard-step.current .wizard-dot {
  background: linear-gradient(135deg, var(--accent), var(--accent-dark));
  color: white;
  box-shadow: 0 0 0 4px rgba(255, 93, 115, 0.16);
}

.wizard-label { font-size: 0.82rem; font-weight: 600; color: var(--muted); }
.wizard-label a { color: var(--muted); text-decoration: none; }
.wizard-label a:hover { color: var(--accent-3); text-decoration: underline; }
.wizard-step.current .wizard-label { color: var(--ink); }
.wizard-step.done .wizard-label { color: var(--ink-soft); }

.wizard-line { flex: 1; height: 2px; background: var(--border); min-width: 12px; border-radius: 2px; }
.wizard-line.done { background: #bfe9d5; }

/* ---------- Quiz ---------- */

.quiz-option {
  display: flex;
  align-items: flex-start;
  gap: 0.7rem;
  padding: 0.85rem 1rem;
  border: 1.5px solid var(--border-strong);
  border-radius: 12px;
  margin-bottom: 0.6rem;
  cursor: pointer;
  transition: all 0.15s ease;
  background: var(--surface);
}

.quiz-option:hover { border-color: var(--accent-3); background: #faf9ff; }
.quiz-option input { margin-top: 0.2rem; width: auto; flex-shrink: 0; }
.quiz-option.picked { border-color: var(--accent-3); background: #f6f4ff; }
.quiz-option.correct { border-color: var(--good); background: var(--good-bg); }

.quiz-feedback {
  margin-top: 1.1rem;
  padding: 1.1rem 1.25rem;
  border-radius: 12px;
  display: none;
}

.quiz-feedback.show { display: block; animation: step-in 0.25s ease-out; }
/* Amber, not red — a wrong answer here is a teaching moment, not an error. */
.quiz-feedback.retry { background: #fff8e8; border: 1px solid #f3e0b0; color: #6b4e00; }
.quiz-feedback.pass { background: var(--good-bg); border: 1px solid #bfe9d5; color: #0d7a4c; }

/* ---------- Dropzone ---------- */

.dropzone {
  border: 2px dashed var(--border-strong);
  border-radius: var(--radius-lg);
  padding: 2.5rem 1.5rem;
  text-align: center;
  background: #fbfaf9;
  transition: all 0.18s ease;
  cursor: pointer;
}

.dropzone:hover, .dropzone.dragover {
  border-color: var(--accent-3);
  background: #f7f5ff;
}

.dropzone-icon { font-size: 2.4rem; display: block; margin-bottom: 0.5rem; }

.file-chip {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.55rem 0.85rem;
  background: #f4f3f8;
  border-radius: 10px;
  margin-top: 0.5rem;
  font-size: 0.88rem;
}

/* ---------- Footer ---------- */

.site-footer {
  text-align: center;
  color: var(--muted);
  font-size: 0.8rem;
  padding: 2rem 1.5rem;
  border-top: 1px solid var(--border);
  margin-top: 2rem;
}

@media (max-width: 640px) {
  main { padding: 1.5rem 1rem 3rem; }
  .card { padding: 1.25rem; border-radius: var(--radius); }
  .site-header { padding: 0.75rem 1rem; }
  .site-header nav a { padding: 0.35rem 0.5rem; font-size: 0.85rem; }
}

/* ---------- Goal progress ---------- */

.progress-track {
  height: 10px;
  border-radius: 999px;
  background: #ecebe8;
  overflow: hidden;
}
.progress-fill {
  height: 100%;
  border-radius: 999px;
  background: linear-gradient(135deg, var(--accent) 0%, var(--accent-dark) 100%);
  /* Zero progress should still read as a bar at zero, not a missing element. */
  min-width: 2px;
  transition: width 0.3s ease;
}
.progress-fill.progress-done {
  background: linear-gradient(135deg, #34c98a 0%, #1f9d68 100%);
}

/* ---------- Two-column working layout ---------- */

/* Reference material belongs beside the thing you're deciding about, not a
   scroll away underneath it. Below 1000px there isn't room for two useful
   columns, so it stacks and the reference simply follows the work. */
.split-layout { display: block; }
.split-side .card { margin-top: 0; }

/* The page is normally capped at a comfortable reading width. A working
   screen with a reference column beside it needs more room, but only that
   screen — everything else stays easy to read. */
main:has(.split-layout) { max-width: 1200px; }

/* Same idea for a page that's mostly a wide table, only more so. Bank
   descriptions run to sixty-odd characters, and reading one shouldn't mean
   scrolling sideways — so this page takes nearly the whole window on a big
   screen instead of a comfortable reading column. */
main:has(.page-wide) { max-width: min(1600px, 96vw); }

@media (min-width: 1000px) {
  .split-layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 22rem;
    gap: 1.25rem;
    align-items: start;
  }
  .split-side {
    position: sticky;
    top: 5rem;
    max-height: calc(100vh - 6.5rem);
    overflow-y: auto;
  }
  .split-side table { font-size: 0.86rem; }
}

/* A category picker only needs to be as wide as a category name. Left to the
   default it stretches to the full width of its card, which reads as a text
   field rather than a short choice. */
.category-select {
  width: auto;
  max-width: 15rem;
  min-width: 11rem;
}
@media (max-width: 640px) {
  /* On a phone the row is narrow anyway; let it use what's there. */
  .category-select { max-width: 100%; width: 100%; }
}

/* ---------- Proportion bars ---------- */

/* Shows where someone sits against a reference range. The range is drawn as a
   band rather than a line, because "about half" is the honest shape of a rule
   of thumb — a single target would imply a precision none of these have. */
.ratio-row { margin-top: 1.1rem; }
.ratio-track {
  position: relative;
  height: 22px;
  border-radius: 999px;
  background: #f1f0ee;
  overflow: hidden;
}
.ratio-band {
  position: absolute;
  top: 0;
  bottom: 0;
  background: rgba(47, 196, 178, 0.22);
}
.ratio-fill {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  background: linear-gradient(135deg, var(--accent-3) 0%, #5a3fd6 100%);
  border-radius: 999px;
  opacity: 0.85;
}
.ratio-caption {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  font-size: 0.88rem;
  margin-top: 0.35rem;
}

/* ---------- Pace bars: where you sit against your own plan ---------- */

/* Same track as the proportion bars, with one difference that carries the
   whole meaning: a marker showing how far into the year (or into a goal's
   timeline) you are. Fill past the marker is ahead of the plan, short of it
   is behind. Nobody should have to do arithmetic to read it. */
.pace-row { margin-top: 1.6rem; }
.pace-row:first-of-type { margin-top: 1.25rem; }

.pace-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
  margin-bottom: 0.4rem;
}

.pace-marker {
  position: absolute;
  top: -3px;
  bottom: -3px;
  width: 2px;
  background: var(--ink);
  opacity: 0.5;
  border-radius: 2px;
}

.pace-verdict {
  margin: 0.45rem 0 0;
  color: var(--ink-soft);
  font-size: 0.9rem;
}

/* Colour is the only thing tone does. "Watch" means worth a look, never
   "bad" — a plan being off is information about the plan as often as it's
   information about the month. */
.pace-badge {
  display: inline-block;
  padding: 0.15rem 0.6rem;
  border-radius: 999px;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  vertical-align: middle;
}
.pace-badge.pace-good { background: var(--good-bg); color: #0d7a4c; }
.pace-badge.pace-watch { background: #fff4e0; color: #8a5a00; }
.pace-badge.pace-quiet { background: #f1f0ee; color: var(--muted); }

.pace-good { color: var(--good); }
.pace-watch { color: #a06a00; }

/* ---------- The path (home screen) ---------- */

/* The one card that matters on the page, so it gets the only accent border
   on the screen. Everything else stays quiet on purpose. */
.step-now {
  border: 1px solid var(--accent-3);
  box-shadow: var(--shadow-md);
}

.step-now-stage {
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--accent-3);
}

.step-now-title { font-size: 1.65rem; margin: 0.4rem 0 0.6rem; }
.step-now-why { color: var(--ink-soft); font-size: 1.05rem; margin: 0 0 1.25rem; max-width: 60ch; }

.step-now-actions {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
}
.step-now-actions form { margin: 0; }

.step-now-note {
  color: var(--muted);
  font-size: 0.85rem;
  margin: 0.9rem 0 0;
}

.step-learn {
  display: flex;
  gap: 0.75rem;
  margin-top: 1.5rem;
  padding: 1rem 1.1rem;
  background: #f7f5ff;
  border-radius: var(--radius);
  border: 1px solid #e6e0ff;
}
.step-learn-icon { font-size: 1.2rem; line-height: 1.4; }
.step-learn p { color: var(--ink-soft); font-size: 0.9rem; margin: 0.3rem 0 0.5rem; }

.path-progress { margin: 1.25rem 0 2rem; }
.path-progress-caption { color: var(--muted); font-size: 0.85rem; margin: 0.5rem 0 0; }

.path-stage { margin-top: 2.25rem; }
.path-stage:first-of-type { margin-top: 1rem; }

.path-stage-head {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
}
.path-stage-icon { font-size: 1.35rem; line-height: 1.4; }
.path-stage-head h3 { margin: 0; }
.path-stage-head p { color: var(--muted); font-size: 0.88rem; margin: 0.15rem 0 0; max-width: 55ch; }

.path-stage-count {
  margin-left: auto;
  flex-shrink: 0;
  font-size: 0.78rem;
  font-weight: 700;
  color: var(--muted);
  background: #f1f0ee;
  border-radius: 999px;
  padding: 0.15rem 0.6rem;
}

/* A closed stage is dimmed, never hidden. Seeing what's ahead is what makes
   this a path instead of a wall. */
.path-stage.is-closed .path-stage-head { opacity: 0.55; }

.path-steps {
  list-style: none;
  margin: 0.9rem 0 0;
  padding: 0 0 0 0.35rem;
  border-left: 2px solid var(--border);
}

.path-step {
  display: flex;
  align-items: flex-start;
  gap: 0.7rem;
  padding: 0.6rem 0 0.6rem 1rem;
  margin-left: -0.35rem;
}

.path-step-mark {
  flex-shrink: 0;
  width: 1.25rem;
  text-align: center;
  font-size: 0.9rem;
  line-height: 1.6;
  color: var(--border-strong);
}
.path-step.is-done .path-step-mark { color: var(--good); }
.path-step.is-current .path-step-mark { color: var(--accent-3); }

.path-step-body { flex: 1; min-width: 0; }
.path-step-title { font-weight: 600; }
.path-step-note { color: var(--muted); font-size: 0.85rem; margin: 0.1rem 0 0; max-width: 58ch; }
.path-step-now { color: var(--accent-3); font-weight: 600; }

.path-step.is-done .path-step-title { font-weight: 500; color: var(--muted); }
.path-step.is-locked, .path-step.is-waiting { opacity: 0.6; }

.path-step-tag {
  font-size: 0.7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
  background: #f1f0ee;
  border-radius: 999px;
  padding: 0.05rem 0.45rem;
  margin-left: 0.4rem;
  vertical-align: 0.08em;
}

.path-step form { margin: 0; }
.path-step-undo {
  background: none;
  border: none;
  padding: 0.2rem 0.4rem;
  font: inherit;
  font-size: 0.78rem;
  color: var(--muted);
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.15s ease;
}
.path-step:hover .path-step-undo,
.path-step-undo:focus { opacity: 1; text-decoration: underline; }

.later-list { color: var(--ink-soft); padding-left: 1.1rem; }
.later-list li { margin: 0.6rem 0; }
.later-list strong { color: var(--ink); }

/* ---------- The first look at real numbers ---------- */

/* Shown once, immediately before someone's own money appears for the first
   time. Quiet on purpose — this is the sentence that decides whether they
   scroll or close the tab. */
.first-look {
  margin-top: 1.5rem;
  padding: 1.1rem 1.25rem;
  border-left: 3px solid var(--accent-2);
  background: #f3fbfa;
  border-radius: 0 var(--radius) var(--radius) 0;
}
.first-look p { color: var(--ink-soft); margin: 0.55rem 0; max-width: 62ch; }
.first-look p:first-child { margin-top: 0; }
.first-look p:last-child { margin-bottom: 0; }
.first-look .lead { color: var(--ink); font-size: 1.05rem; }

/* Three tiles want to be three, not three-across-a-six-wide-grid. */
.stat-grid-three { grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }

/* ---------- Lesson nudges ---------- */

/* Deliberately not a modal and not a toast. It sits in the page flow, waits,
   and goes away for good when closed. */
.nudge {
  background: #f7f5ff;
  border: 1px solid #e6e0ff;
  border-radius: var(--radius-lg);
  padding: 1.15rem 1.35rem;
  margin-bottom: 1.25rem;
}

.nudge-head {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
}
.nudge-head strong { flex: 1; font-size: 1.02rem; }
.nudge-head form { margin: 0; flex-shrink: 0; }
.nudge-icon { font-size: 1.1rem; line-height: 1.5; }

.nudge p {
  color: var(--ink-soft);
  margin: 0.6rem 0 0;
  max-width: 68ch;
}

.nudge-actions {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  flex-wrap: wrap;
  margin-top: 1rem !important;
}
.nudge-note { color: var(--muted); font-size: 0.83rem; }

/* Quiet, but never hidden — dismissing has to be as easy as accepting. */
.nudge-close {
  background: none;
  border: none;
  padding: 0.1rem 0.3rem;
  font: inherit;
  font-size: 0.83rem;
  color: var(--muted);
  cursor: pointer;
}
.nudge-close:hover { color: var(--ink); text-decoration: underline; }

/* ==========================================================================
   Editorial layer — see your money
   ==========================================================================
   Full-width canvas, thin rules instead of endless cards, a typewriter face
   for display type and a plain sans for anything with numbers in it. Warm
   off-white rather than sterile white; charcoal rather than pure black.
   ========================================================================== */

:root {
  --paper: #faf7f2;
  --ink: #23201d;
  --ink-soft: #4a453f;
  --muted: #7d766d;
  --rule: #e3ddd3;
  --accent: #14110f;          /* the one accent — near-black, not red */
  --accent-quiet: #8a8279;    /* for rules, bars, and anything decorative */
  --mono: ui-monospace, "SFMono-Regular", "IBM Plex Mono", "Courier New", monospace;
}

main:has(.sheet) {
  max-width: min(1280px, 92vw);
  padding: 0 0 5rem;
}

.sheet {
  padding: 3rem 0;
  border-bottom: 1px solid var(--rule);
}
.sheet:last-of-type { border-bottom: none; }

.sheet h1 {
  font-family: var(--mono);
  font-size: clamp(1.9rem, 3.6vw, 2.9rem);
  font-weight: 500;
  letter-spacing: -0.02em;
  margin: 0 0 0.75rem;
  /* No gradient text here — this heading has to stay readable, not perform. */
  background: none;
  -webkit-text-fill-color: currentColor;
  color: var(--ink);
}

.sheet h2 {
  font-family: var(--mono);
  font-weight: 500;
  font-size: 1.15rem;
  letter-spacing: 0.01em;
  margin: 0;
}

.sheet .lead { font-size: 1.12rem; color: var(--ink-soft); max-width: 46ch; margin: 0 0 1rem; }
.sheet .note { color: var(--muted); font-size: 0.9rem; max-width: 62ch; }
.sheet .actions { display: flex; gap: 0.75rem; flex-wrap: wrap; margin-top: 1.5rem; }

/* The four numbers. */
.headline-figures {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 2.5rem 3rem;
  margin: 2.75rem 0 2rem;
}

.figure-label {
  font-family: var(--mono);
  font-size: 0.8rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--muted);
}
.figure-value {
  font-size: clamp(2rem, 4.5vw, 3rem);
  font-weight: 700;
  letter-spacing: -0.03em;
  margin-top: 0.3rem;
  font-variant-numeric: tabular-nums;
}

.remaining {
  font-family: var(--mono);
  font-size: 1.05rem;
  color: var(--ink-soft);
  padding-top: 1.5rem;
  border-top: 1px solid var(--rule);
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
}
.remaining strong { font-size: 1.5rem; color: var(--ink); font-variant-numeric: tabular-nums; }

.group-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  padding-bottom: 0.9rem;
  border-bottom: 1px solid var(--rule);
}
.group-total {
  font-size: 1.35rem;
  font-weight: 700;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}

/* Rows, not cards. Reads like a ledger and clears like an inbox. */
.ledger { list-style: none; margin: 0; padding: 0; }

.ledger-row {
  display: grid;
  grid-template-columns: minmax(9rem, 14rem) 1fr auto;
  align-items: center;
  gap: 0.75rem 1.25rem;
  padding: 0.85rem 0;
  border-bottom: 1px solid var(--rule);
}
.ledger-row:last-child { border-bottom: none; }

.ledger-name { font-weight: 600; }
.ledger-amount { font-variant-numeric: tabular-nums; font-weight: 600; text-align: right; }

.ledger-bar {
  display: block;
  height: 5px;
  background: var(--rule);
  border-radius: 3px;
  overflow: hidden;
}
.ledger-fill {
  display: block;
  height: 100%;
  background: var(--accent);
  opacity: 0.85;
}

/* A goal gets a bigger bar than a budget line does. .ledger-bar is 5px and
   hidden on a phone, which is right for decoration beside a figure — this one
   is the point of the row, so it stays and it's worth looking at. */
.goal-list { list-style: none; margin: 1.5rem 0 0; padding: 0; }
.goal { padding: 1.1rem 0; border-bottom: 1px solid var(--rule); }
.goal:last-child { border-bottom: none; }
.goal-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
}
.goal-head strong { font-size: 1.05rem; }
.goal-figure { font-family: var(--mono); font-variant-numeric: tabular-nums; }
.goal-bar {
  display: block;
  height: 14px;
  margin: 0.6rem 0 0;
  background: var(--rule);
  border-radius: 999px;
  overflow: hidden;
}
/* Amber while it's filling, green once it's there. Not red: a goal is short
   of its target from the day it's created, and red would tell someone they're
   failing at something they just started. Amber says "on the way". */
.goal-fill { display: block; height: 100%; background: #b5731a; }
.goal-bar { background: #f7ecdb; }
.goal-said { margin: 0.5rem 0 0; font-size: 0.9rem; color: var(--ink-soft); }

/* What's in it, and what it's for. Typed rather than counted — a fund can
   have a starting balance or interest that never showed up as a transaction. */
.goal-fields {
  display: flex;
  align-items: baseline;
  gap: 0.5rem 0.9rem;
  flex-wrap: wrap;
  margin: 0.75rem 0 0;
}
.goal-fields label {
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
.goal-fields input {
  width: 8rem;
  margin: 0;
  font-family: var(--mono);
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--ink);
  border-color: var(--ink);
  padding: 0.3rem 0.5rem;
}
.goal-note { font-size: 0.82rem; color: var(--muted); }

/* The dashboard's goals table. Figures centred under the heading that names
   them rather than hung off the right edge — nobody adds these three columns
   up, they read across one goal. */
.goal-table th.col-amount,
.goal-table td.col-amount,
.goal-table th.col-left,
.goal-table td.col-left { text-align: center; }

/* Equal widths, so In it, Target and Left sit the same distance apart. They
   were 7.5rem, 7.5rem and 9rem, which put Left further from Target than
   Target was from In it — three answers to the same question shouldn't be
   spaced as though one of them is a different kind of thing. */
.goal-table .col-amount,
.goal-table .col-left { width: 9rem; }

/* The name column was taking two thirds of the row, which pushed the three
   figures out to the right edge and stretched the bar with it. At 55% the
   figures come in and the bar lands just past halfway, which is where it
   reads as a bar rather than a rule across the row. */
.goal-table .col-merchant { width: 55%; }

/* Same pair as the goals page, so a goal looks the same on both screens —
   and the same thickness, so the bar on the dashboard, the bar on the goals
   page and the lessons bar all read as one component rather than three. The
   widths still differ: two of them live in a table column. */
.goal-table .ledger-bar { height: 14px; border-radius: 999px; }
.goal-table .ledger-fill { background: #b5731a; opacity: 1; }
.goal-table .ledger-fill.is-maxed { background: #0d7a4c; }
.goal-table .ledger-bar { background: #f7ecdb; }
.goal-table tr:has(.is-maxed) .ledger-bar { background: var(--good-bg); }

/* Label, figure and share centred on each other, but the group still sitting
   to the left of the page. 1fr columns stretched the figures across the whole
   sheet, which centred them on the page rather than on each other — the ask
   was the second thing, not the first. */
.headline-figures {
  grid-template-columns: repeat(auto-fit, minmax(180px, max-content));
  justify-content: start;
}
.headline-figures .figure { text-align: center; }
.figure-share {
  margin-top: 0.2rem;
  font-size: 0.82rem;
  color: var(--muted);
}

/* Reaching one is the only moment in this app worth a bit of noise. Green
   because it's the one state that is unambiguously good news and nobody has
   to act on it — the same green the counter uses for money still to place,
   so it isn't a new colour. */
/* Wider than the 62ch reading measure the rest of the app uses for prose.
   These are two or three short lines each with a link on the end, not
   paragraphs, and at the measure they used less than half the sheet while the
   right-hand side sat empty. Scoped to this section so nothing else moves. */
#saving-for .roadmap,
#saving-for > p,
#saving-for > .note { max-width: 75%; }

/* For a bar that isn't inside a .goal — the lessons one on the dashboard. */
.goal-fill.is-complete { background: #0d7a4c; }
.goal-bar.is-complete { background: var(--good-bg); }

.goal.is-done .goal-fill { background: #0d7a4c; }
.goal.is-done .goal-bar { background: var(--good-bg); }
.goal.is-done .goal-said { color: #0d7a4c; }

.ledger-target {
  grid-column: 2 / -1;
  font-size: 0.85rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.status { font-style: normal; margin-left: 0.6rem; }
.status-good  { color: #3f7a55; }
.status-watch { color: #8a6318; }
/* "Over your target" is information, not an alarm. No red, no banner. */
.status-over  { color: var(--accent); }

/* Balances go stale, and after three months they're quietly wrong. Red here
   rather than ink — the usual rule is that looking back at a month is never
   red, because nobody can change a month that's already happened. This isn't
   that: it's a fact about data that has an obvious fix, and the fix is the
   link sitting next to it. Same red as .flash-error and the assign counter,
   so it isn't a new colour. */
.note.is-stale, .note.is-stale a { color: #a1222d; }

@media (max-width: 640px) {
  .sheet { padding: 2rem 0; }
  .ledger-row { grid-template-columns: 1fr auto; }
  .ledger-bar { display: none; }
  .ledger-target { grid-column: 1 / -1; }
}

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}

/* ---------- Sort: rows you clear like an inbox ---------- */

.sort-progress {
  font-family: var(--mono);
  font-size: 1rem;
  color: var(--ink-soft);
  margin: 1.75rem 0 0;
}
.sort-progress strong { color: var(--ink); }

/* A row filed without the page reloading. Quiet on purpose — it's an
   acknowledgement, not an event. The row stays visible and in place so the
   group it was split out of doesn't move under the cursor. */
.child-tr.row-filed .child-desc,
.child-tr.row-filed .col-amount,
.child-tr.row-filed .col-date { color: var(--ink-soft); }

.row-said {
  font-family: var(--mono);
  font-size: 0.8rem;
  color: var(--ink-soft);
}

/* Pages of transactions. A rule above it rather than a box around it, same as
   every other group of controls on this page. Figures are mono because they
   are figures; the words either side of them are not. */
.page-count {
  font-family: var(--mono);
  margin: 0.35rem 0 0;
}

.pager {
  display: flex;
  flex-wrap: wrap;          /* Forty pages wraps to a second line rather than
                               squeezing forty unclickable targets onto one. */
  align-items: center;
  gap: 0.15rem;
  margin-top: 1.5rem;
  padding-top: 1rem;
  border-top: 1px solid var(--rule);
  font-family: var(--mono);
  font-size: 0.9rem;
}

.pager a,
.pager-here,
.pager-gap {
  padding: 0.4rem 0.6rem;
  min-width: 2.2rem;        /* Digits are a small target; 1 and 10 get the same
                               one so the row doesn't jitter as you page. */
  text-align: center;
}

.pager a {
  color: var(--ink-soft);
  text-decoration: none;
  border-bottom: 1px solid transparent;
}
.pager a:hover,
.pager a:focus { color: var(--ink); border-bottom-color: var(--ink); }

.pager-here {
  color: var(--ink);
  font-weight: 600;
  border-bottom: 2px solid var(--ink);
}

.pager-gap { color: var(--muted); padding-left: 0.2rem; padding-right: 0.2rem; }

.how-to {
  list-style: none;
  margin: 1.5rem 0 0;
  padding: 0;
  max-width: 62ch;
}
.how-to li {
  position: relative;
  padding-left: 1.1rem;
  margin: 0.5rem 0;
  color: var(--ink-soft);
}
.how-to li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.65em;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--accent-quiet);
}

/* The numbered sibling of .how-to, for a list where the order is the point.
   Same measure, same colour, same restraint — the only addition is a figure
   in the margin, in the typewriter face the headings use. Nothing fills in,
   goes green, or counts down: someone who hasn't started yet is being shown
   what they're agreeing to, not tracked through it. */
.roadmap {
  list-style: none;
  counter-reset: roadmap;
  margin: 1.5rem 0 0;
  padding: 0;
  max-width: 62ch;
}
.roadmap li {
  counter-increment: roadmap;
  position: relative;
  padding-left: 2.2rem;
  margin: 0 0 1rem;
  color: var(--ink-soft);
}
.roadmap li:last-child { margin-bottom: 0; }
.roadmap li::before {
  content: counter(roadmap);
  position: absolute;
  left: 0;
  top: 0;
  font-family: var(--mono);
  font-size: 0.95rem;
  /* Read, not decoration — so it follows the legibility pass below and takes
     --ink-soft rather than the grey the .how-to dot uses. */
  font-weight: 500;
  color: var(--ink-soft);
}
.roadmap strong { display: block; color: var(--ink); }

.sort-tools {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
  margin-top: 1.75rem;
}
.sort-views { margin-left: auto; display: inline-flex; gap: 1rem; font-size: 0.9rem; }
.sort-views a { color: var(--muted); text-decoration: none; }
.sort-views a.on { color: var(--ink); font-weight: 600; text-decoration: underline; text-underline-offset: 4px; }

/* One line. Select all, the find box and every action used to stack into
   three rows, which pushed the table down the page and made the bar read as
   three unrelated things instead of one row of controls. Everything here is
   sized to fit across; below 860 it wraps rather than shrinking, because a row
   of controls too small to hit is worse than two rows. */
.bulk-bar {
  display: flex;
  align-items: center;
  gap: 0.5rem 0.7rem;
  flex-wrap: wrap;
  padding-bottom: 1rem;
  border-bottom: 1px solid var(--rule);
}
.bulk-all { font-size: 0.9rem; color: var(--muted); display: inline-flex; align-items: center; gap: 0.4rem; white-space: nowrap; }
.bulk-actions { display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; margin-left: auto; }
/* The category box was the widest thing in the row by a distance — it sized
   itself to the longest category name. It holds a name, not a sentence. */
.bulk-actions select {
  width: auto;
  max-width: 9.5rem;
  margin-top: 0;
  font-size: 0.9rem;
  padding: 0.35rem 0.5rem;
}
.bulk-rule { font-size: 0.85rem; color: var(--muted); display: inline-flex; align-items: center; gap: 0.35rem; }
.button.danger { color: var(--accent); }

.sortlist { list-style: none; margin: 0; padding: 0; }

.sortrow {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  grid-template-areas:
    "pick what amount"
    ".    do   do";
  align-items: start;
  gap: 0.4rem 1rem;
  padding: 1rem 0;
  border-bottom: 1px solid var(--rule);
}
.sortrow:last-child { border-bottom: none; }
.sortrow[data-focus] { background: #fff8ee; }
.sortrow.is-aside { opacity: 0.55; }

.sortrow-pick { grid-area: pick; padding-top: 0.15rem; }
.sortrow-what { grid-area: what; min-width: 0; }
.sortrow-amount { grid-area: amount; font-variant-numeric: tabular-nums; font-weight: 600; }
.sortrow-do { grid-area: do; display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }

.sortrow-desc { display: block; font-weight: 600; overflow-wrap: anywhere; }
.sortrow-meta { font-size: 0.82rem; color: var(--muted); }

.row-form { display: contents; }
.suggested { font-size: 0.92rem; color: var(--ink-soft); }
.suggested.none { color: var(--muted); font-style: italic; }
.row-rule { font-size: 0.82rem; color: var(--muted); display: inline-flex; align-items: center; gap: 0.35rem; }
.why { font-size: 0.8rem; color: var(--muted); flex-basis: 100%; }
.sortrow-do select { width: auto; margin-top: 0; }

.aside-tag {
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

.linkish {
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  font-size: 0.85rem;
  color: var(--accent);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
}

.learn-skip { text-align: center; margin: 0.9rem 0 0; }
.learn-skip-warning {
  margin: 0.9rem 0 0;
  padding: 0.9rem 1rem;
  background: #fff8ee;
  border-radius: var(--radius);
  color: var(--ink-soft);
  font-size: 0.9rem;
}

.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

@media (max-width: 640px) {
  /* One thumb, one row at a time. */
  .sortrow {
    grid-template-columns: auto minmax(0, 1fr);
    grid-template-areas:
      "pick what"
      "pick amount"
      "do   do";
  }
  .sortrow-do { padding-top: 0.4rem; }
  .bulk-actions { margin-left: 0; }
}

/* ---------- Sort, grouped and tighter ---------- */

/* More rows on screen. The list is the interface here, so the page chrome
   gives up its padding first. */
.sheet-tight { padding: 2rem 0 1.5rem; }
.sheet-tight h1 { margin-bottom: 0.4rem; }
.sheet-tight .lead { margin-bottom: 0; }
.sheet-tight + .sheet-tight { padding-top: 0.5rem; }

.grp { border-bottom: 1px solid var(--rule); }
.grp:last-child { border-bottom: none; }

.grp-head {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) 7rem minmax(0, 20rem);
  align-items: center;
  gap: 0.9rem;
  padding: 0.5rem 0;
}

.grp-what { min-width: 0; display: flex; align-items: baseline; gap: 0.6rem; flex-wrap: wrap; }
.grp-name {
  font-weight: 600;
  font-size: 0.95rem;
  overflow-wrap: anywhere;
}
.grp-count, .grp-count-flat {
  font-size: 0.78rem;
  color: var(--muted);
  background: none;
  border: none;
  padding: 0;
  font-family: inherit;
}
.grp-count { cursor: pointer; text-decoration: underline; text-underline-offset: 2px; }
.grp.is-open .grp-count { color: var(--ink); }

.grp-amount {
  text-align: right;
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  font-size: 0.95rem;
}

.grp-do { display: flex; justify-content: flex-end; }
.grp-form { display: flex; align-items: center; gap: 0.45rem; flex-wrap: wrap; justify-content: flex-end; }
.grp-form select { width: auto; margin-top: 0; font-size: 0.85rem; padding: 0.3rem 0.5rem; }

.suggested {
  font-size: 0.85rem;
  color: var(--ink-soft);
  border: 1px solid var(--rule);
  border-radius: 999px;
  padding: 0.12rem 0.6rem;
  white-space: nowrap;
}
.row-rule { font-size: 0.78rem; color: var(--muted); display: inline-flex; align-items: center; gap: 0.25rem; }

/* The folded-away individual transactions. */
.grp-rows { list-style: none; margin: 0 0 0.6rem; padding: 0 0 0 2rem; }
.grp-rows li, .flatrow {
  display: grid;
  grid-template-columns: auto 4.5rem minmax(0, 1fr) 7rem;
  align-items: center;
  gap: 0.9rem;
  padding: 0.3rem 0;
  font-size: 0.85rem;
  color: var(--ink-soft);
}
.flatrow {
  grid-template-columns: auto 4.5rem minmax(0, 1fr) 7rem minmax(0, 16rem);
  padding: 0.45rem 0;
  border-bottom: 1px solid var(--rule);
}
.flatrow:last-child { border-bottom: none; }
.flatrow.is-aside { opacity: 0.5; }
.flatrow-cat { display: flex; justify-content: flex-end; align-items: center; gap: 0.5rem; }
.flatrow-cat select { width: auto; margin-top: 0; font-size: 0.82rem; padding: 0.25rem 0.45rem; }

.grp-row-date { color: var(--muted); font-variant-numeric: tabular-nums; }
.grp-row-desc { overflow-wrap: anywhere; }
.grp-row-amount { text-align: right; font-variant-numeric: tabular-nums; }

@media (max-width: 780px) {
  .grp-head {
    grid-template-columns: auto minmax(0, 1fr) auto;
    grid-template-areas: "pick what amount" ".  do  do";
    gap: 0.35rem 0.9rem;
  }
  .grp-pick { grid-area: pick; }
  .grp-what { grid-area: what; }
  .grp-amount { grid-area: amount; }
  .grp-do { grid-area: do; justify-content: flex-start; }
  .grp-form { justify-content: flex-start; }
  .grp-rows { padding-left: 1.2rem; }
  .grp-rows li { grid-template-columns: auto 4rem minmax(0, 1fr) auto; }
  .flatrow {
    grid-template-columns: auto 4rem minmax(0, 1fr) auto;
    grid-template-areas: "pick date desc amount" ". . cat cat";
  }
  .flatrow-cat { grid-area: cat; justify-content: flex-start; }
}

/* ---------- Sort: one face, one line ---------- */

/* The whole sorting page is typewriter. It's a ledger being worked through,
   and a single face keeps the eye moving down the column instead of noticing
   the furniture. Slightly smaller than the sans it replaces, since monospace
   sets wider at the same size. */
.page-sort,
.page-sort input,
.page-sort select,
.page-sort button,
.page-sort .button {
  font-family: var(--mono);
}
.page-sort { font-size: 0.94rem; letter-spacing: -0.005em; }
.page-sort .lead { font-size: 1rem; }
.page-sort .how-to li { font-size: 0.88rem; }
.page-sort .button { letter-spacing: 0; }

/* Everything in the group row sits on one line: category, Accept, Change,
   and the checkbox that says in full what it does. The shop name gives up
   space first — it can be truncated, the controls can't. */
.grp-head {
  grid-template-columns: auto minmax(4rem, 1fr) 6.5rem auto;
  gap: 0.75rem;
}
.grp-what { flex-wrap: nowrap; min-width: 0; }
.grp-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  overflow-wrap: normal;
}
.grp-count, .grp-count-flat { white-space: nowrap; }

.grp-form { flex-wrap: nowrap; white-space: nowrap; }
.grp-form .button { padding: 0.28rem 0.7rem; font-size: 0.8rem; }
.row-rule { white-space: nowrap; gap: 0.3rem; }
.row-rule b { font-weight: 600; color: var(--ink-soft); }
.suggested { font-size: 0.82rem; }

@media (max-width: 980px) {
  /* Below this the four controls genuinely don't fit; wrap rather than
     shrink them into something unclickable. */
  .grp-head { grid-template-columns: auto minmax(0, 1fr) auto; }
  .grp-form { flex-wrap: wrap; white-space: normal; justify-content: flex-start; }
}

/* ---------- Sort: a real table, fixed columns ---------- */

/* table-layout: fixed is the whole point — every row's category dropdown
   starts at the same x, every Accept at the same x. With auto layout each
   row sizes to its own content and nothing lines up down the page. */
.sorttable {
  width: 100%;
  table-layout: fixed;
  border-collapse: collapse;
  margin-top: 0.75rem;
  font-size: 0.88rem;
}

.sorttable thead th {
  text-align: left;
  font-weight: 600;
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  padding: 0 0.5rem 0.5rem;
  border-bottom: 1px solid var(--rule);
  vertical-align: bottom;
  line-height: 1.3;
}

.sorttable td {
  padding: 0.35rem 0.5rem;
  border-bottom: 1px solid var(--rule);
  vertical-align: middle;
}
.sorttable tbody tr:last-child td { border-bottom: none; }

.col-pick     { width: 2.2rem; }
.col-date     { width: 5rem; color: var(--muted); font-variant-numeric: tabular-nums; }
.col-merchant { width: auto; }
.col-amount   { width: 7.5rem; text-align: right; font-variant-numeric: tabular-nums; }
.col-category { width: 15rem; }
.col-accept   { width: 6.5rem; }
.col-always   { width: 7rem; text-align: center; }
.sorttable td.col-always { text-align: center; }

.sorttable .grp-name {
  font-weight: 600;
  display: inline-block;
  max-width: calc(100% - 4.5rem);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: bottom;
}
.sorttable .grp-count,
.sorttable .grp-count-flat { margin-left: 0.5rem; }

.sorttable select {
  width: 100%;
  margin-top: 0;
  font-size: 0.82rem;
  padding: 0.28rem 0.4rem;
}
.sorttable .button { padding: 0.28rem 0.75rem; font-size: 0.8rem; width: 100%; }

/* The folded-out individual visits sit under their shop, indented and quiet. */
.child-tr td { color: var(--muted); font-size: 0.82rem; padding-top: 0.2rem; padding-bottom: 0.2rem; }
.child-desc { padding-left: 1.25rem !important; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.child-date { display: inline-block; width: 4rem; }
.sorttable tr.is-ignored td { opacity: 0.5; }

@media (max-width: 860px) {
  /* Fixed columns stop being a kindness on a phone. Each row becomes a block
     with its own labels, and the whole thing still clears with one thumb. */
  .sorttable, .sorttable tbody, .sorttable tr, .sorttable td { display: block; width: 100%; }
  .sorttable thead { display: none; }
  .sorttable tr { padding: 0.6rem 0; border-bottom: 1px solid var(--rule); }
  .sorttable td { border: none; padding: 0.15rem 0; }
  .sorttable td.col-pick { display: inline-block; width: 2rem; }
  .sorttable td.col-merchant { display: inline-block; width: calc(100% - 2.5rem); }
  .sorttable td.col-amount { text-align: left; font-weight: 600; }
  .sorttable td.col-always::before { content: "Add to Rules: "; color: var(--muted); }
  .sorttable td.col-always { text-align: left !important; }
  .sorttable .button { width: auto; }
  .sorttable select { max-width: 22rem; }
}

/* ---------- Colour and legibility corrections ---------- */

:root {
  /* --accent-dark was never overridden, so buttons kept rendering the
     original pink-to-red gradient with a pink glow underneath. */
  --accent-dark: #2c2622;

  /* The old --muted (#7d766d) sat at roughly 4.3:1 on this paper — under the
     4.5:1 floor for small text, and most of the small text on the sorting
     screen used it. These are ~6.8:1 and ~9:1. */
  --muted: #5c564e;
  --ink-soft: #38332d;
  --border-strong: #b9b2a8;
}

/* Flat black. No gradient, no coloured glow. */
.button,
.site-header nav a.button {
  background: var(--accent);
  box-shadow: none;
  border: 1px solid var(--accent);
}
.button:hover {
  background: var(--accent-dark);
  border-color: var(--accent-dark);
  box-shadow: none;
  filter: none;
  transform: none;
}
.button:active { transform: translateY(1px); }

.button-ghost {
  background: transparent;
  color: var(--ink);
  border: 1px solid var(--border-strong);
}
.button-ghost:hover { background: #efeae2; color: var(--ink); }
.button.danger, .button-ghost.danger { color: var(--ink); }

/* Small text on the sorting screen was doing the most work and had the least
   size. Nothing here is under 0.8rem now. */
.sorttable { font-size: 0.94rem; }
.sorttable thead th { font-size: 0.78rem; color: var(--ink-soft); letter-spacing: 0.06em; }
.sorttable select { font-size: 0.88rem; }
.sorttable .button { font-size: 0.85rem; }
.child-tr td { color: var(--ink-soft); font-size: 0.88rem; }
.grp-count, .grp-count-flat { font-size: 0.85rem; }
.page-sort .how-to li { font-size: 0.95rem; color: var(--ink-soft); }
.page-sort .lead { color: var(--ink-soft); }
.sort-progress { color: var(--ink-soft); }
.bulk-all, .bulk-rule { font-size: 0.88rem; color: var(--ink-soft); }
.sort-views a { color: var(--ink-soft); }
.aside-tag { color: var(--ink-soft); }

/* Same floor on the numbers page. */
.figure-label { font-size: 0.82rem; color: var(--ink-soft); }
.sheet .note { color: var(--ink-soft); font-size: 0.92rem; }
.ledger-target { color: var(--ink-soft); font-size: 0.88rem; }
.nudge-note { color: var(--ink-soft); font-size: 0.86rem; }

/* ---------- Matte ---------- */

/* No gradients anywhere. Flat paper, flat ink, flat buttons. Every gradient
   in the file above this point is neutralised here rather than deleted, so
   the older screens that still reference them degrade to a solid colour
   instead of breaking. */

body {
  background: var(--paper);
  background-attachment: initial;
}

/* The two remaining loud hues. Purple links and teal accents were left over
   from the earlier palette; they're ink now, so black is the only colour
   the interface uses. */
:root {
  --accent-2: #5c564e;
  --accent-3: #23201d;
  --sun: #5c564e;
}

.hero h1 {
  background: none;
  -webkit-text-fill-color: currentColor;
  color: var(--ink);
}

.callout,
.checkpoint,
.disclaimer-banner,
.wizard-step.current .wizard-dot,
.progress-fill,
.progress-fill.progress-done,
.nudge {
  background-image: none;
}

.callout { background: #f4f0e9; border: 1px solid var(--rule); }
.checkpoint { background: #f4f0e9; }
.disclaimer-banner { background: #f4f0e9; }
.nudge { background: #f4f0e9; border: 1px solid var(--rule); }
.progress-fill { background: var(--accent); }
.progress-fill.progress-done { background: var(--accent); }

.wizard-step.current .wizard-dot {
  background: var(--accent);
  color: white;
  box-shadow: none;
}

input[type="checkbox"], input[type="radio"] { accent-color: var(--accent); }

a { color: var(--ink); text-decoration-thickness: 1px; }
a:hover { color: var(--accent-dark); }
.linkish { color: var(--ink); }

input[type="text"]:focus, input[type="email"]:focus, input[type="password"]:focus,
input[type="number"]:focus, input[type="date"]:focus, select:focus, textarea:focus {
  border-color: var(--ink);
  box-shadow: none;
  outline: 2px solid var(--ink);
  outline-offset: 1px;
}

/* ---------- Typefaces ---------- */

/* Suisse Intl is a licensed face from Swiss Typefaces — it can't be bundled
   or fetched, so it renders only where it's already installed. Behind it sit
   the closest things that are actually available everywhere: Inter, then the
   system neo-grotesque (SF Pro on a Mac, Segoe on Windows). If a webfont
   licence is bought later, dropping the @font-face in is the only change —
   this stack already asks for it first. */
:root {
  --sans: "Suisse Intl", "Suisse Int'l", Inter, -apple-system, BlinkMacSystemFont,
          "Segoe UI", "Helvetica Neue", Arial, sans-serif;
}

body,
input, select, textarea, button, .button {
  font-family: var(--sans);
  font-weight: 400;
  font-style: normal;
}

/* Weight is left to carry meaning rather than decoration: an amount, a
   merchant name, a heading. Everything else sits at 400. */
p, li, td, label, .lead, .note, .how-to li { font-weight: 400; }

/* The typewriter face stays where it was asked for: display type, and the
   sorting screen. It overrides the stack above because it comes later. */
.sheet h1, .sheet h2,
.figure-label, .remaining, .sort-progress,
.page-sort,
.page-sort input, .page-sort select, .page-sort button, .page-sort .button {
  font-family: var(--mono);
}

/* ---------- Legibility pass ---------- */

/* -webkit-font-smoothing: antialiased makes macOS render text *lighter* than
   it should. It's a fashion, and on a warm paper background it's the reason
   anything that isn't bold looks like it's fading out. Back to the default,
   which restores the weight the typeface actually has. */
body {
  -webkit-font-smoothing: auto;
  -moz-osx-font-smoothing: auto;
  color: var(--ink);
  line-height: 1.62;
}

/* 400 was too light once the face is rendering honestly. 450 reads as normal
   text rather than as thin text — it is not bold, and nothing here competes
   with a real 600. */
body, p, li, td, th, label, input, select, textarea, button, .button,
.lead, .note, .how-to li {
  font-weight: 450;
}

/* Secondary text stops using --muted for anything a person has to read. It's
   still there for genuinely tertiary marks, but body copy, table cells and
   captions now sit at --ink-soft (11.7:1) or --ink (15:1). */
.lead, .note, .how-to li, .sheet .note,
.sort-progress, .bulk-all, .bulk-rule,
.child-tr td, .ledger-target, .nudge p, .nudge-note {
  color: var(--ink-soft);
}

.sorttable { font-size: 0.98rem; }
.sorttable td { color: var(--ink); padding-top: 0.45rem; padding-bottom: 0.45rem; }
.sorttable thead th { color: var(--ink); font-weight: 600; font-size: 0.8rem; }
.child-tr td { font-size: 0.92rem; }
.sorttable select { font-size: 0.92rem; font-weight: 450; }
.sorttable .button { font-size: 0.88rem; font-weight: 600; }

.page-sort { font-size: 0.98rem; }
.page-sort .lead { font-size: 1.05rem; }
.page-sort .how-to li { font-size: 0.98rem; }

/* Monospace at 400 is thinner than the same size in a sans. Nudge the faces
   that use it up one step so the two read as equally solid. */
.page-sort, .sheet h1, .sheet h2, .figure-label, .remaining, .sort-progress {
  font-weight: 500;
}
.sheet h1 { font-weight: 600; }

.grp-name { font-weight: 600; }
.col-amount { font-weight: 600; }
.aside-tag, .grp-count, .grp-count-flat { color: var(--ink-soft); font-weight: 500; }

/* Sortable headers. The arrow is always present so the column reads as
   clickable before you click it, rather than only after. */
.sorttable thead th a {
  color: inherit;
  text-decoration: none;
  display: inline-flex;
  align-items: baseline;
  gap: 0.3rem;
}
.sorttable thead th a:hover { text-decoration: underline; text-underline-offset: 3px; }
.sorttable thead th .arrow { font-size: 0.85em; opacity: 0.35; }
.sorttable thead th.is-sorted { color: var(--ink); }
.sorttable thead th.is-sorted .arrow { opacity: 1; }
.sorttable thead th.col-amount a { justify-content: flex-end; }

/* Rules table: same shell as the sorting screen. */
.col-matches    { width: 9rem; color: var(--ink-soft); white-space: nowrap; }
.col-rowactions { width: 10rem; white-space: nowrap; text-align: right; }
.col-rowactions .button { width: auto; margin-left: 0.3rem; }
.col-rowactions form.inline { display: inline; }

.rule-match {
  width: 100%;
  min-width: 12rem;
  margin-top: 0;
  font-family: var(--mono);
  font-size: 0.9rem;
  padding: 0.28rem 0.45rem;
}

.sorttable .sort-button {
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink);
  cursor: pointer;
}
.sorttable .sort-button:hover { text-decoration: underline; text-underline-offset: 3px; }

/* The editorial rule only ever set display, gap and the rule above it, so
   everything else from the card-era .save-bar at the top of this file was
   still landing: a white rounded panel with a border, a shadow and a blur,
   stuck to the bottom of the window. On paper-and-ink pages it read as a
   floating card from a different app. Neutralised here rather than deleted
   up there, the same way the matte pass handles the old gradients. */
.save-bar {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  position: static;
  background: none;
  backdrop-filter: none;
  box-shadow: none;
  border: none;
  border-radius: 0;
  z-index: auto;
  margin: 1.25rem 0 0;
  padding: 1rem 0 0;
  border-top: 1px solid var(--rule);
}
#save-note { color: var(--ink-soft); font-size: 0.9rem; }

@media (max-width: 860px) {
  .sorttable td.col-rowactions { text-align: left; }
  .sorttable td.col-matches::before { content: "Matches now: "; color: var(--ink-soft); }
}

/* The apply-to-history opt-in. Sits apart from the save button so it reads as
   a separate decision rather than a modifier on one. */
/* One line, next to the button it changes. The full explanation lives in the
   confirmation, so it's read at the moment it matters rather than sitting on
   the page three times. */
.apply-existing {
  display: inline-flex;
  gap: 0.4rem;
  align-items: center;
  font-size: 0.88rem;
  color: var(--ink-soft);
  font-weight: 450;
  white-space: nowrap;
}
.apply-existing input { flex-shrink: 0; margin: 0; }

/* Ghost buttons read black rather than grey: black border, black label. They
   stay outlined so the solid buttons still carry more weight than they do. */
.button-ghost {
  border-color: var(--ink);
  color: var(--ink);
}
.button-ghost:hover { background: var(--ink); color: var(--paper); border-color: var(--ink); }
.button-ghost.danger { border-color: var(--ink); color: var(--ink); }

/* ---------- Sort: the same type mix as See your money ---------- */

/* Typewriter for display type, column labels and figures. The sans for
   anything that's a sentence — the lead, the instructions, a merchant name,
   a bank description. The whole page in one face made every sentence read
   like a label; this is the mix the numbers page already had. */

.page-sort,
.page-sort input,
.page-sort select,
.page-sort button,
.page-sort .button,
.page-sort .lead,
.page-sort .how-to li,
.page-sort .note,
.page-sort p,
.page-sort label,
.sorttable td,
.sorttable .grp-name,
.sorttable .child-desc,
.apply-existing {
  font-family: var(--sans);
}

.page-sort h1,
.page-sort h2,
.page-sort h3,
.sorttable thead th,
.sorttable .sort-button,
.sort-progress,
.sorttable td.col-amount,
.sorttable td.col-date,
.sorttable td.col-matches,
.sorttable .grp-count,
.sorttable .grp-count-flat,
.sorttable .child-date,
.sorttable input.rule-match,
.aside-tag {
  font-family: var(--mono);
}

/* Sans sets narrower than the mono it replaces, so the sentences can come
   back down a touch and the figures keep their weight. */
.page-sort { font-size: 0.95rem; }
.page-sort .lead { font-size: 1.06rem; }
.sorttable td.col-amount { font-weight: 600; letter-spacing: -0.01em; }
.sorttable thead th { letter-spacing: 0.07em; }

/* ---------- Duplicate review ---------- */

.dupe { padding-top: 1.25rem; }
.dupe + .dupe { padding-top: 1rem; }

.dupe-tag {
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--ink);
  border: 1px solid var(--ink);
  border-radius: 999px;
  padding: 0.1rem 0.6rem;
}
.dupe-tag.quiet { color: var(--ink-soft); border-color: var(--border-strong); }

.dupe-table { margin-top: 0.85rem; }
/* The two rows being compared, labelled down the left so the eye reads across
   one line rather than between two panels. */
.dupe-which {
  width: 12rem;
  font-family: var(--mono);
  font-size: 0.78rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--ink-soft);
  white-space: nowrap;
}

.dupe-choices { margin-top: 1rem; display: grid; gap: 0.4rem; max-width: 62ch; }
.dupe-choice {
  display: flex;
  gap: 0.6rem;
  align-items: flex-start;
  padding: 0.6rem 0.75rem;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  font-size: 0.9rem;
  color: var(--ink-soft);
  cursor: pointer;
}
.dupe-choice:hover { border-color: var(--ink); }
.dupe-choice input { margin-top: 0.25rem; flex-shrink: 0; }
.dupe-choice strong { color: var(--ink); font-weight: 600; }
.dupe-choice:has(input:checked) { border-color: var(--ink); background: #f4f0e9; }

/* The skipped-duplicates summary: one line, detail behind a disclosure. */
.skipped-line { font-size: 1rem; color: var(--ink); }
.skipped-line strong { font-family: var(--mono); }

.skipped-detail > summary {
  cursor: pointer;
  font-family: var(--mono);
  font-size: 0.85rem;
  letter-spacing: 0.04em;
  color: var(--ink);
  text-decoration: underline;
  text-underline-offset: 3px;
  list-style: none;
}
.skipped-detail > summary::-webkit-details-marker { display: none; }
.skipped-detail > summary::before { content: "▸ "; text-decoration: none; display: inline-block; }
.skipped-detail[open] > summary::before { content: "▾ "; }
.skipped-detail[open] > summary { margin-bottom: 0.5rem; }

/* ---------- The five-card deck ---------- */

.deck { position: relative; }
.deck-dots { display: flex; gap: 0.4rem; margin-bottom: 0.6rem; }
.deck-dot {
  width: 9px; height: 9px;
  border-radius: 50%;
  border: 1px solid var(--border-strong);
  background: transparent;
  padding: 0;
  cursor: pointer;
}
.deck-dot.active { background: var(--accent); border-color: var(--accent); }

.deck-count {
  font-family: var(--mono);
  font-size: 0.78rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin: 0 0 1rem;
}

/* One card at a time. Without JS every card stays visible and the page still
   reads top to bottom, which is the whole reason this isn't a modal. */
.deck-card { display: none; min-height: 17rem; }
.deck-card.active { display: block; }
.deck-card h2 { font-family: var(--mono); font-size: 1.35rem; margin: 0 0 0.6rem; }
.deck-card p { max-width: 60ch; color: var(--ink-soft); }
.deck-card .note { margin-top: 0.9rem; }

.deck-nav {
  display: flex;
  gap: 0.6rem;
  align-items: center;
  margin-top: 1.5rem;
  padding-top: 1.1rem;
  border-top: 1px solid var(--rule);
}
.deck-nav button:disabled { opacity: 0.35; cursor: default; }

/* Practice */
.quiz-count {
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin: 0 0 0.4rem;
}
.quiz-prompt { font-size: 1.1rem; color: var(--ink); max-width: 62ch; margin: 0 0 1.1rem; }

#practice .quiz-option {
  display: block;
  width: 100%;
  max-width: 34rem;
  text-align: left;
  font: inherit;
  font-size: 0.95rem;
  background: transparent;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: 0.6rem 0.85rem;
  margin-bottom: 0.45rem;
  cursor: pointer;
  color: var(--ink);
}
#practice .quiz-option:hover { border-color: var(--ink); }
#practice .quiz-option.correct { border-color: var(--ink); background: #f4f0e9; }

/* Needs beside Wants, so the pair reads as a comparison rather than two lists. */
.need-want {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: 1rem 2rem;
  margin: 1rem 0;
}
.need-want h3 {
  font-family: var(--mono);
  font-size: 0.92rem;
  font-weight: 500;
  margin: 0 0 0.5rem;
}
.need-want .learn-examples { margin: 0; }

/* The bucket names carry themselves now the pictures are gone. */
.deck-card h2 { font-weight: 600; }
.need-want h3 { font-weight: 600; color: var(--ink); }
.learn-step h2 { font-family: var(--mono); font-weight: 600; }

/* ---------- Chips and callouts, warmed up ---------- */

/* The example chips and the verdict box were a cool lavender and a
   purple-to-teal gradient — the last of the old palette, and both of them
   cold against warm paper. Outlined and flat now. */
.learn-examples li {
  background: transparent;
  border: 1px solid var(--rule);
  color: var(--ink-soft);
  padding: 0.22rem 0.65rem;
  font-size: 0.85rem;
}

.learn-verdict {
  background: #f4f0e9;
  background-image: none;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  font-weight: 450;
  color: var(--ink-soft);
}
.learn-verdict strong { color: var(--ink); font-weight: 600; }

/* The examples that are a phrase rather than a noun get their own line. A
   one-word chip and "the car that gets you to work" are doing different jobs,
   and shaping them the same buried the one that actually teaches something. */
.learn-examples li.eg {
  flex-basis: 100%;
  background: none;
  border: none;
  border-left: 2px solid var(--accent);
  border-radius: 0;
  padding: 0.1rem 0 0.1rem 0.7rem;
  margin-top: 0.15rem;
  font-size: 0.94rem;
  font-style: italic;
  color: var(--ink);
}

/* Each definition sits with its own examples, so the column reads as one
   idea rather than a header hunting for the paragraph that explains it. */
.need-want > div > p {
  margin: 0 0 0.6rem;
  color: var(--ink-soft);
  font-size: 0.94rem;
  max-width: 34ch;
}
.need-want h3 { margin-bottom: 0.35rem; }

/* ---------- Budget ---------- */

/* A category and its two numbers belong to each other, but stretched across the
   full sheet the name sat against one edge and the figures against the other,
   with a canyon between them. So a bucket is a narrower block centred on the
   page: label, columns, subtotal and add button all the same width, so the
   whole thing reads as one object with the page's margins either side of it.
   Two numbers to change if the balance ever looks off. */
.budget-bucket { --budget-cluster: 66%; --budget-indent: 1.5rem; }
.budget-bucket .budget-head,
.budget-bucket .sorttable[data-group],
.budget-bucket .add-in-section {
  width: var(--budget-cluster);
  margin-inline: auto;
}

.sorttable[data-group] .col-merchant { width: 45%; }
.sorttable[data-group] .col-amount   { width: 27.5%; }
.sorttable[data-group] .col-budget   { width: 27.5%; }

/* The words come in from the left of that block by a small step, so the names
   lean toward the figures rather than sitting hard against the rule. */
.budget-bucket .budget-head h2,
.budget-bucket th.col-merchant,
.budget-bucket tr.budget-group .col-merchant,
.budget-bucket .add-in-section { padding-left: var(--budget-indent); }

/* Centred rather than right-aligned. A ledger would hang these off the right
   edge so the digits stack, but these are a handful of whole-dollar figures
   being read one line at a time, not a column anyone adds up by eye. */
.sorttable[data-group] th.col-amount,
.sorttable[data-group] th.col-budget,
.sorttable[data-group] td.col-amount,
.sorttable[data-group] td.col-budget { text-align: center; }
.sorttable[data-group] .col-budget input { margin-inline: auto; text-align: center; }

/* Just the bucket's name now — its total moved into the totals row, where the
   column heading says what it is. Nothing left to lay out against the table's
   tracks, so the plain .group-head rule carries it. */

@media (max-width: 860px) {
  /* Full width and no indent on a phone: the table stops being a table here,
     and the inset would only eat room a phone hasn't got. */
  .budget-bucket { --budget-cluster: 100%; --budget-indent: 0rem; }
}

.col-budget { width: 9rem; text-align: right; }
.sorttable td.col-budget { text-align: right; }
/* The one thing on the page someone is actually here to do, so it carries the
   most ink: 600 for emphasis, an --ink hairline against the --rule ones
   everywhere else, and the figure itself in full --ink. The placeholder stays
   muted — a line nobody has set yet shouldn't shout as loudly as one they have. */
.col-budget input {
  width: 100%;
  max-width: 7rem;
  margin-top: 0;
  text-align: right;
  font-family: var(--mono);
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--ink);
  border-color: var(--ink);
  padding: 0.32rem 0.5rem;
}
.col-budget input::placeholder { color: var(--muted); font-weight: 450; }

/* The bucket subtotal rows, so the eye can find the group in a long list. */
tr.budget-group td {
  border-top: 1px solid var(--ink);
  border-bottom: none;
  padding-top: 1.1rem;
  font-family: var(--mono);
  font-size: 0.9rem;
}
tr.budget-group + tr td { padding-top: 0.5rem; }
.budget-total { font-weight: 600; }
.budget-name { padding-left: var(--budget-indent, 1rem) !important; }
#budget-note { color: var(--ink-soft); font-size: 0.9rem; }


/* The on-page walkthrough. No dimming scrim: the point is to look at the page,
   so the rest of it stays readable and one thing gets ringed. Structure is a
   rule and some space, same as everywhere else. */
.tour-spot {
  position: relative;
  z-index: 12;
  outline: 2px solid var(--ink);
  outline-offset: 5px;
  border-radius: 3px;
}
/* Already fixed to the corner — relative would drop it back into the flow. */
.assign-counter.tour-spot { position: fixed; }

.tour-tip {
  position: fixed;
  z-index: 13;
  width: min(23rem, calc(100vw - 1.5rem));
  padding: 0.9rem 1.05rem;
  background: var(--paper);
  border: 1px solid var(--ink);
  border-radius: 10px;
}
.tour-tip[hidden] { display: none; }
.tour-text { margin: 0; font-size: 0.9rem; line-height: 1.5; color: var(--ink); }
.tour-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  margin-top: 0.85rem;
  padding-top: 0.7rem;
  border-top: 1px solid var(--rule);
}

@media (max-width: 860px) {
  /* Anchoring a tip to an element is a wide-screen luxury. On a phone it sits
     along the bottom, where it can't cover whatever is being pointed at. */
  .tour-tip {
    left: 0.75rem !important;
    right: 0.75rem;
    top: auto !important;
    bottom: 0.75rem;
    width: auto;
  }
}

/* What's left to assign, riding along with the page. The buckets run for
   several screens and the verdict sits under all of them, so without this
   someone types the whole sheet before finding out whether it fits.
   A pill because .pace-badge already made that the shape a live figure takes;
   colour because the two answers need telling apart at a glance, and it reads
   from across the room in a way ink alone doesn't. */
.assign-counter {
  position: fixed;
  /* Sits just under the sticky header. Its height is measured and published as
     --header-h, because the nav wraps to two lines on a narrow window and a
     fixed offset would tuck the counter behind it. The fallback is the
     one-line height, for the moment before the script runs. */
  top: calc(var(--header-h, 4rem) + 0.75rem);
  right: 1.75rem;
  z-index: 9; /* under the site header, which owns 10 */
  display: flex;
  align-items: baseline;
  gap: 0.45rem;
  max-width: calc(100vw - 3.5rem);
  padding: 0.5rem 1rem;
  border: 1px solid var(--rule);
  border-radius: 999px;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  font-size: 0.8rem;
  text-decoration: none;
}
.assign-counter[hidden] { display: none; }
.assign-counter:hover { text-decoration: none; }
.assign-amount { font-size: 1.05rem; font-weight: 700; }

/* Money still to place, and money placed twice. Everything assigned is the
   quiet one — it's the resting state, not a prize. */
.assign-counter.is-left { background: var(--good-bg); border-color: #bfe9d5; color: #0d7a4c; }
.assign-counter.is-over { background: var(--bad-bg); border-color: #f6ccd1; color: #a1222d; }
.assign-counter.is-done { background: var(--paper); border-color: var(--rule); color: var(--ink); }

@media (max-width: 860px) {
  /* A phone has no spare corner up there, and the header wraps to two lines.
     Along the bottom it's out of the way of both, and nearer the thumb. */
  .assign-counter {
    top: auto;
    bottom: 1rem;
    right: 50%;
    transform: translateX(50%);
  }
}

/* The month picker sits in the column header it controls. */
.month-pick {
  width: auto;
  margin: 0;
  padding: 0.1rem 0.3rem;
  font-family: var(--mono);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  border-color: var(--rule);
  background: transparent;
}

.view-pick { margin-right: auto; }
.view-pick a { font-family: var(--mono); font-size: 0.8rem; }

.year-pick {
  width: auto;
  margin: 0 0.75rem 0 0;
  padding: 0.2rem 0.4rem;
  font-family: var(--mono);
  font-size: 0.85rem;
  font-weight: 600;
  border-color: var(--ink);
  background: transparent;
}
.year-locked { margin: 0 0.75rem 0 0 !important; font-size: 0.85rem; }

.findings .col-share { width: 8.5rem; text-align: right; }
.findings .col-ref { width: 10.5rem; text-align: right; color: var(--muted); }

/* Stacked on a phone, where the header row is gone and a bare "50%" would
   have nothing to be a percentage of. */
@media (max-width: 720px) {
  .findings td.col-share::before { content: "Your share of income: "; color: var(--muted); }
  .findings td.col-ref::before { content: "Starting point: "; color: var(--muted); }
  .findings td.col-share, .findings td.col-ref { text-align: left !important; width: 100%; }
}
.findings-note {
  display: block;
  font-family: var(--sans);
  font-size: 0.85rem;
  font-weight: 400;
  color: var(--muted);
}
.crumb { margin: 0 0 0.75rem; font-size: 0.85rem; }
.crumb a { color: var(--muted); text-decoration: none; }
.crumb a:hover { color: var(--ink); }

/* The findings page asks three questions in a row as h3s. Left at the default
   they were bold body text with a tight margin, which read as emphasis rather
   than as a new question starting. */
.page-sort h3 {
  font-size: 1rem;
  font-weight: 600;
  margin: 2.2rem 0 0.6rem;
}

/* The findings copy poses questions on their own line — "Would I miss this?".
   A thin rule and a step in, so the question reads as a beat rather than as
   another paragraph. No card, no tint. */
.page-sort p.pull {
  margin: 1rem 0 1rem 0.1rem;
  padding: 0.15rem 0 0.15rem 1rem;
  border-left: 2px solid var(--ink);
  font-size: 1.05rem;
  font-weight: 600;
}

/* Said plainly and early, because percentages built on half the data are worse
   than no percentages. A rule, not a coloured alert box — this is information,
   not an error. */
.coverage {
  margin: 1.25rem 0 0;
  padding: 0.9rem 0 0.2rem;
  border-top: 2px solid var(--ink);
}
.coverage p { margin: 0 0 0.6rem; }
.coverage .actions { margin: 0.9rem 0 0; }

/* "Can I afford this" — the verdict column carries a sentence, not a number,
   so it gets the room and the input column stays narrow. */
/* The adjustment column, from Kim's own sheet. Narrow input, and the sentence
   about what it does lives on its own row underneath so the table keeps its
   columns instead of one cell stretching to hold a paragraph. */
/* The same box as the Current Budget screen's, on purpose: it's the same act
   — a number you're deciding — and two different-looking inputs for it made
   the two pages feel like different tools. Mirrors .col-budget input. */
.adjust .col-plan input {
  width: 100%;
  max-width: 7rem;
  margin-top: 0;
  font-family: var(--mono);
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--ink);
  border-color: var(--ink);
  padding: 0.32rem 0.5rem;
}
.adjust .col-plan input::placeholder { color: var(--muted); font-weight: 450; }

/* One line's own "apply this". Sits under the new figure it would write, small
   enough that a column of them doesn't compete with the numbers, and hidden
   until that line actually goes over. */
.line-apply {
  display: inline-flex;
  margin-top: 0.4rem;
  padding: 0.2rem 0.55rem;
  font-size: 0.76rem;
  white-space: nowrap;
}

/* Naming a scenario, under the button that changes a budget and deliberately
   not beside it: one writes the question down, the other moves money. */
.scenario-bar { margin-top: 1.1rem; gap: 0.6rem; flex-wrap: wrap; }
.scenario-bar .scenario-name { font-size: 0.85rem; color: var(--ink-soft); }
.scenario-bar input {
  width: 100%;
  max-width: 18rem;
  margin: 0;
  font-size: 0.95rem;
}

/* Full width of the sheet, like every other table in the app. This was a
   narrower centred block for a while, copying the worksheet's cluster — five
   columns is too many for it, and the page ended up looking inset rather than
   tidy. The columns are still shares rather than fixed rems, so the figures
   spread across the row instead of hanging off its right edge. */
.adjust .col-merchant { width: 32%; }
.adjust .col-amount   { width: 17%; }
.adjust .col-plan     { width: 17%; }

/* Centred under their headings rather than right-aligned, matching the
   worksheet: a handful of whole-dollar figures read one line at a time, not a
   ledger anyone adds up by eye. */
.adjust th.col-amount,
.adjust td.col-amount,
.adjust th.col-plan,
.adjust td.col-plan { text-align: center; }
.adjust .col-plan input { margin-inline: auto; text-align: center; }

/* Budget Projections has five columns, four of them figures that only mean
   anything under their own heading — stacked into blocks it was $1,850 three
   times over with nothing saying which was which. So it stays a table at every
   width and scrolls sideways in a narrow window. It briefly refused to render
   under 860px and told people to open it on a laptop instead; 860px is most
   laptops that aren't maximised, so that took the page away from the people
   using it. */
.table-scroll { overflow-x: auto; }

@media (max-width: 860px) {
  /* Undo the stacking .sorttable does at this width, for this table only. */
  .adjust { display: table; width: auto; min-width: 44rem; }
  .adjust thead { display: table-header-group; }
  .adjust tbody { display: table-row-group; }
  .adjust tr { display: table-row; width: auto; padding: 0; }
  .adjust td { display: table-cell; width: auto;
               border-bottom: 1px solid var(--rule); padding: 0.55rem 0.5rem; }
  .adjust td.col-amount { text-align: center; font-weight: inherit; }
  .adjust-said td { border-bottom: none; }
}
.adjust td[data-new] , .adjust .is-changed { font-weight: 700; }
.adjust-said td { border-bottom: none !important; padding-top: 0 !important; }
/* 60ch is the reading measure for a paragraph. These are one sentence sitting
   under the row they answer, and holding them to it broke short answers across
   three lines for no reason. */
.adjust-said .verdict-fits,
.adjust-said .verdict-over,
.adjust-said .verdict-quiet { font-size: 0.88rem; line-height: 1.45; display: block; max-width: 92ch; }
/* Plain weight. These are the longest sentences on the page and the only ones
   that turn up in response to something you did — set in 600 they read as the
   page raising its voice at a decision nobody has made yet. */
.verdict-fits { font-weight: 450; }
.verdict-over { font-weight: 450; border-left: 2px solid var(--ink); padding-left: 0.6rem; display: inline-block; }
.verdict-quiet { color: var(--muted); }
@media (max-width: 720px) {
  .adjust td.col-plan { width: 100%; }
  .adjust .col-plan input { width: 8rem; text-align: left; }
}

/* The progress bar, back on each row — how full the year's plan is, and
   obviously maxed when it is. Sits under the name so the money columns stay
   in their own grid. */
.adjust .col-merchant .ledger-bar {
  display: block;
  height: 4px;
  margin-top: 0.45rem;
  max-width: 22rem;
  background: var(--rule);
  border-radius: 2px;
  overflow: hidden;
}
.adjust .ledger-fill {
  display: block;
  height: 100%;
  background: var(--ink);
  transition: width 0.15s ease-out;
}
.adjust .ledger-fill.is-maxed {
  background: repeating-linear-gradient(
    45deg, var(--ink), var(--ink) 3px, transparent 3px, transparent 6px);
}
.adjust .col-left { width: 9rem; text-align: right; font-variant-numeric: tabular-nums; }
.adjust .col-left .soft { color: var(--muted); }

/* Plan a one-time expense. Four fields on one line on a desktop, stacked on a
   phone — it is a sentence, not a form to fill out slowly. */
.plan-fields {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: 0.9rem 1.25rem;
  margin: 1.25rem 0 0;
}
.plan-fields label {
  display: block;
  font-family: var(--mono);
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: 0.3rem;
}
.plan-fields input, .plan-fields select { width: 100%; margin: 0; }
/* display: grid beats the browser's own [hidden] { display: none }, which is a
   single-attribute selector — same reason .button[hidden] exists above. The
   budget worksheet keeps its goal fields hidden until they're asked for. */
.plan-fields[hidden] { display: none; }
.plan-decide .actions { gap: 0.5rem; flex-wrap: wrap; }

/* The month sits beside the amount rather than under it: "if this becomes
   5,000 from August" is one sentence and should read as one line. Sized to its
   own text — every option is "from Xxx", so auto is exactly wide enough — and
   the amount box shrinks to make room, on income rows only, because they're
   the only ones carrying two controls in a cell. */
.adjust .from-month {
  width: auto;
  margin: 0 0 0 0.4rem;
  padding: 0.2rem 0.3rem;
  font-family: var(--mono);
  font-size: 0.78rem;
  vertical-align: middle;
}
.adjust[data-bucket="income"] td.col-plan { white-space: nowrap; }
.adjust[data-bucket="income"] .col-plan input {
  width: 4.75rem;
  vertical-align: middle;
}

/* Sits under its own bucket, so the button already knows what kind of category
   is being made and the dialog opens with that answered. */
.add-in-section { margin: 0.9rem 0 0; }

.goals .col-plan { width: 8rem; }
.goals .col-plan input { width: 100%; text-align: right; }
.goals .col-left { width: 9rem; text-align: right; font-variant-numeric: tabular-nums; }
.goals .col-left .soft { color: var(--muted); }
.goals .col-merchant .ledger-bar {
  display: block; height: 4px; margin-top: 0.45rem; max-width: 22rem;
  background: var(--rule); border-radius: 2px; overflow: hidden;
}

/* The same box as the Current Budget worksheet's, and centred under its own
   heading rather than pinned to one edge of the row. These are a handful of
   whole-dollar figures read one line at a time, next to the name that names
   them — not a column anyone adds up by eye. Mirrors .col-budget input. */
.accounts .col-plan { width: 11rem; text-align: center; }
.accounts th.col-plan { text-align: center; }
.accounts .col-plan input {
  width: 100%;
  max-width: 7rem;
  margin-inline: auto;
  text-align: center;
  font-family: var(--mono);
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--ink);
  border-color: var(--ink);
  padding: 0.32rem 0.5rem;
}
.accounts .col-plan input::placeholder { color: var(--muted); font-weight: 450; }

/* Delete sits at the end of the row now, as a button rather than a text link
   tucked under the amount. Right-aligned so a column of them lines up. */
.accounts .col-accept { width: 7rem; text-align: right; }
.choice-row { display: flex; flex-wrap: wrap; gap: 0.5rem 1.25rem; align-items: center; }
.choice-row .dupe-choice { margin: 0; }

/* Grouped nav. Opens on hover for a mouse and on click for everything else,
   because a hover-only menu is unusable on a phone and invisible to a
   keyboard. */
.nav-group { position: relative; display: inline-block; }
.nav-top {
  background: none;
  border: 0;
  padding: 0.35rem 0.7rem;
  font: inherit;
  color: var(--ink-soft);
  cursor: pointer;
  border-radius: 999px;
}
.nav-top:hover, .nav-group.open .nav-top { color: var(--ink); }
.nav-group.active .nav-top { color: var(--ink); font-weight: 700; background: var(--accent-soft); }
.nav-menu {
  display: none;
  position: absolute;
  top: 100%;
  left: 0;
  z-index: 60;
  min-width: 15rem;
  padding: 0.6rem 0;
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 10px;
  box-shadow: 0 12px 30px rgba(0, 0, 0, 0.08);
}
.nav-group:hover .nav-menu, .nav-group.open .nav-menu { display: block; }
.nav-blurb {
  display: block;
  padding: 0.1rem 1rem 0.5rem;
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  color: var(--muted);
}
.nav-menu a, .nav-menu .nav-soon {
  display: block;
  padding: 0.4rem 1rem;
  font-size: 0.92rem;
  text-decoration: none;
  color: var(--ink);
  white-space: nowrap;
}
.nav-menu a:hover { background: var(--accent-soft); }
.nav-menu .nav-soon { color: var(--muted); cursor: default; }
.nav-menu .nav-soon em {
  font-style: normal;
  margin-left: 0.5rem;
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
@media (max-width: 860px) {
  .nav-group { display: block; width: 100%; }
  .nav-menu { position: static; display: none; border: 0; box-shadow: none; min-width: 0; }
  .nav-group.open .nav-menu { display: block; }
  .nav-group:hover .nav-menu { display: none; }
  .nav-group.open:hover .nav-menu { display: block; }
}

/* Two emergency-fund targets side by side, with the one their answer points at
   marked. Both stay visible — the other one is the reason this one is a choice. */
.target-band { display: block; font-size: 0.85rem; color: var(--muted); }
.target-band strong { margin-left: 0.4rem; color: var(--ink); font-size: 0.95rem; }
.target-band.is-aimed { color: var(--ink); }
.target-band.is-aimed::before { content: "→ "; }

/* Hover the number, get the reasoning. Focusable too — a hover-only
   explanation doesn't exist for anyone on a keyboard or a touchscreen. */
.tip { position: relative; display: inline-block; cursor: help; border-bottom: 1px dotted var(--muted); }
.tip:focus { outline: 2px solid var(--ink); outline-offset: 2px; }
.tip-body {
  display: none;
  position: absolute;
  right: 0;
  bottom: calc(100% + 0.5rem);
  z-index: 70;
  width: 22rem;
  max-width: 78vw;
  padding: 0.8rem 0.95rem;
  text-align: left;
  white-space: normal;
  font-family: var(--sans);
  font-size: 0.85rem;
  font-weight: 400;
  line-height: 1.5;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--ink);
  border-radius: 8px;
  box-shadow: 0 10px 26px rgba(0, 0, 0, 0.12);
}
.tip-body strong { display: block; margin-bottom: 0.3rem; font-family: var(--mono); font-size: 0.8rem; }
.tip:hover .tip-body, .tip:focus .tip-body, .tip:focus-within .tip-body { display: block; }

.add-asset { display: inline-flex; gap: 0.5rem; margin-left: 1rem; }
.add-asset input { margin: 0; }
.add-asset input[name="new_asset_name"] { width: 12rem; }
.add-asset input[name="new_asset_value"] { width: 6rem; text-align: right; }
@media (max-width: 720px) {
  .add-asset { display: flex; width: 100%; margin: 0.75rem 0 0; }
  .add-asset input[name="new_asset_name"] { flex: 1; width: auto; }
}

/* The four statement headings. Bigger and heavier than a section heading
   elsewhere, because on this page they're the structure, not a label. */
.page-sort h2.statement {
  font-size: 1.5rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  margin: 0 0 0.2rem;
  padding-bottom: 0.5rem;
  border-bottom: 2px solid var(--ink);
}
.accounts .col-plan .linkish { display: block; margin-top: 0.3rem; font-size: 0.78rem; }

.inline-restore { display: inline; }

/* The controls on an admin user row, in one line rather than three stacked
   full-width blocks. Each form is its own element, so the row is the flex
   container and the forms sit in it. */
.admin-actions {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  flex-wrap: wrap;
}

/* Everything but the email and the name lines up under its own heading. Those
   two stay left — a column of addresses centred on itself can't be scanned. */
.admin-users .col-mid { text-align: center; }
.admin-users .col-mid .inline { justify-content: center; }
.admin-actions form.inline { display: flex; align-items: center; gap: 0.35rem; margin: 0; }
/* Trimmed so the three fit the column on one line — they were about nine
   pixels over and Delete now dropped to a row of its own. */
.admin-actions .button {
  white-space: nowrap;
  padding-inline: 0.7rem;
  font-size: 0.8rem;
}
.admin-actions input[type="text"] {
  width: 9.5rem;
  margin: 0;
  padding: 0.28rem 0.5rem;
  font-size: 0.85rem;
}

.row-lesson {
  display: inline-block;
  margin-top: 0.3rem;
  font-family: var(--mono);
  font-size: 0.76rem;
  letter-spacing: 0.03em;
}

.worth { margin-top: 1.75rem; padding-top: 1.25rem; border-top: 2px solid var(--ink); }
.worth h2 { margin: 0 0 0.75rem; }

/* A top-level link that isn't a menu — same shape as the group buttons so the
   row reads as one thing. */
a.nav-top { text-decoration: none; display: inline-block; }

/* Admin runs on the same furniture as the rest of the app now. Two things it
   still needs of its own: the index is a plain list of links, and a couple of
   its tables are wider than the money tables were designed for. */
.page-sort ul.admin-links { list-style: none; padding: 0; margin: 1.25rem 0 0; }
.page-sort ul.admin-links li { border-bottom: 1px solid var(--rule); }
.page-sort ul.admin-links li:last-child { border-bottom: none; }
.page-sort ul.admin-links a {
  display: block;
  padding: 0.8rem 0;
  font-size: 1.05rem;
  text-decoration: none;
}
.page-sort ul.admin-links a:hover { color: var(--ink); }
.sheet .sorttable { table-layout: auto; }

/* Find a description. Sits under the heading on the transactions pages: an
   input wide enough to hold what someone would actually type, the button that
   searches beyond this page, and the running count. */
.row-find {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
  margin: 0;
}
/* Narrow enough that Select all, the box and the actions stay on one line at
   the width this page actually gets. It wraps below that rather than
   squeezing the buttons. */
.row-find input[type="search"] {
  width: auto;
  flex: 0 1 8.5rem;
  margin: 0;
  padding: 0.35rem 0.55rem;
  font-size: 0.9rem;
}
.row-find .note { margin: 0; white-space: nowrap; }


/* My rules: the words a rule looks for get every pixel the other columns
   don't need. An old rule can hold a whole bank line — reference number and
   all — and you can't shorten what you can't read. The three columns beside
   it are a dropdown, a short count and two buttons, so they're pinned to what
   they actually need and the text field takes the rest. */
/* Repeated from the two-column layout section, because the .sheet width cap
   is declared after it and was quietly winning — a page-wide table inside a
   sheet stayed at the reading width it was trying to escape. */
main:has(.page-wide) { max-width: min(1600px, 96vw); }

/* Fixed layout, so the widths below are the widths — under the auto layout
   a .sheet table normally gets, a text input's preferred size is about twenty
   characters and the leftover space is shared out by content rather than
   given to the column that needs it. The selector carries .sheet to outrank
   the auto-layout rule above, which is more specific than a bare .page-rules
   and would otherwise win wherever it sat in the file. */
.sheet .sorttable.page-rules { table-layout: fixed; }

/* No width: with fixed layout the unsized column takes everything the sized
   ones leave. */
.page-rules .col-merchant { width: auto; }

/* And the field fills that column. `table input[type="text"]` sets width:auto
   for the narrow inputs in the money tables, and it outranks a bare
   .rule-match — so the cell grew and the box inside it stayed twenty
   characters wide. */
.page-rules .rule-match { width: 100%; min-width: 0; }
.page-rules .col-category { width: 13rem; }
.page-rules .col-matches { width: 8.5rem; }
.page-rules .col-rowactions { width: 9.5rem; }

@media (max-width: 1100px) {
  /* Below this the four columns stop fitting side by side, and squeezing the
     text field to make room defeats the point of widening it. */
  .page-rules .rule-match { min-width: 0; }
}

/* The list of imported files, on the end-of-import screen and on Files you've
   imported. Inside a .sheet the table lays out automatically, which gives the
   filename the room it needs — and takes it from the two columns that read
   worst when they wrap: a date broken across two lines, and an action that
   turns into "Remove this / file". */
.page-files .col-date,
.page-files .col-accept { white-space: nowrap; }

/* A monthly goal beside the section it belongs to. Blank is the common answer,
   so it never looks like a required field. */
.area-goal { margin: 1.25rem 0 0; }
.area-goal label {
  display: block;
  font-family: var(--mono);
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: 0.3rem;
}
.area-goal input { width: 9rem; margin: 0; text-align: right; }
.area-goal .findings-note { display: block; margin-top: 0.35rem; max-width: 42ch; }

/* ---------- Add-rule dialog ---------- */
/* The .modal-box above is card-era — rounded, shadowed, on --surface. This
   sits on the sorting screen, so it is pulled back to paper and hairlines. */

.rule-dialog .modal-box {
  background: var(--paper);
  border: 1px solid var(--rule-strong, var(--rule));
  border-radius: 2px;
  box-shadow: none;
  max-width: 34rem;
  padding: 1.6rem 1.7rem 1.4rem;
}

.rule-dialog h2 {
  font-family: var(--mono);
  font-size: 1.05rem;
  letter-spacing: 0.01em;
  margin-bottom: 0.9rem;
}

/* The bank's line, dimmed. It is what they recognise, and without it the
   pre-filled field looks like it came from nowhere. */
.rule-dialog-raw {
  font-family: var(--mono);
  font-size: 0.76rem;
  line-height: 1.5;
  color: var(--muted);
  word-break: break-all;
  padding-bottom: 0.9rem;
  margin-bottom: 1.1rem;
  border-bottom: 1px solid var(--rule);
}

.rule-dialog label {
  display: block;
  font-family: var(--mono);
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ink-soft);
  margin-bottom: 0.35rem;
}

.rule-dialog input[type="text"],
.rule-dialog select {
  width: 100%;
  font-family: var(--mono);
  font-size: 0.9rem;
  margin: 0;
}

.rule-dialog select { margin-bottom: 1.1rem; }

/* The count. Same size as a note, never red — nothing they type is wrong,
   it just does less than they probably meant. */
.rule-dialog-count {
  font-size: 0.82rem;
  line-height: 1.45;
  color: var(--muted);
  margin: 0.45rem 0 1.1rem;
  min-height: 1.2rem;
}
.rule-dialog-count.is-good { color: var(--ink-soft); }
.rule-dialog-count.is-one,
.rule-dialog-count.is-broad,
.rule-dialog-count.is-none { color: var(--ink); }

/* Beats `.rule-dialog label` above, which would render this uppercase mono. */
.rule-dialog label.rule-dialog-apply {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
  font-family: var(--sans);
  font-size: 0.85rem;
  text-transform: none;
  letter-spacing: 0;
  color: var(--ink);
  margin: 0 0 1.2rem;
}
.rule-dialog label.rule-dialog-apply input { margin: 0.15rem 0 0; width: auto; }

.rule-dialog-actions {
  display: flex;
  justify-content: flex-end;
  gap: 0.6rem;
  margin: 0;
  padding-top: 0.9rem;
  border-top: 1px solid var(--rule);
}
