/* Second Brain app styles — layered on styles.css tokens */

/* App-only tokens (styles.css owns the base four) + dark theme overrides.
   Theme is applied as data-theme on <html>; a head script sets it pre-paint. */
:root {
  --surface: #fbfaf7;
  --text-soft: #2c2c2a;
  --danger: #b30000;
  --graph-edge: rgba(19, 19, 19, 0.18);
  --graph-edge-sim: rgba(19, 19, 19, 0.30);

  /* One radius for the whole app. Every rounded corner refers to this token,
     so the shape is changed in one place rather than hunted per element. It
     lives here rather than in styles.css deliberately: styles.css is shared
     with the PRAZE landing page, which keeps its square corners. */
  --radius: 10px;
  /* Small controls only. --radius on a 22px checkbox rounds it into a circle,
     and a circle means "pick one of these" — the wrong promise for a checkbox. */
  --radius-sm: 4px;
}

[data-theme="dark"] {
  --bone: #131313;
  --ink: #f5f3ee;
  --gray: #9b9b94;
  --lime: #9db300;
  --border: rgba(245, 243, 238, 0.16);
  --surface: #1b1b19;
  --text-soft: #d8d6cf;
  --danger: #ff7a6b;
  --graph-edge: rgba(245, 243, 238, 0.20);
  --graph-edge-sim: rgba(245, 243, 238, 0.34);
}

/* Dragging down at the top of the app must not reload it. On Android that
   browser-style pull-to-refresh is one of the loudest "this is a web page"
   tells, and here it would throw away an unsaved capture. Scoped to the app
   rather than styles.css on purpose: the landing page is a web page, and
   pull-to-refresh is the right behaviour there. */
html,
body {
  overscroll-behavior-y: contain;
}

.brain {
  padding: 2.5rem 1.5rem 4rem;
  max-width: 760px;
}

/* Banner */

.banner[hidden] {
  display: none;
}

.banner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.75rem 1.5rem;
  font-family: var(--mono);
  font-size: 0.8rem;
  background: var(--lime);
  color: var(--ink);
  border-bottom: 1px solid var(--border);
}

.banner--error {
  background: var(--ink);
  color: var(--bone);
}

#banner-text {
  flex: 1;
}

.banner__action {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  background: none;
  color: inherit;
  border: 1px solid currentColor;
  padding: 0.3rem 0.7rem;
  cursor: pointer;
}

.banner__action[hidden] {
  display: none;
}

.banner__dismiss {
  font-family: var(--mono);
  font-size: 1rem;
  line-height: 1;
  background: none;
  border: none;
  color: inherit;
  cursor: pointer;
  padding: 0.25rem;
}

/* Interface icons — inline SVG on a 24×24 grid, sized in em so each one tracks
   the type it sits beside. currentColor throughout, so a button's hover/active
   colour and both themes are inherited rather than restated. */

.icon {
  width: 1.15em;
  height: 1.15em;
  flex: none;
  vertical-align: -0.2em;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: square;
  stroke-linejoin: miter;
}

.icon--fill {
  fill: currentColor;
  stroke: none;
}

/* Capture */

.capture {
  border: 1px solid var(--border);
  background: var(--surface);
  padding: 1.5rem;
  margin-bottom: 2rem;
}

.capture__title,
.capture__body,
.capture__tags,
.toolbar__search input {
  width: 100%;
  font-family: var(--body);
  font-size: 0.95rem;
  color: var(--ink);
  background: var(--bone);
  border: 1px solid var(--border);
  padding: 0.7rem 0.85rem;
  outline: none;
}

.capture__title:focus,
.capture__body:focus,
.capture__tags:focus,
.toolbar__search input:focus {
  border-color: var(--ink);
}

.capture__title {
  font-weight: 600;
  margin-bottom: 0.75rem;
}

/* autoGrow() sizes these to their content. Without a ceiling a long dictation
   or a pasted essay grew the box to tens of thousands of pixels and pushed the
   save button miles below the fold, so past this height the text scrolls
   inside the box instead. */
.capture__body,
.note__edit-body {
  max-height: 45vh;
  overflow-y: auto;
}

.capture__body {
  resize: vertical;
  min-height: 5.5rem;
  line-height: 1.5;
  margin-bottom: 0.75rem;
}

.capture__tags {
  font-family: var(--mono);
  font-size: 0.8rem;
  margin-bottom: 1rem;
}

.tag-suggest {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem;
  margin: -0.5rem 0 1rem;
}

.tag-suggest:empty {
  display: none;
}

.tag-suggest__label {
  font-family: var(--mono);
  font-size: 0.65rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gray);
  margin-right: 0.2rem;
}

.capture__actions {
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap; /* CAPTURE + Brain dump + hint + streak must break, not overflow, at 375px */
}

.capture__actions .btn {
  cursor: pointer;
}

.capture__hint {
  font-family: var(--mono);
  font-size: 0.7rem;
  color: var(--gray);
  letter-spacing: 0.05em;
}

/* Toolbar */

.toolbar {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  margin-bottom: 1rem;
}

.toolbar__search {
  position: relative;
  flex: 1;
}

.toolbar__search input {
  font-family: var(--mono);
  font-size: 0.85rem;
  padding-right: 6.5rem;
}

.toolbar__count {
  position: absolute;
  right: 0.85rem;
  top: 50%;
  transform: translateY(-50%);
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  color: var(--gray);
  pointer-events: none;
}

.toolbar__actions {
  display: flex;
  gap: 0.5rem;
}

.toolbar__btn {
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  background: none;
  color: var(--ink);
  border: 1px solid var(--border);
  padding: 0.55rem 1rem;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* flex layout strips the whitespace between an icon and its label, so the
     gap has to be declared; with no icon there is one item and it does nothing */
  gap: 0.5em;
  cursor: pointer;
  transition: background 0.2s, border-color 0.2s;
}

.toolbar__btn:hover {
  background: var(--lime);
  border-color: var(--ink);
}

/* Tag rail + chips */

.tag-rail {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: 1.75rem;
}

.tag-chip {
  position: relative;
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.06em;
  background: none;
  color: var(--ink);
  border: 1px solid var(--border);
  padding: 0.35rem 0.7rem;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}

/* The pill has to stay small to stay quiet, so the tap target is an invisible
   box centred on it rather than padding. Vertical only: widening it would let
   neighbouring chips in the same row steal each other's taps. The containers
   below carry a row-gap big enough that stacked rows cannot overlap either. */
.tag-chip::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  height: 44px;
}

.tag-chip:hover {
  border-color: var(--ink);
}

.tag-chip--active {
  background: var(--lime);
  border-color: var(--ink);
}

.tag-chip__count {
  color: var(--gray);
  margin-left: 0.35rem;
}

.tag-chip--active .tag-chip__count {
  color: var(--ink);
}

/* Note list */

.note-list {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.note {
  border: 1px solid var(--border);
  background: var(--surface);
  padding: 1.5rem;
}

.note--pinned {
  border-left: 3px solid var(--lime);
}

.note__pinned {
  font-family: var(--mono);
  font-size: 0.6rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  background: var(--lime);
  color: var(--ink);
  padding: 0.15rem 0.4rem;
  margin-right: 0.3rem;
}

.note__title {
  font-family: var(--display);
  font-size: 1.05rem;
  letter-spacing: -0.01em;
  margin-bottom: 0.6rem;
  overflow-wrap: break-word;
}

.note__body {
  font-size: 0.95rem;
  color: var(--text-soft);
  white-space: pre-wrap;
  overflow-wrap: break-word;
  margin-bottom: 1rem;
  /* cap measure for readability; wider than any phone, so no effect on mobile */
  max-width: 70ch;
}

/* Keeps a list scannable when one entry runs long. -webkit-box is still the
   only line-clamp that works everywhere this app runs; it is unprefixed in
   newer engines but the prefixed pair is what actually applies today. */
.note__body--clamped {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  overflow: hidden;
  margin-bottom: 0.4rem;
}

.note__more {
  font-family: var(--mono);
  font-size: 0.65rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  background: none;
  border: none;
  color: var(--gray);
  cursor: pointer;
  /* text stays small; the tappable box does not */
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  padding: 0;
  margin-bottom: 0.3rem;
}

.note__more:hover {
  color: var(--ink);
}

/* An author `display` beats the UA stylesheet's [hidden] { display: none },
   so setting .hidden on this button would otherwise do nothing at all and
   every short note would offer to expand text it is already showing. */
.note__more[hidden] {
  display: none;
}

.note__body mark,
.note__title mark {
  background: var(--lime);
  color: var(--ink);
  padding: 0 1px;
}

/* ==highlight== in a diary entry — same lime as a search hit, but with room
   for a whole phrase. The ink is pinned dark in both themes: the dark theme's
   olive lime sits far too close to its cream ink to read against. */
.note__body mark.hl {
  color: #131313;
  padding: 0.08em 0.28em;
}

.wiki-link {
  font: inherit;
  color: var(--ink);
  font-weight: 600;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  text-decoration: none;
  border-bottom: 2px solid var(--lime);
  transition: background 0.15s;
}

.wiki-link:hover {
  background: var(--lime);
}

.wiki-link--missing {
  font-weight: 400;
  color: var(--gray);
  border-bottom: 2px dashed var(--border);
}

.wiki-link--missing:hover {
  background: none;
  border-bottom-color: var(--ink);
  color: var(--ink);
}

.note__backlinks {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem;
  margin-bottom: 0.9rem;
}

.note__backlinks-label {
  font-family: var(--mono);
  font-size: 0.65rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gray);
  margin-right: 0.2rem;
}

.backlink {
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.04em;
  background: none;
  color: var(--ink);
  border: 1px solid var(--border);
  border-left: 3px solid var(--lime);
  padding: 0.3rem 0.6rem;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}

.backlink:hover {
  background: var(--lime);
  border-color: var(--ink);
}

@keyframes note-flash {
  from { background: var(--lime); }
  to { background: var(--surface); }
}

.note--flash {
  animation: note-flash 1.2s ease;
}

/* Row-gap has to exceed the 44px tap box minus the pill height, or a chip in
   the next row overlaps the one above and steals its taps. Column-gap stays
   tight because the tap box only grows vertically. Only visible when tags
   actually wrap, which is the minority of notes. */
.note__tags {
  display: flex;
  flex-wrap: wrap;
  gap: 1.4rem 0.4rem;
  /* clears the chips' tap boxes, which reach ~10px above the pills and would
     otherwise steal taps from whatever sits directly above — "Show more" did */
  margin-top: 0.7rem;
  margin-bottom: 0.9rem;
}

/* Quieter than the filter rail: on a card the tags are context, and the note
   itself should stay the loudest thing in it. Type and border only — the tap
   box set on .tag-chip is unchanged. */
.note__tags .tag-chip {
  font-size: 0.62rem;
  color: var(--gray);
  border-color: transparent;
  background: var(--bone);
  padding: 0.2rem 0.5rem;
}

.note__tags .tag-chip:hover {
  color: var(--ink);
  border-color: var(--border);
}

.note__meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  flex-wrap: wrap;
}

.note__date {
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gray);
}

.note__actions {
  display: flex;
  gap: 0.75rem;
}

/* The delete confirmation. Its own row under the actions rather than a swap of
   them, so that whatever opened it stays exactly where it was and nothing new
   can appear under the pointer. See the note above deleteOrActions() in
   brain.js for what that swap cost, and verify-confirm-geometry.js for the
   rule it now has to satisfy.

   Left-aligned on purpose. The actions sit right, so putting the question at
   the other end of the card makes it read as a new thing to answer rather than
   as the same row rearranged. */
/* Opening the confirm makes its card taller, and Chromium's scroll anchoring
   answers that by scrolling the page to hold some other element still —
   measured at 73px on a phone-width clip. The card the user just clicked jumps
   out from under the pointer, which puts a confirm button where the trigger
   was and reintroduces the exact bug the strip exists to prevent.

   Suppressed only while a confirm is open, not globally: .clip__thumb reserves
   no height, so a YouTube thumbnail resolving above the viewport genuinely does
   shift content, and anchoring is what stops that jump. Turning it off
   everywhere would trade one jump for another. A browser without :has() keeps
   anchoring and behaves as it does today — the same thing the wide-viewport
   rules degrade to. */
body:has(.confirm-strip) {
  overflow-anchor: none;
}

.confirm-strip {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-top: 0.25rem;
  padding-top: 0.75rem;
  border-top: 1px solid var(--border);
}

.confirm-strip__q {
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--danger);
}

/* The row of actions under a card. These read as quiet text, and must keep
   reading that way — so the type size is untouched and only the box around it
   grows to a thumb-sized target. */
.note__action {
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  background: none;
  border: none;
  color: var(--gray);
  cursor: pointer;
  padding: 0.15rem 0;
  border-bottom: 1px solid transparent;
  min-height: 44px;
  min-width: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
}

.note__action:hover {
  color: var(--ink);
  border-bottom-color: var(--lime);
}

.note__action--danger:hover {
  color: var(--danger);
}

/* Inline edit form */

.note--editing .note__edit-title,
.note--editing .note__edit-body,
.note--editing .note__edit-tags {
  width: 100%;
  font-family: var(--body);
  font-size: 0.95rem;
  color: var(--ink);
  background: var(--bone);
  border: 1px solid var(--border);
  padding: 0.6rem 0.75rem;
  outline: none;
  margin-bottom: 0.6rem;
}

.note--editing .note__edit-title:focus,
.note--editing .note__edit-body:focus,
.note--editing .note__edit-tags:focus {
  border-color: var(--ink);
}

.note--editing .note__edit-title {
  font-weight: 600;
}

.note--editing .note__edit-body {
  resize: vertical;
  min-height: 5rem;
  line-height: 1.5;
}

.note--editing .note__edit-tags {
  font-family: var(--mono);
  font-size: 0.8rem;
}

/* Empty states */

/* 3rem of vertical padding made these read as major blocks rather than as the
   placeholders they are — the box was mostly air. The border, the dashed
   treatment and the copy are all deliberate and unchanged; only the padding
   moved. */
.empty {
  border: 1px dashed var(--border);
  padding: 1.5rem 1.5rem;
  text-align: center;
}

.empty__title {
  font-family: var(--display);
  font-size: 1.1rem;
  margin-bottom: 0.5rem;
}

.empty__text {
  font-family: var(--mono);
  font-size: 0.8rem;
  color: var(--gray);
  margin-bottom: 1rem;
}

.empty .toolbar__btn {
  margin-top: 0.25rem;
}

.empty__tips {
  list-style: none;
  display: inline-block;
  text-align: left;
  font-family: var(--mono);
  font-size: 0.75rem;
  color: var(--gray);
  line-height: 2;
  margin-top: 0.5rem;
}

.empty__tips li::before {
  content: '→ ';
  color: var(--ink);
}

.empty__tips em {
  font-style: normal;
  color: var(--ink);
  background: var(--lime);
  padding: 0 3px;
}

/* Footer + noscript */

.brain-footer {
  padding: 0 1.5rem 1.5rem;
  max-width: 760px;
}

.brain-footer p,
.noscript {
  font-family: var(--mono);
  font-size: 0.7rem;
  color: var(--gray);
  line-height: 1.7;
}

/* The storage disclaimer is important information, but it is a footnote and
   was reading as a content block. Default state is one muted line; the full
   sentence is one tap away and unchanged.

   The summary carries more padding than its type needs because it is a
   control, and the 44px rule from Polish §6 applies to it exactly as it does
   to a button. The footer's own bottom padding pays for that, so the block is
   lighter overall rather than the same weight rearranged. */
.storage-note__line {
  font-family: var(--mono);
  font-size: 0.65rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--gray);
  opacity: 0.7;
  line-height: 1.5;
  /* 0.9rem, not 0.8: at 0.8 the summary measured 41px and Polish §6 asks for
     44. verify-tap-targets.js probes it now, so this cannot drift back. */
  padding: 0.9rem 0;
  cursor: pointer;
  list-style: none;             /* Firefox draws a marker without this */
}

.storage-note__line::-webkit-details-marker {
  display: none;
}

/* An affordance is needed — a muted line of caps does not otherwise read as
   openable. A character rather than a rotating glyph, so there is no animation
   to exempt from prefers-reduced-motion. */
.storage-note__line::after {
  content: ' +';
  opacity: 0.7;
}

.storage-note[open] .storage-note__line {
  opacity: 1;
}

.storage-note[open] .storage-note__line::after {
  content: ' \2212';           /* minus, not a hyphen */
}

.storage-note__line:hover {
  opacity: 1;
}

.storage-note p {
  padding-bottom: 0.5rem;
}

.noscript {
  display: block;
  padding: 1.5rem 0;
}

/* Tabs (Chrome feel) */

.tabs {
  display: flex;
  align-items: stretch;
  gap: 0;
  padding: 0 1.5rem;
  background: var(--bone);
  border-bottom: 1px solid var(--border);
  overflow-x: auto;
}

.tabs__tab {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  background: none;
  border: none;
  border-bottom: 3px solid transparent;
  color: var(--gray);
  padding: 0.8rem 0.9rem;
  cursor: pointer;
  white-space: nowrap;
  transition: color 0.15s, border-color 0.15s;
}

.tabs__tab:hover {
  color: var(--ink);
}

.tabs__tab--active {
  color: var(--ink);
  border-bottom-color: var(--lime);
  font-weight: 700;
}

.tabs__gear {
  margin-left: auto;
  background: none;
  border: none;
  font-size: 1rem;
  color: var(--gray);
  cursor: pointer;
  /* 44px tap target (was 22×43); stretches the tab row so tabs reach 44 too */
  min-width: 44px;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.tabs__gear:hover {
  color: var(--ink);
}

/* Settings */

.settings {
  padding: 1.5rem;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  max-width: 760px;
}

.settings__text {
  font-size: 0.85rem;
  color: var(--gray);
  max-width: 56ch;
  margin-bottom: 1rem;
}

.settings__row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.settings__row input {
  flex: 1;
  min-width: 200px;
  font-family: var(--mono);
  font-size: 0.8rem;
  color: var(--ink);
  background: var(--bone);
  border: 1px solid var(--border);
  padding: 0.55rem 0.85rem;
  outline: none;
}

.settings__row input:focus {
  border-color: var(--ink);
}

.settings__section {
  margin-top: 1.5rem;
}

.settings__inline-label {
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gray);
  align-self: center;
  margin-right: 0.25rem;
}

#model-row {
  margin-top: 0.75rem;
}

.toolbar__btn--active {
  background: var(--lime);
  border-color: var(--ink);
  color: #131313;
}

.settings__status {
  font-family: var(--mono);
  font-size: 0.72rem;
  color: var(--gray);
  margin-top: 0.75rem;
  min-height: 1em;
}

/* Views */

.view[hidden] {
  display: none;
}

.capture__streak {
  font-family: var(--mono);
  font-size: 0.7rem;
  color: var(--ink);
  letter-spacing: 0.03em;
}

/* Diary */

.diary-day {
  list-style: none;
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.12em;
  color: var(--gray);
  margin: 0.75rem 0 0.25rem;
}

.mood {
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0.15rem 0.45rem;
  margin-right: 0.35rem;
  border: 1px solid var(--border);
}

.mood--good { background: var(--lime); color: var(--ink); border-color: var(--ink); }
.mood--rough { background: var(--ink); color: var(--bone); }
.mood--neutral { color: var(--gray); }
.mood--activedot { border-left: 3px solid var(--lime); color: var(--ink); }

.diary-mic--live {
  border-color: var(--lime);
  color: var(--ink);
  animation: diary-mic-pulse 1.1s ease-in-out infinite;
}

@keyframes diary-mic-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(212, 255, 0, 0.55); }
  50% { box-shadow: 0 0 0 5px rgba(212, 255, 0, 0); }
}

.reflect {
  border-left: 3px solid var(--lime);
  padding: 0.5rem 0.85rem;
  margin-bottom: 0.9rem;
  background: rgba(212, 255, 0, 0.08);
}

.reflect__text {
  font-size: 0.9rem;
  color: var(--text-soft);
  font-style: italic;
  margin: 0.25rem 0 0.5rem;
}

.tag-chip--ai {
  border-left: 3px solid var(--lime);
}

.note__related .backlink {
  border-left-style: dashed;
}

/* Clips */

.clip-list {
  list-style: none;
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
}

.clip__badge {
  display: inline-block;
  font-family: var(--mono);
  font-size: 0.6rem;
  letter-spacing: 0.12em;
  padding: 0.2rem 0.5rem;
  border: 1px solid var(--ink);
  margin-bottom: 0.75rem;
}

.clip__badge--youtube { background: var(--ink); color: var(--bone); }
.clip__badge--instagram { background: var(--lime); color: var(--ink); }
.clip__badge--tiktok { background: var(--bone); color: var(--ink); }
.clip__badge--link { color: var(--gray); border-color: var(--border); }

.clip__thumb {
  display: block;
  width: 100%;
  max-height: 180px;
  object-fit: cover;
  border: 1px solid var(--border);
  margin-bottom: 0.75rem;
  filter: grayscale(35%);
}

.clip__player {
  position: relative;
  margin-bottom: 0.75rem;
}

.clip__player .clip__thumb {
  margin-bottom: 0;
}

.clip__play {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 54px;
  height: 54px;
  border-radius: 50%;
  background: var(--lime);
  color: #131313;
  border: 2px solid #131313;
  font-size: 1rem;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: transform 0.15s;
}

.clip__play:hover {
  transform: translate(-50%, -50%) scale(1.1);
}

.clip__embed {
  position: relative;
  padding-bottom: 56.25%;
  height: 0;
  margin-bottom: 0.75rem;
  border: 1px solid var(--border);
}

.clip__embed iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

.clip__open {
  text-decoration: none;
  border-bottom: 2px solid var(--lime);
}

/* Goals */

.streaks {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1px;
  border: 1px solid var(--border);
  background: var(--border);
  margin-bottom: 2rem;
}

.streak-card {
  background: var(--bone);
  padding: 1.25rem;
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  flex-wrap: wrap;
}

.streak-card__flame { font-size: 1.2rem; }

.streak-card__value {
  font-family: var(--display);
  font-size: 2rem;
}

.streak-card__label {
  font-family: var(--mono);
  font-size: 0.65rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--gray);
}

.goal-form__row {
  display: flex;
  gap: 0.75rem;
}

.goal-form__row .capture__title { margin-bottom: 0.75rem; }
.goal-form__target { max-width: 90px; }

.goal-list {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  margin-bottom: 2rem;
}

.goal {
  border: 1px solid var(--border);
  background: var(--surface);
  padding: 1.25rem 1.5rem;
}

.goal__head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem;
  margin-bottom: 0.75rem;
}

.goal__title {
  font-family: var(--display);
  font-size: 1rem;
}

.goal__count {
  font-family: var(--mono);
  font-size: 0.8rem;
  color: var(--gray);
  white-space: nowrap;
}

.goal__bar {
  height: 10px;
  border: 1px solid var(--ink);
  background: var(--bone);
  margin-bottom: 0.9rem;
}

.goal__fill {
  height: 100%;
  background: var(--lime);
  transition: width 0.25s ease-out;
}

.goal__actions {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}


.goal__plus {
  font-weight: 700;
  background: var(--lime);
  border-color: var(--ink);
}

.wins { margin-bottom: 1rem; }

.win {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.6rem 0;
  border-bottom: 1px solid var(--border);
  flex-wrap: wrap;
}

.win__title {
  font-family: var(--display);
  font-size: 1rem; /* Archivo Black floor — 0.9rem turned muddy */
}

.win__meta {
  font-family: var(--mono);
  font-size: 0.68rem;
  color: var(--gray);
}

/* Diary photos */

.photo-preview {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  flex-wrap: wrap;
  margin-top: 0.9rem;
}

.photo-preview[hidden] { display: none; }

.photo-thumb {
  display: block;
  width: 96px;
  height: 96px;
  object-fit: cover;
  border: 1px solid var(--border);
  background: var(--surface);
}

/* the thumbnail on a saved entry — a button, so it reads as tappable */
.note__photo {
  display: block;
  padding: 0;
  margin: 0 0 0.9rem;
  border: none;
  background: none;
  cursor: pointer;
  line-height: 0;
}

.note__photo .photo-thumb {
  width: 120px;
  height: 120px;
}

.note__photo:hover .photo-thumb {
  border-color: var(--lime);
}

.edit-photo {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  flex-wrap: wrap;
  margin-bottom: 0.9rem;
}

/* Full-size view. Deliberately plain — tap anywhere to close, no zoom or pan. */
.photo-view {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1.5rem;
}

.photo-view[hidden] { display: none; }

.photo-view__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(19, 19, 19, 0.88);
}

.photo-view__img {
  position: relative;
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  border: 1px solid var(--border);
}

.photo-view__close {
  position: absolute;
  top: 0.75rem;
  right: 0.75rem;
  min-width: 44px;
  min-height: 44px;
  color: #f5f3ee;
}

/* Todo */

.todo-add {
  display: flex;
  gap: 0.75rem;
  align-items: flex-start;
}

.todo-add__text {
  flex: 1;
  min-width: 0;
  margin-bottom: 0.75rem;
}

.todo-add__due {
  font-family: var(--mono);
  font-size: 1rem; /* 16px floor — anything smaller makes iOS zoom the page */
  padding: 0.5rem 0.6rem;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--ink);
  max-width: 100%;
}

.todo-add__due[hidden] { display: none; }

.todo-notice {
  font-family: var(--mono);
  font-size: 0.68rem;
  line-height: 1.5;
  color: var(--gray);
  border-left: 3px solid var(--lime);
  padding: 0.5rem 0 0.5rem 0.75rem;
  margin-bottom: 1.5rem;
}

.todo-notice[hidden] { display: none; }

.todo-list {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin: 0 0 1.5rem;
  padding: 0;
}

/* checkbox | text (+ due badge) | actions, with the due editor spanning all
   three when it opens. A grid rather than flex so a long todo wraps inside its
   own column instead of pushing the actions onto another line. */
.todo {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: start;
  gap: 0.6rem 0.75rem;
  border: 1px solid var(--border);
  background: var(--surface);
  padding: 0.85rem 1rem;
}

.todo__body {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}

.todo__check {
  flex: none;
  width: 1.35rem;
  height: 1.35rem;
  border: 2px solid var(--ink);
  background: var(--surface);
  cursor: pointer;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.todo--done .todo__check {
  background: var(--lime);
  border-color: var(--ink);
}

.todo--done .todo__check::after {
  content: '✓';
  color: #131313;
  font-weight: 700;
  font-size: 0.85rem;
  line-height: 1;
}

.todo__text {
  font-size: 0.95rem;
  line-height: 1.45;
  color: var(--ink);
  overflow-wrap: anywhere;
}

.todo--done .todo__text {
  color: var(--gray);
  text-decoration: line-through;
}

.todo__due {
  font-family: var(--mono);
  font-size: 0.64rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--gray);
  white-space: nowrap;
}

.todo__due--over {
  color: var(--danger);
}

.todo__actions {
  display: flex;
  gap: 0.75rem;
  white-space: nowrap;
}

.todo__due-edit {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem;
  padding-top: 0.75rem;
  border-top: 1px solid var(--border);
}

.todo__due-input {
  font-family: var(--mono);
  font-size: 1rem; /* 16px floor — see .todo-add__due */
  padding: 0.4rem 0.5rem;
  border: 1px solid var(--border);
  background: var(--bone);
  color: var(--ink);
  max-width: 100%;
}

.todo-done { margin-bottom: 2rem; }

.todo-done__toggle {
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gray);
  background: none;
  border: none;
  cursor: pointer;
  padding: 0.5rem 0;
}

.todo-done__toggle:hover { color: var(--ink); }

.todo-list--done { margin-top: 0.5rem; }

/* Graph */

.view--graph .label { display: flex; justify-content: space-between; flex-wrap: wrap; gap: 0.5rem; }

.graph-legend {
  font-family: var(--mono);
  font-size: 0.62rem;
  color: var(--gray);
  text-transform: none;
  letter-spacing: 0.04em;
}

.graph-legend__diary { color: var(--gray); opacity: 0.6; }

.graph-legend .icon { width: 0.85em; height: 0.85em; vertical-align: -0.08em; }

.graph-wrap {
  border: 1px solid var(--border);
  background: var(--surface);
  height: 60vh;
  min-height: 320px;
  position: relative;
}

#graph-canvas {
  width: 100%;
  height: 100%;
  display: block;
  touch-action: none;
}

.graph-tooltip {
  position: absolute;
  z-index: 5;
  max-width: 240px;
  background: var(--ink);
  color: var(--bone);
  padding: 0.5rem 0.7rem;
  pointer-events: none;
  border: 1px solid var(--border);
}

.graph-tooltip__title {
  font-family: var(--display);
  font-size: 1rem; /* Archivo Black floor — 0.75rem was unreadable */
  margin-bottom: 0.25rem;
  overflow-wrap: break-word;
}

.graph-tooltip__meta {
  font-family: var(--mono);
  font-size: 0.6rem;
  letter-spacing: 0.05em;
  opacity: 0.75;
}

#graph-empty {
  position: absolute;
  top: 50%;
  left: 0;
  right: 0;
  text-align: center;
  transform: translateY(-50%);
}

/* Cold-start hint: a note count below the similarity gate. Bottom strip,
   non-interactive so node dragging in the canvas passes straight through. */
.graph-cold {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  margin: 0;
  padding: 0.6rem 1rem;
  font-family: var(--mono);
  font-size: 0.68rem;
  line-height: 1.5;
  text-align: center;
  color: var(--gray);
  background: var(--surface);
  border-top: 1px solid var(--border);
  pointer-events: none;
}

.graph-cold[hidden] {
  display: none;
}

/* Graph settings: a flyout hanging below the gear icon, not overlapping it —
   the toggle stays visible and clickable while the panel is open, same as
   the app-wide gear in the tab bar. */
.graph-settings-toggle {
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
  z-index: 4;
  width: 36px;
  height: 36px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--gray);
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
}

.graph-settings-toggle:hover {
  color: var(--ink);
  border-color: var(--ink);
}

.graph-settings-toggle[hidden] {
  display: none;
}

.graph-settings {
  position: absolute;
  top: 3rem;
  right: 0.5rem;
  z-index: 5;
  width: min(280px, calc(100% - 1rem));
  max-height: calc(100% - 3.5rem);
  overflow-y: auto;
  background: var(--surface);
  border: 1px solid var(--border);
  padding: 1rem;
}

.graph-settings[hidden] {
  display: none;
}

.graph-settings .label {
  margin-bottom: 0.25rem;
}

.graph-settings__row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.3rem;
  margin-top: 0.7rem;
}

.graph-settings__label {
  flex: 1 1 100%;
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-soft);
}

.graph-settings__control {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex: 1 1 100%;
}

.graph-settings__slider {
  flex: 1;
  min-width: 0;
  accent-color: var(--lime);
}

.graph-settings__value {
  flex: 0 0 auto;
  font-family: var(--mono);
  font-size: 0.68rem;
  color: var(--gray);
  min-width: 2.8em;
  text-align: right;
}

.graph-settings__section {
  margin: 1rem 0 0;
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gray);
}

.graph-settings__toggle-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  margin-top: 0.7rem;
}

.graph-settings__toggle-row input[type="checkbox"] {
  width: 18px;
  height: 18px;
  accent-color: var(--lime);
}

/* Dashboard */

.dash {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
}

.dash-card {
  border: 1px solid var(--border);
  background: var(--surface);
  padding: 1.5rem;
}

.dash-card--hero {
  border-left: 4px solid var(--lime);
}

.dash-greeting {
  font-family: var(--display);
  font-size: 2rem;
  margin-bottom: 0.25rem;
}

.dash-tagline {
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.12em;
  color: var(--gray);
  margin-bottom: 1.25rem;
}

/* Dashboard capture box. Every option is a shortcut into a capture flow that
   already exists on another tab — nothing captures from here directly. The
   margin lives on this block rather than on the line above it, so spacing is
   the same whether or not the link-line is showing. */

.dash-capture {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-top: 1.25rem;
}

.dash-capture__main {
  flex: 1;
  min-width: 0;
}

.dash-capture__prompt {
  font-family: var(--body);
  font-size: 0.95rem;
  color: var(--gray);
  margin-bottom: 0.35rem;
}

.dash-capture__opts {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
}

.dash-capture__opt {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.55rem 0;
  border: 0;
  background: none;
  color: var(--ink);
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  cursor: pointer;
}

/* Divider between options, never before the first one. */
.dash-capture__opt + .dash-capture__opt {
  margin-left: 0.7rem;
  padding-left: 0.7rem;
  border-left: 1px solid var(--border);
}

.dash-capture__opt:hover {
  color: var(--gray);
}

.dash-capture__fab {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  border: 0;
  /* rounded square, not a circle: a pill reads as a floating action button
     borrowed from another app, where this is the app's own square-cornered
     language with the corners taken off */
  border-radius: 14px;
  /* Same pairing every other lime chip uses (.banner, .note__pinned): the dark
     theme darkens --lime and flips --ink, so the pair travels together. Do not
     pin either to a literal here — that is what makes it drift from the rest. */
  background: var(--lime);
  color: var(--ink);
  font-size: 1.3rem;
  cursor: pointer;
  box-shadow: 0 2px 10px var(--border);
}

.dash-row {
  display: block;
  width: 100%;
  text-align: left;
  font-family: var(--body);
  font-size: 0.9rem;
  color: var(--ink);
  background: none;
  border: none;
  border-bottom: 1px solid var(--border);
  padding: 0.6rem 0;
  cursor: pointer;
}

.dash-row:last-child {
  border-bottom: none;
}

.dash-row:hover {
  color: var(--ink);
  background: rgba(212, 255, 0, 0.10);
}

.dash-row__meta {
  display: block;
  font-family: var(--mono);
  font-size: 0.65rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--gray);
  margin-top: 0.2rem;
}

/* Weekly diary digest */

.digest {
  margin-bottom: 1.5rem;
}

#digest-btn {
  padding: 0.5rem 1rem;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  font-family: var(--mono);
  font-size: 0.65rem;
  letter-spacing: 0.1em;
  color: var(--ink);
  background: none;
  border: 1px solid var(--ink);
  cursor: pointer;
}

#digest-btn:hover:not(:disabled) {
  background: var(--lime);
  border-color: var(--lime);
  color: #131313;
}

#digest-btn:disabled {
  opacity: 0.45;
  cursor: default;
}

#digest-hint {
  margin-left: 0.75rem;
}

#digest-result {
  border: 1px solid var(--border);
  background: var(--surface);
  padding: 1rem 1.25rem;
  margin-top: 0.75rem;
}

.digest__row {
  font-family: var(--body);
  font-size: 0.9rem;
  color: var(--text-soft);
  margin: 0.4rem 0;
}

.digest__label {
  display: inline-block;
  min-width: 5.5rem;
  font-family: var(--mono);
  font-size: 0.65rem;
  letter-spacing: 0.1em;
  color: var(--gray);
}

.echo + .echo {
  margin-top: 1.25rem;
  padding-top: 1rem;
  border-top: 1px solid var(--border);
}

.echo__copy {
  font-family: var(--body);
  font-size: 0.9rem;
  color: var(--text-soft);
  margin-bottom: 0.35rem;
}

.echo__link {
  display: inline-block;
  margin-top: 0.6rem;
  padding: 0.35rem 0.75rem;
  font-family: var(--mono);
  font-size: 0.65rem;
  letter-spacing: 0.1em;
  color: var(--ink);
  background: none;
  border: 1px solid var(--ink);
  cursor: pointer;
}

.echo__link:hover {
  background: var(--lime);
  border-color: var(--lime);
  color: #131313;
}

.resurface-row {
  display: flex;
  align-items: center;
  border-bottom: 1px solid var(--border);
}

.resurface-row:last-child {
  border-bottom: none;
}

.resurface-row .dash-row {
  flex: 1;
  min-width: 0;
  border-bottom: none;
}

.resurface-dismiss {
  flex: none;
  background: none;
  border: none;
  color: var(--gray);
  font-family: var(--mono);
  font-size: 0.8rem;
  line-height: 1;
  padding: 0.5rem;
  cursor: pointer;
}

.resurface-dismiss:hover {
  color: var(--danger);
}

.dash-bar {
  display: block;
  height: 6px;
  border: 1px solid var(--ink);
  background: var(--bone);
  margin-top: 0.4rem;
}

.dash-bar__fill {
  display: block;
  height: 100%;
  background: var(--lime);
}

/* Select mode + synthesize */

.note--selectable {
  position: relative;
  padding-left: 3.4rem;
  cursor: pointer;
}

.note--selected {
  border-color: var(--ink);
  background: rgba(212, 255, 0, 0.08);
}

.note__check {
  position: absolute;
  left: 1rem;
  top: 1.35rem;
  width: 1.35rem;
  height: 1.35rem;
  border: 2px solid var(--ink);
  background: var(--surface);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 0.85rem;
  color: var(--ink);
}

.note--selected .note__check {
  background: var(--lime);
}

.note--selected .note__check::after {
  content: '✓';
  color: #131313;
  font-weight: 700;
}

.synth-bar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 0.75rem;
  padding: 0.7rem 1rem;
  background: var(--ink);
}

.synth-bar[hidden] {
  display: none;
}

.synth-bar__count {
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.1em;
  color: var(--bone);
}

.synth-bar__btn {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  background: var(--lime);
  color: #131313;
  border: none;
  min-height: 44px;
  padding: 0 1.1rem;
  cursor: pointer;
}

.synth-bar__cancel {
  background: none;
  border: none;
  color: var(--bone);
  font-size: 1.1rem;
  min-width: 44px;
  min-height: 44px;
  cursor: pointer;
}

.synth-result {
  border: 1px solid var(--border);
  border-left: 4px solid var(--lime);
  background: var(--surface);
  padding: 1.25rem;
  margin-bottom: 1.5rem;
}

.synth-result[hidden] {
  display: none;
}

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

.synth-result__actions {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
}

.synth-result__text {
  white-space: pre-wrap;
  font-size: 0.92rem;
  line-height: 1.6;
  max-width: 70ch;
}

/* Brain dump overlay */

.dump {
  position: fixed;
  inset: 0;
  z-index: 55;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
}

.dump[hidden] {
  display: none;
}

.dump__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(19, 19, 19, 0.65);
}

.dump__box {
  position: relative;
  width: 100%;
  max-width: 640px;
  max-height: 88vh;
  display: flex;
  flex-direction: column;
  background: var(--bone);
  border: 2px solid var(--ink);
  padding: 1.25rem;
}

.dump__head {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 0.75rem;
}

.dump__head .label {
  margin: 0;
  flex: 1;
}

.dump__time {
  font-family: var(--mono);
  font-size: 0.75rem;
  color: var(--gray);
}

.dump__body {
  flex: 1;
  overflow-y: auto;
  min-height: 8rem;
}

.dump__transcript {
  font-size: 1rem;
  line-height: 1.6;
  max-width: 70ch;
  white-space: pre-wrap;
  min-height: 5rem;
}

.dump__interim {
  color: var(--gray);
}

.dump__hint {
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  color: var(--gray);
  margin-top: 0.9rem;
}

.dump__actions {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
  margin-top: 1rem;
}

.dump__list {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.dump__item {
  position: relative;
  display: flex;
  gap: 0.75rem;
  border: 1px solid var(--border);
  background: var(--surface);
  padding: 0.75rem 0.9rem 0.75rem 3.1rem;
  cursor: pointer;
}

.dump__item .note__check {
  left: 0.9rem;
  top: 0.85rem;
}

.dump__item--checked {
  border-color: var(--ink);
}

.dump__item--checked .note__check {
  background: var(--lime);
}

.dump__item--checked .note__check::after {
  content: '✓';
  color: #131313;
  font-weight: 700;
}

.dump__item-main {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  min-width: 0;
}

.dump__item-title {
  font-weight: 600;
  font-size: 0.92rem;
}

.dump__item-body {
  font-size: 0.85rem;
  color: var(--gray);
}

/* First run */

.onboard-unlocks {
  padding: 0 1.5rem;
}

.onboard-unlocks__line {
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  color: var(--gray);
  padding: 0.35rem 0;
}

.dash-linkline {
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  color: var(--gray);
}

/* Why — explain a similarity link */

.why-btn {
  font-family: var(--mono);
  font-size: 0.6rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  background: none;
  color: var(--gray);
  border: none;
  padding: 0.2rem 0.3rem;
  cursor: pointer;
}

.why-btn:hover {
  color: var(--ink);
}

.why-btn[disabled] {
  cursor: default;
}

.why-text {
  font-size: 0.82rem;
  color: var(--text-soft);
  border-left: 2px solid var(--lime);
  padding-left: 0.6rem;
  margin: 0.5rem 0 0;
  max-width: 70ch;
}

.why-text__pair {
  display: block;
  font-family: var(--mono);
  font-size: 0.6rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gray);
  margin-bottom: 0.15rem;
}

/* Ask */

.ask-result {
  margin-top: 1.5rem;
}

.ask-answer {
  font-family: var(--body);
  font-size: 0.95rem;
  line-height: 1.65;
  max-width: 70ch;
  color: var(--ink);
  margin-bottom: 1rem;
}

.ask-cite {
  vertical-align: baseline;
}

.ask-keyless-hint {
  margin-top: 1rem;
}

/* Timeline */

.timeline-filters {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
  margin-bottom: 1rem;
}

.timeline-list {
  list-style: none;
}

.timeline-bucket {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--bone);
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.12em;
  color: var(--gray);
  padding: 0.6rem 0 0.4rem;
  margin-top: 0.75rem;
  border-bottom: 1px solid var(--border);
}

.tl-item {
  list-style: none;
}

.tl-row {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  width: 100%;
  text-align: left;
  font-family: var(--body);
  color: var(--ink);
  background: none;
  border: none;
  border-bottom: 1px solid var(--border);
  padding: 0.7rem 0;
  cursor: pointer;
}

.tl-row:hover {
  background: rgba(212, 255, 0, 0.10);
}

.tl-row__badge {
  flex: none;
  margin-bottom: 0;
  margin-top: 0.1rem;
}

.tl-row__badge--idea { background: var(--ink); color: var(--bone); }
.tl-row__badge--diary { background: none; color: var(--gray); border-color: var(--border); }
.tl-row__badge--clip { background: var(--lime); color: #131313; border-color: #131313; }

.tl-row__main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}

.tl-row__title {
  font-weight: 600;
  font-size: 0.9rem;
}

.tl-row__preview {
  font-size: 0.85rem;
  color: var(--gray);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.tl-row__tags {
  font-family: var(--mono);
  font-size: 0.65rem;
  letter-spacing: 0.06em;
  color: var(--gray);
}

.tl-row__age {
  flex: none;
  font-family: var(--mono);
  font-size: 0.65rem;
  color: var(--gray);
  margin-top: 0.2rem;
  white-space: nowrap;
}

@media (min-width: 640px) {
  .dash {
    grid-template-columns: 1fr 1fr;
  }

  .dash-card--hero {
    grid-column: 1 / -1;
  }

  /* .dash-linkline used to sit here too, as a direct grid child below the hero
     card. It now renders inside that card, so it is no longer a grid item. */
  .onboard-unlocks {
    grid-column: 1 / -1;
  }
}

/* Command palette */

.palette {
  position: fixed;
  inset: 0;
  z-index: 60;
}

.palette[hidden] {
  display: none;
}

.palette__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
}

.palette__box {
  position: relative;
  max-width: 560px;
  margin: 12vh 1rem 0;
  background: var(--surface);
  border: 1px solid var(--border);
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.35);
}

@media (min-width: 600px) {
  .palette__box {
    margin: 12vh auto 0;
  }
}

.palette__box input {
  width: 100%;
  font-family: var(--mono);
  font-size: 0.9rem;
  padding: 0.9rem 1rem;
  background: var(--surface);
  color: var(--ink);
  border: none;
  border-bottom: 1px solid var(--border);
  outline: none;
}

.palette__list {
  list-style: none;
  max-height: 320px;
  overflow-y: auto;
}

.palette__item {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem;
  padding: 0.6rem 1rem;
  font-size: 0.85rem;
  color: var(--ink);
  cursor: pointer;
  border-left: 3px solid transparent;
}

.palette__item--active {
  background: rgba(212, 255, 0, 0.12);
  border-left-color: var(--lime);
}

.palette__hint {
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gray);
  white-space: nowrap;
}

/* Celebration */

.confetti {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 100;
}

/* Polish §6 — mobile tap targets + 16px inputs (brain page only; brain.css
   isn't loaded by the landing page, so overrides here stay scoped to the app). */

/* Every text-entry field at 16px so iOS Safari doesn't zoom the page on focus.
   Same selectors as the rules that set the smaller sizes, placed later to win
   on source order (the duplicated input blocks are intentionally left as-is). */
.capture__title,
.capture__body,
.capture__tags,
.toolbar__search input,
.settings__row input,
.palette__box input,
.note--editing .note__edit-title,
.note--editing .note__edit-body,
.note--editing .note__edit-tags {
  font-size: 16px;
}

/* Polish §7 — visible keyboard focus. :focus-visible only, so mouse/touch
   clicks stay ringless; --ink keeps it visible in both light and dark. Listed
   after the input rules that set `outline: none` so this wins on source order;
   the edit-field selectors are named explicitly to out-specify theirs. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible,
.note--editing .note__edit-title:focus-visible,
.note--editing .note__edit-body:focus-visible,
.note--editing .note__edit-tags:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
}

/* Polish §4 — honour the OS "reduce motion" setting. Collapses the existing
   animations (note-flash, mic pulse, goal-bar fill) and transitions to
   near-instant; no new motion is introduced. Confetti is already JS-guarded. */
/* Micro-interactions — every rule here is neutralized by the
   prefers-reduced-motion block below (duration collapses to 0.001ms). */

/* §1 press states: taps read as confirmed. Full shorthands re-declared so
   the transform transition extends the existing ones instead of clobbering. */
.btn {
  transition: background 0.2s, color 0.2s, border-color 0.2s, transform 0.1s ease-out;
}

.toolbar__btn {
  transition: background 0.2s, border-color 0.2s, transform 0.1s ease-out;
}

/* transform only — color/border-color aren't composited, and the underline
   they used to carry is the indicator's job now */
.tabs__tab {
  transition: transform 0.1s ease-out;
}

.note__action,
.synth-bar__btn,
#digest-btn {
  transition: transform 0.1s ease-out;
}

.btn:active,
.toolbar__btn:active,
.tabs__tab:active,
.note__action:active,
.synth-bar__btn:active,
#digest-btn:active {
  transform: scale(0.96);
}

/* icon-only buttons: press-scale + slight opacity dip */
.tabs__gear,
.banner__dismiss,
.resurface-dismiss,
.synth-bar__cancel {
  transition: transform 0.1s ease-out, opacity 0.1s ease-out;
}

.clip__play {
  transition: transform 0.15s, opacity 0.1s ease-out;
}

.tabs__gear:active,
.banner__dismiss:active,
.resurface-dismiss:active,
.synth-bar__cancel:active,
.clip__play:active {
  transform: scale(0.96);
  opacity: 0.8;
}

/* §2 sliding tab indicator: replaces the per-tab static underline. JS
   positions it under the active tab; the slide class is applied only on
   physical clicks — hash-driven jumps stay instant. */
.tabs {
  position: relative;
}

/* Fixed 100px base scaled by JS, so position AND size ride on one composited
   transform — animating width here would hit layout on every frame. */
.tabs__indicator {
  position: absolute;
  bottom: 0;
  left: 0;
  height: 3px;
  width: 100px;
  background: var(--lime);
  transform: scaleX(0);
  transform-origin: 0 50%;
  will-change: transform;
  pointer-events: none;
}

.tabs__indicator--slide {
  transition: transform 0.15s ease-out;
}

/* Scroll affordance: the tab row scrolls on narrow screens, and without a cue
   there is nothing to suggest HOME/ASK/IDEAS exist off to the left. Fade the
   cut-off edge; JS adds the class only for the side that actually has more
   tabs, so the fade disappears once you reach that end. */
.tabs--fade-left {
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 32px);
  mask-image: linear-gradient(to right, transparent 0, #000 32px);
}

.tabs--fade-right {
  -webkit-mask-image: linear-gradient(to left, transparent 0, #000 32px);
  mask-image: linear-gradient(to left, transparent 0, #000 32px);
}

.tabs--fade-both {
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 32px, #000 calc(100% - 32px), transparent 100%);
  mask-image: linear-gradient(to right, transparent 0, #000 32px, #000 calc(100% - 32px), transparent 100%);
}

.tabs__tab--active {
  border-bottom-color: transparent; /* the indicator is the underline now */
}

/* §3 check pop: the one place a gentle overshoot reads as satisfying.
   Applied as a one-shot class from JS so re-renders don't re-fire it. */
@keyframes micro-pop {
  0% { transform: scale(1); }
  50% { transform: scale(1.15); }
  100% { transform: scale(1); }
}

.pop-once {
  animation: micro-pop 0.2s cubic-bezier(0.34, 1.56, 0.64, 1);
}

/* §7: streak numbers pop when they grow; inline-block so transform applies */
.streak-num,
.streak-card__value {
  display: inline-block;
}

/* §4 card lift: notes, clips, goals get 2px of life under the pointer.
   Hover only where hover exists; touch gets the same lift on :active. */
.note,
.goal {
  transition: transform 0.15s ease-out, box-shadow 0.15s ease-out;
}

@media (hover: hover) {
  .note:not(.note--editing):hover,
  .goal:hover {
    transform: translateY(-2px);
    box-shadow: 0 6px 16px rgba(0, 0, 0, 0.08);
  }
}

.note:not(.note--editing):active,
.goal:active {
  transform: translateY(-2px);
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.08);
}

/* §5 banner entrance: slide-up + fade on show (unhiding restarts the
   animation via the display flip). Dismissal stays instant. */
@keyframes banner-in {
  from {
    opacity: 0;
    transform: translateY(6px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

.banner {
  animation: banner-in 0.18s ease-out;
}

/* §8 tab content: the indicator already slides, but the content underneath it
   swapped instantly. Views toggle [hidden], so unhiding restarts the animation
   the same way the banner's does.

   Opacity only, deliberately. A translate on .view would make it a containing
   block for the whole 180ms, and #synth-bar is position:fixed *inside*
   #view-ideas — it would jump to the view's box and back on every tab switch.
   A fade alone reads as the transition without buying that bug. */
@keyframes view-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

.view {
  animation: view-in 0.18s ease-out;
}

/* §9 panel + overlay entrance, matching the banner's 180ms.
   Panels drop from the edge they're anchored to; sheets rise; backdrops just
   dim in, so the dimming and the content arrive together. */
@keyframes panel-in {
  from { opacity: 0; transform: translateY(-6px); }
  to { opacity: 1; transform: none; }
}

@keyframes sheet-in {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: none; }
}

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

.settings,
.graph-settings {
  animation: panel-in 0.18s ease-out;
}

.dump__box,
.palette__box,
.photo-view__img {
  animation: sheet-in 0.18s ease-out;
}

.dump__backdrop,
.palette__backdrop,
.photo-view__backdrop {
  animation: fade-in 0.18s ease-out;
}

/* §10 AI results arriving. Height cannot be transitioned to auto, so the block
   fades and rises into the space instead of snapping into it — the jump is
   what reads as jarring, not the reflow itself. Each of these is either
   rebuilt or unhidden per result, so the animation fires once per answer. */
@keyframes result-in {
  from { opacity: 0; transform: translateY(4px); }
  to { opacity: 1; transform: none; }
}

.ask-answer,
.synth-result,
#digest-result,
.reflect {
  animation: result-in 0.18s ease-out;
}

/* the press state replaces the mobile gray tap flash — ours is better */
button,
a {
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

/* Tablet+ */

@media (min-width: 640px) {
  .toolbar {
    flex-direction: row;
    align-items: center;
  }

  .clip-list {
    grid-template-columns: 1fr 1fr;
  }
}

@media (min-width: 900px) {
  .brain,
  .brain-footer {
    padding-left: 4rem;
    padding-right: 4rem;
    max-width: 860px;
  }

  .banner,
  .tabs,
  .settings {
    padding-left: 4rem;
    padding-right: 4rem;
  }

  .settings {
    max-width: 860px;
  }
}

/* ---------- Tap targets ----------
   Everything below grows the box a thumb has to hit without growing the thing
   an eye sees. Measured at 390px, which is where it matters; `test/
   verify-tap-targets.js` fails the build if any of it regresses.
   .clip__play (54px), .synth-bar__cancel and .tabs__gear were already fine. */

/* Icon-only dismiss controls: the glyph stays the size it was. */
.banner__dismiss,
.resurface-dismiss {
  min-width: 44px;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
}

/* The checkbox is a deliberate 1.35rem square — it reads as a checkbox at that
   size and no bigger. So the target goes around it rather than on it. */
.todo__check {
  position: relative;
}

.todo__check::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 44px;
  height: 44px;
}

/* Row-gap clears the tap boxes on .tag-chip so stacked rows cannot overlap. */
.tag-rail {
  gap: 1.15rem 0.5rem;
}

.echo__link,
.dash-capture__opt,
.dash-row,
.tl-row {
  min-height: 44px;
}

.dash-capture__opt,
.echo__link {
  display: inline-flex;
  align-items: center;
}

/* ---------- Offline ----------
   Offline is the working state for everything except the AI calls, so this
   marks a fact rather than raising an alarm — no red, no banner, no layout
   shift when it appears. */

.offline-badge {
  font-family: var(--mono);
  font-size: 0.6rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--gray);
  border: 1px solid var(--border);
  padding: 0.2rem 0.45rem;
  margin-left: 0.75rem;
  white-space: nowrap;
}

/* ---------- Corner radius ----------
   Every rounded corner in the app resolves to --radius, so the shape is one
   edit rather than fifty. Grouped here rather than sprinkled through the file
   for the same reason: it is a design-system decision and reads as one.

   Deliberate exceptions, both of which would be wrong at --radius:
     .tabs, .banner   full-bleed strips — rounding a full-width bar leaves
                      slivers of background at the corners
     .clip__play      a circular play overlay is a convention worth keeping
     .tabs__indicator a 3px underline, not a surface

   Bars keep the token too: at 6px tall the browser clamps a 10px radius to a
   pill, which is what a progress bar should look like anyway. */

/* Surfaces */
.capture,
.note,
.dash-card,
.settings,
.streaks,
.goal,
.empty,
.todo,
.clip__thumb,
.clip__embed,
.photo-thumb,
.photo-view__img,
.graph-wrap,
.graph-tooltip,
.graph-settings,
/* only rendered when the graph is too sparse to draw, so no screenshot pass
   ever reached it — it was still square after the rest of the app rounded */
.graph-cold,
.synth-bar,
.synth-result,
.dump__box,
.dump__item,
.timeline-bucket,
.palette__box,
#digest-result {
  border-radius: var(--radius);
}

/* Inputs */
.capture__title,
.capture__body,
.capture__tags,
.toolbar__search input,
.settings__row input,
.note--editing .note__edit-title,
.note--editing .note__edit-body,
.note--editing .note__edit-tags,
.todo-add__due,
.todo__due-input,
.palette__box input {
  border-radius: var(--radius);
}

/* Controls */
.btn,
.toolbar__btn,
.banner__action,
.tag-chip,
.goal__plus,
.echo__link,
.synth-bar__btn,
.graph-settings-toggle,
.dash-row,
.tl-row,
#digest-btn {
  border-radius: var(--radius);
}

/* Badges and inline marks */
.note__pinned,
.mood,
.clip__badge,
.tl-row__badge,
.offline-badge,
.backlink,
.empty__tips em,
.note__body mark,
.note__title mark {
  border-radius: var(--radius);
}

/* Progress bars — track and fill must match or the fill corners poke out */
.dash-bar,
.dash-bar__fill,
.goal__bar,
.goal__fill {
  border-radius: var(--radius);
}

/* The dashboard + button is the one place a slightly larger radius reads
   better, because it is a 48px square rather than a strip of text. */
.dash-capture__fab {
  border-radius: calc(var(--radius) + 4px);
}

/* Checkboxes take the small token. At 22px the full one rounds them into
   circles, and a circle is the radio-button promise: pick exactly one. These
   are independent toggles, so they stay square-ish. */
.todo__check,
.note__check {
  border-radius: var(--radius-sm);
}

/* A lime accent bar keeps its hard edge. The bar is the left border of the
   surface it marks, so rounding the surface rounds the bar with it and blunts
   the one thing on the card meant to be sharp. Squaring only that edge keeps
   the accent and the rounding, which is why this is a rule rather than an
   exception list: every accented surface gets the same treatment. */
.note--pinned,
.dash-card--hero,
.synth-result,
.reflect,
.todo-notice,
.why-text {
  border-radius: 0 var(--radius) var(--radius) 0;
}

/* The streaks pair is one card containing two cells. Rounding the cells too
   leaves notches either side of the divider — the container owns the shape. */
.streak-card {
  border-radius: 0;
}

/* ---------- Dashboard card labels ----------
   Scoped to .dash-card rather than .label: the same class heads the graph
   panel and the brain-dump overlay, where it already carries its own layout. */

.dash-card .label {
  display: flex;
  align-items: center;
  gap: 0.4rem;
}

/* The label is 0.65rem, so a 1.15em icon lands at ~12px — right for a heading
   marker, and it inherits the label's grey rather than shouting. */
.dash-card .label .icon {
  flex: none;
  opacity: 0.85;
}

/* A streak's progress against its own best. Sits under the row's text, so it
   needs to clear the meta line above it. */
.dash-row .dash-bar {
  margin-top: 0.45rem;
}

/* ---------- Ideas tab polish ---------- */

/* Input with a leading glyph. One pattern, used by the tags field and the
   search box rather than solved twice. The glyph never takes the tap:
   pointer-events: none means a finger aimed at it lands on the input. */
.field-icon {
  position: relative;
  display: block;
}

.field-icon > .icon {
  position: absolute;
  left: 0.85rem;
  top: 50%;
  transform: translateY(-50%);
  color: var(--gray);
  pointer-events: none;
}

.field-icon > input {
  padding-left: 2.5rem;
}

/* The section heading carries the same glyph treatment as the dashboard card
   labels — scoped, because .label also heads the graph and dump panels which
   have their own layout. */
.capture .label {
  display: flex;
  align-items: center;
  gap: 0.4rem;
}

.capture .label .icon {
  flex: none;
  opacity: 0.85;
}

/* A note title reads as a document. inline-flex on the h2 would break the
   wrapping of a long title, so the glyph stays inline and is nudged onto the
   baseline instead. */
.note__title .icon {
  color: var(--gray);
  margin-right: 0.4rem;
  vertical-align: -0.12em;
}

/* Every primary action in the app is lime. This supersedes an earlier decision
   that made CAPTURE the single lime exception — the exception is gone, and the
   rule is now the shared .btn rather than one scoped selector, so a new primary
   button is lime by existing rather than by being remembered.

   .btn's base state used to be ink with a lime *hover*, so the hover needs a
   different job now: it dips opacity instead, since "turns lime" is no longer
   available to it.

   In brain.css and not styles.css on purpose: styles.css is shared with the
   PRAZE landing page, whose buttons stay as they are. */
.btn {
  background: var(--lime);
  color: var(--ink);
  border-color: var(--ink);
}

.btn:hover:not(:disabled) {
  background: var(--lime);
  color: var(--ink);
  border-color: var(--ink);
  opacity: 0.88;
}

/* A lime button that cannot be pressed must not look pressable — the dump's
   save-checked button spends most of its life disabled. */
.btn:disabled {
  opacity: 0.45;
  cursor: default;
}

/* ---------- Diary tab ---------- */

/* The three actions read as one row. Deliberately NOT equal thirds with stacked
   sublabels: every other tab lays its primary action out at natural width with
   the keyboard hint as a separate quiet span, and matching that matters more
   than matching a reference. The hint takes a whole line of its own so the
   buttons stay grouped when they wrap on a narrow screen. */
#diary-form .capture__actions {
  gap: 0.5rem;
  /* stretch so the three read as one row of equal blocks: .btn carries more
     vertical padding than .toolbar__btn, and centred they sit at three
     different heights */
  align-items: stretch;
}

/* Three actions where other tabs have one or two, so at 390px they share the
   row instead of dropping Dictate onto a line of its own. The primary keeps
   its own type and weight — only its generous horizontal padding gives way,
   since that padding exists for a button sitting alone. */
/* Measured, not assumed: at 390px this row is 292px wide and the three labelled
   buttons need about 440. Forcing them onto one line costs either the labels or
   the type size, and neither is worth it — so they wrap and stay grouped, which
   is what every other tab does too. From 640px up they sit on one line by
   themselves. */
#diary-form .capture__actions .btn,
#diary-form .capture__actions .toolbar__btn {
  justify-content: center;
  white-space: nowrap;
}

#diary-form .capture__actions .capture__hint {
  flex-basis: 100%;
}

/* The weekly digest is an offer, not an instruction — so it sits in a quiet
   strip rather than shouting with a bordered button. Surface + border + the
   radius token, the same shape as every other panel in the app. */
.digest {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem;
  border: 1px solid var(--border);
  background: var(--surface);
  border-radius: var(--radius);
  padding: 0.35rem 0.9rem;
  margin-bottom: 1.25rem;
}

.digest__glyph {
  flex: none;
  color: var(--gray);
}

/* Quiet inside the strip: no border of its own, and it earns emphasis on hover
   rather than holding it permanently. */
#digest-btn {
  border-color: transparent;
  color: var(--gray);
  padding-left: 0;
  padding-right: 0.5rem;
}

#digest-btn:hover:not(:disabled) {
  background: none;
  border-color: transparent;
  color: var(--ink);
}

#digest-hint {
  margin-left: 0;
}

/* the result belongs under the strip, not inline inside it */
#digest-result {
  flex-basis: 100%;
  margin-top: 0.5rem;
  margin-bottom: 0.5rem;
}

/* ---------- Goals tab ---------- */

/* The flame gets a box of its own so it reads as a marker rather than a stray
   glyph. --border as the fill works in both themes: it is translucent ink on
   bone in light, translucent bone on ink in dark, so it sits a shade off the
   card either way rather than needing two values. */
.streak-card__flame {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.1rem;
  height: 2.1rem;
  border-radius: var(--radius);
  background: var(--border);
  font-size: 1.1rem;
}

/* Number, label and best on their own lines. baseline alignment was what kept
   them on one line before, so the card aligns to the top now. */
.streak-card {
  align-items: flex-start;
  gap: 0.75rem;
  flex-wrap: nowrap;
}

.streak-card__body {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.streak-card__value {
  line-height: 1.05;
}

.streak-card__label {
  margin-top: 0.15rem;
}

.streak-card__best {
  font-family: var(--mono);
  font-size: 0.6rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--gray);
  margin-top: 0.2rem;
}

/* A caption under the number, so the field says what it is once the placeholder
   has been typed over — which is exactly when a placeholder stops helping. */
.goal-form__target-field {
  display: flex;
  flex-direction: column;
  flex: none;
}

.goal-form__caption {
  font-family: var(--mono);
  font-size: 0.6rem;
  letter-spacing: 0.1em;
  color: var(--gray);
  margin-top: -0.5rem;
  padding-left: 0.1rem;
}

/* Two streak cards side by side leave each one 170px, and after padding and the
   flame the label gets 84 of the ~105 it needs — so it wraps. Measured, not
   guessed. Shaving the padding enough to fit would leave the card cramped, so
   the flame moves above the text instead and the label gets the full width. */
@media (max-width: 480px) {
  .streak-card {
    flex-direction: column;
    gap: 0.5rem;
  }
}

/* Empty-state glyph. Sized off the title beneath it rather than a fixed px, so
   it tracks the type if that ever changes. */
.empty__glyph {
  display: block;
  font-size: 1.6rem;
  line-height: 1;
  color: var(--gray);
  margin-bottom: 0.6rem;
}

/* ---------- Wide viewports ----------

   Everything here is min-width only, so the phone layout cannot be reached by
   it. That is verified rather than assumed: a geometry snapshot of all nine
   tabs at 390x844 is identical before and after this section existed.

   The column was pinned left — .brain had a max-width and no auto margin — so
   at 1440px all 644px of slack piled up on one side and read as a bug. */

.brain,
.brain-footer {
  margin-inline: auto;
}

/* Three tabs earn more room; the rest keep the 860px measure on purpose. A
   text input or a list row stretched across 1400px is worse to use, not
   better, and those tabs are a separate decision.

   Widening is driven by :has() rather than a class toggled from JS, because
   the app has no view-level hook on the container and this needs none. A
   browser without :has() keeps the old width — a fine thing to degrade to.
   The footer is matched to the container so their edges stay aligned. */

@media (min-width: 1100px) {
  /* The graph is a spatial view, so it gets the width. Note that giving the
     container room was NOT the whole change it was described as: the canvas
     grew and the graph did not, because the layout's size is set in absolute
     pixels. brain-graph.js now frames the content to the canvas, which is what
     actually makes the extra room count — here and at every other width. */
  body:has(#view-graph:not([hidden])) .brain,
  body:has(#view-graph:not([hidden])) .brain-footer {
    max-width: 1600px;
  }

  /* Height, now that height buys graph rather than empty canvas. 60vh was less
     conservative than it looks — at a 900px-tall laptop it lands exactly at the
     footer — but it is a fixed fraction, so it leaves 120px unused at 1200px
     tall and overshoots into a scrollbar on a short one. Everything on this tab
     other than the canvas (navbar, tabs, legend, footer, margins) measures
     319px at this breakpoint, so filling to the footer is 100vh - 319px. That
     keeps the footer note and the .graph-cold strip on screen without
     scrolling at every height, rather than only at 900. min-height: 320px from
     the base rule still floors it.

     This number is measured, not guessed, and it moves whenever the chrome
     does: 360 originally, 385 while the per-tab headers existed, 319 now that
     they are gone and the footer note stays collapsed. Note that it did NOT
     return to 360 when the headers were removed — the lighter footer is worth
     41px on its own — so re-measure rather than reaching for a previous value.
     Verified flat at 319 across 700/800/900/1080/1200px viewport heights. */
  .graph-wrap {
    height: calc(100vh - 319px);
  }

  /* Card grids. Three columns inside 860px would be pointless — ~244px each —
     so the container has to grow with them, otherwise the reflow makes the
     cards worse rather than better. */
  body:has(#view-dashboard:not([hidden])) .brain,
  body:has(#view-dashboard:not([hidden])) .brain-footer,
  body:has(#view-clips:not([hidden])) .brain,
  body:has(#view-clips:not([hidden])) .brain-footer {
    max-width: 1200px;
  }

  /* The hero card already spans the full row from the 640px rules. */
  .dash {
    grid-template-columns: repeat(3, 1fr);
    /* Cards size to their content instead of stretching to the tallest in the
       row. Three columns makes this visible in a way two did not: one long
       ECHOES list would otherwise leave two half-empty cards beside it.
       Scoped here rather than on .dash so the two-column layout is untouched. */
    align-items: start;
  }

  .clip-list {
    grid-template-columns: repeat(3, 1fr);
  }

  /* Spacing proportion. Measured at 1440 before this: the gaps BETWEEN
     top-level sections were 32px while the padding INSIDE a card was 24px, so
     the space separating two things outweighed the space binding a card to its
     own contents. That reads as a page of loose fragments rather than as a
     stack of blocks, and it is the wrong way round.

     So gaps come down to 20px and card padding goes up to 28px. Nothing here
     changes what sits where — only how much air is around it — and it is all
     behind min-width, so the phone keeps the spacing it was tuned with.

     The 32px gaps came from margin-bottom: 2rem on .capture, .streaks,
     .goal-list and .todo-done; .tag-rail and .todo-list sat at 28 and 24. The
     rule is simply that no top-level gap exceeds 20px at this width. .toolbar
     and .wins are already below it and are left alone — this reduces gaps, it
     does not standardise them upwards. */
  .capture,
  .streaks,
  .goal-list,
  .todo-done,
  .tag-rail,
  .todo-list {
    margin-bottom: 1.25rem;
  }

  /* Content cards only. .empty is deliberately NOT in this list: its padding
     was cut from 3rem to 1.5rem in #34 precisely because those boxes were
     mostly air, and putting it back here would quietly undo that. An empty
     state is a placeholder, not a card. */
  .capture,
  .dash-card,
  .note,
  .clip {
    padding: 1.75rem;
  }
}

/* Very wide screens. The containers above were sized for a laptop, and past
   about 1600px they start to read as narrow again: measured at 1920, Home and
   Clips used 56% of the width and the graph 77%. One more step, no reflow —
   the card grids keep three columns, since a fourth would make each card
   narrower than it is at 1440 and that is not an improvement.

   Only the three tabs batch 1 widened are here. The rest keep the 860px
   measure on purpose: a text field or a list row stretched across 1400px is
   worse to use, not better, and giving them the width is not the fix for the
   space around them — that is a layout question, not a max-width one. */
@media (min-width: 1600px) {
  body:has(#view-dashboard:not([hidden])) .brain,
  body:has(#view-dashboard:not([hidden])) .brain-footer,
  body:has(#view-clips:not([hidden])) .brain,
  body:has(#view-clips:not([hidden])) .brain-footer {
    max-width: 1440px;
  }

  body:has(#view-graph:not([hidden])) .brain,
  body:has(#view-graph:not([hidden])) .brain-footer {
    max-width: 1800px;
  }
}
