/* ── The shared goal card ─────────────────────────────────────────────
   One card, four tabs: Daily, Weekly, Monthly, Quarterly. See
   js/goal-board/goal_card.js for the anatomy and
   instructions/spec-goal-card-unification.md for why.

       ┌──────────────────────────────────────────────┐
       │  ○  🎯  ship the beta cut          ⋯  ✎  ×   │
       │         ┌ Aug goal ┐ ┌ Outcome ┐       ⟨7⟩   │
       └──────────────────────────────────────────────┘

   🔴 EVERYTHING here is scoped through `.gb-card`, which `arrangeGoalCard`
   stamps. That is deliberate and load-bearing: the reflection blocker's modal
   renders the SAME card DOM (`.gd-daily-group`, `.gd-daily-line`, …) but never
   arranges it, because it wants the reflection tray this card does without. A
   rule scoped on the shared classes instead would reach into the blocker — which
   is live on stable — and take its tray's chrome with it.

   The Daily board's own stylesheet (views/daily-long-term-goals/) still owns the
   day column, the strip and the card's surface there; this file adds only what
   the shared card brought with it. */

/* ── The card, on the tabs that had no card before ────────────────────
   The Daily board and the Goal Tree draw their own; these are the period columns,
   where a row was a row. Scoped to those views so those two are untouched.

   🔴 `.ylg-view` is on this list, and its absence is the bug that prompted the
   2026-08-13 pass: the rail letter is a SPAN, so "Y" shows the QUARTERS board
   (`.qlg-view`) and it is "3Y" that opens the yearly one. The four names here
   read like all four tiers and are not — the tri-yearly board had no card
   surface at all, no fill, no border and no hover, which is why the theming
   "did not apply" there. A new tier has to be added here as well as built.

   🔴 The fill is `--card-surface`, a resolved OPAQUE colour, not the
   `rgba(var(--overlay-rgb), 0.04)` it was in dark mode until 2026-08-13. Nothing
   sits behind a card — the board, the column and the section card are all
   transparent down to the body — so the tint was compositing against the backdrop
   GRADIENT and a card changed colour as the wash moved under it. See
   js/settings/display_settings.js for how each theme resolves the token.

   🔴 `.gb-card.ltg-row` — the DOUBLED class is load-bearing, not tidiness. Every
   card is `class="ltg-row gd-daily-group"` before `arrangeGoalCard` stamps
   `.gb-card` on it (js/goal-board/goal_card.js), so
   `body.theme-light .ltg-row` (views/week/shared/long-term-popup.css) also
   matches — and at 0-2-1 it OUTRANKS a single-class `.wlg-view .gb-card` at
   0-2-0. This rule was `body.theme-light .wlg-view .gb-card` until 2026-08-13,
   which won on specificity by accident; folding the two themes into one token
   rule quietly lost that fight and every light-theme period card went back to
   the accent tint while the Daily board, the Reflect grid and the Goal Tree —
   all scoped through three classes — stayed white. The second class buys the
   0-3-0 that beats the row tint in both themes. Any future card fill here needs
   to outrank `body.theme-light .ltg-row` or it will not be the one you see. */
.wlg-view .gb-card.ltg-row,
.mlg-view .gb-card.ltg-row,
.qlg-view .gb-card.ltg-row,
.ylg-view .gb-card.ltg-row,
.wlg-sidebar .gb-card.ltg-row,
.goals-side-panel .gb-card.ltg-row {
  position: relative;
  box-sizing: border-box;
  background: var(--card-surface);
  border: 1px solid var(--border-subtle);
  border-radius: 8px;
  /* Two lines and the space to separate them, nothing else. Every extra pixel
     here is multiplied by the number of goals in the period. */
  padding: 5px 8px 6px;
  transition: background 0.12s ease, border-color 0.12s ease;
}
.wlg-view .gb-card.ltg-row:hover,
.mlg-view .gb-card.ltg-row:hover,
.qlg-view .gb-card.ltg-row:hover,
.ylg-view .gb-card.ltg-row:hover,
.wlg-sidebar .gb-card.ltg-row:hover,
.goals-side-panel .gb-card.ltg-row:hover {
  background: var(--card-surface-strong);
  border-color: rgba(var(--overlay-rgb), 0.22);
}

/* The goal's line: tick · emoji · words · ⋯ · ✎ · ×. `auto` on the lead columns
   so an EMPTY emoji slot (an untagged goal, which inherits nothing) collapses
   and the words start where every other card's do.

   🔴 SIX tracks, one per child, and that count is not cosmetic. This grid had
   four while the row grew three trailing controls, and grid auto-placement does
   not overflow a short template — it WRAPS: the ⋯ took the last track and the
   pencil and the × dropped onto a second row underneath the goal, which is what
   the card looked like on beta (2026-08-07). A row that gains a control has to
   gain a track here with it. The trailing three are `auto`, so a tier that draws
   fewer (no menu, no pencil) simply leaves them empty. */
.gb-card .gd-daily-line {
  display: grid;
  grid-template-columns: auto auto minmax(0, 1fr) auto auto auto;
  align-items: center;
  gap: 3px;
}
.gb-card .gd-daily-body { display: flex; gap: 2px; }
.gb-card .gd-daily-content { flex: 1 1 auto; min-width: 0; }

/* ── The three-state completion tick ──────────────────────────────────
   Empty circle → green check → red × → back. The values are the dashboard's
   (`views/goals-dashboard/goals-dashboard.css`, `.gd-view .gd-daily-check`) and
   must stay identical to them: on the Daily tab BOTH rules match the same button
   and the dashboard's sheet loads later, so a difference here would show up on
   the period boards only and read as two different controls.

   It is restated here because the dashboard's copy is scoped to `.gd-view`, and a
   Weekly / Monthly / Quarterly card is not inside one. Without it the button fell
   back to the browser's default — a light-grey pill with a 2px outset border and
   a black glyph, 30×23 instead of 14×14 — which is what shipped to beta on
   2026-08-07 and reads as a stray form control in the middle of the card. */
.gb-card .gd-daily-check {
  align-items: center;
  background: transparent;
  border: 0;
  border-radius: 50%;
  color: rgba(255, 255, 255, 0.55);
  cursor: pointer;
  display: inline-flex;
  flex: 0 0 14px;
  height: 14px;
  justify-content: center;
  justify-self: center;
  line-height: 0;
  margin: 0;
  padding: 0;
  width: 14px;
}
.gb-card .gd-daily-check[data-completion-state='checked'] { color: #4caf50; }
.gb-card .gd-daily-check[data-completion-state='x'] { color: #ef5350; }
/* The translucent white is invisible on a light ground — same treatment the
   dashboard gives it. */
body.theme-light .gb-card
  .gd-daily-check:not([data-completion-state='checked']):not([data-completion-state='x']) {
  color: rgba(0, 0, 0, 0.45);
}

/* ── The inherited emoji ──────────────────────────────────────────────
   Left of the words, right of the tick — part of the goal's name rather than a
   control beside it. Below yearly it is a span and takes no press. */
.gb-card .gb-emoji {
  font-size: 13px;
  line-height: 1;
  flex: 0 0 auto;
}
/* Deliberately NOT `[hidden] { display: none }`. The row's tracks are positional:
   collapsing the slot out of the grid slides every control after it one track
   left, which put the ⋯ in the middle of a card whose goal happens to inherit no
   emoji. Empty, it is already 0px wide. */
.gb-card .gb-emoji--editable {
  background: none;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  padding: 1px 2px;
  /* An empty picker still has to be aimable, so it keeps a target when a span
     would collapse. */
  min-width: 16px;
}
.gb-card .gb-emoji--editable:hover {
  background: rgba(var(--overlay-rgb), 0.14);
}

/* ── The three row controls ───────────────────────────────────────────
   Quiet until the card is hovered: on a column of twenty goals, twenty sets of
   three glyphs is the loudest thing on screen and none of it is the goals. */
.gb-card .gb-more,
.gb-card .dlg-row-edit,
.gb-card > .gd-daily-body .ltg-remove,
.gb-card .gd-daily-line > .ltg-remove {
  background: none;
  border: none;
  border-radius: 4px;
  color: var(--text-tertiary, var(--text-secondary));
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  line-height: 1;
  opacity: 0.45;
  padding: 2px 3px;
  transition: opacity 0.12s ease, background 0.12s ease, color 0.12s ease;
}
.gb-card:hover .gb-more,
.gb-card:hover .dlg-row-edit,
.gb-card:hover .gd-daily-line > .ltg-remove,
.gb-card .gb-more:focus-visible,
.gb-card .dlg-row-edit:focus-visible,
.gb-card .gd-daily-line > .ltg-remove:focus-visible {
  opacity: 1;
}
.gb-card .gb-more:hover,
.gb-card .dlg-row-edit:hover {
  background: rgba(var(--overlay-rgb), 0.16);
  color: var(--text-primary);
}
.gb-card .gd-daily-line > .ltg-remove:hover {
  background: rgba(255, 90, 90, 0.18);
  color: #ff8a8a;
}
/* ── The pencil is the LOUD one ───────────────────────────────────────
   The rule above keeps the row controls quiet, and that was right for two of the
   three: the ⋯ duplicates a press anywhere on the card and the × is the one that
   must not be hit by accident. Editing the goal's words has no second way in and
   is the control people go looking for — drawn at the same whisper it read as a
   speck nobody recognised as a pencil.

   So it opts out — in the GLYPH, not around it. It keeps the strip's COLOUR (the
   tertiary the quiet controls wear) and its bare-button shape: no chip, no
   outline, because a box drawn at rest is a second thing to look at and what has
   to be seen is the pencil. What it gets is size, stroke weight and presence
   before the hover — 17px at 1.9 against the ⋯'s 14px, and opacity 0.85 where the
   others sit at 0.45. Still icon-only: no label rides along, because six of these
   in a column of twenty goals is a wall of words where the goals should be. */
.gb-card .dlg-row-edit {
  opacity: 0.85;
  padding: 1px 2px;
}
.gb-card .dlg-row-edit svg { display: block; }
.gb-card .gb-more svg { fill: currentColor; display: block; }
.gb-card .gb-more[aria-expanded='true'] {
  background: rgba(var(--overlay-rgb), 0.2);
  color: var(--text-primary);
  opacity: 1;
}

/* The words. A locked title is part of the drag handle, so it must not offer a
   caret it will not give — and must not SELECT, because a press inside selected
   text starts Chrome's native text-drag and kills the row gesture. */
.gb-card .ltg-text {
  background: none;
  border: none;
  font-size: 13px;
  line-height: 1.35;
  padding: 1px 0;
  resize: none;
  width: 100%;
}
/* ── Light mode: the words read NEUTRAL ───────────────────────────────
   `.ltg-row .ltg-text` (views/week/shared/long-term-popup.css) paints every goal
   title in its own colour — in light mode `--ltg-row-text-light`, a dark tint of
   it rather than the saturated hue. That is right wherever the ROW is the only
   thing carrying the goal's identity, which is what a period board used to be.
   It is not one any more: the card is a white sheet and the parent chip on it is
   FILLED with that same colour, so a coloured title states the same fact twice
   and turns a column of goals back into a paint chart. The Daily board opted out
   of this rule when it became a tag board; these four tabs draw the same card and
   had simply never been given the same line (2026-08-13).

   ⚠️ LIGHT ONLY, and that asymmetry is deliberate. On a dark theme the card is a
   neutral `--card-surface` and the parent chip is quiet (`--utility-surface` with
   coloured text), so the title is the only colour a goal has left — take it away
   there and the period boards lose their colour coding entirely.

   The Goal Tree is untouched by both: it is not inside any of these views, and
   its own sheet takes the ink from the card's fill
   (`views/goals-dashboard/goals-dashboard-solid-cards.css`). */
body.theme-light .wlg-view .gb-card .ltg-text,
body.theme-light .mlg-view .gb-card .ltg-text,
body.theme-light .qlg-view .gb-card .ltg-text,
body.theme-light .ylg-view .gb-card .ltg-text,
body.theme-light .wlg-sidebar .gb-card .ltg-text,
body.theme-light .goals-side-panel .gb-card .ltg-text {
  color: var(--text-primary);
}

.gb-card .ltg-text[readonly] {
  cursor: grab;
  user-select: none;
}
.gb-card .ltg-text:focus {
  background: rgba(var(--overlay-rgb), 0.08);
  border-radius: 4px;
  outline: none;
  padding: 1px 4px;
}

/* A completed goal reads as done without disappearing: it is still one of the
   period's goals. */
.gb-card.gb-done .ltg-text { opacity: 0.55; text-decoration: line-through; }

/* The checklist's own header is the control the `⋯` REPLACED, so on a board card
   it is not drawn: it was a full-width row carrying one chevron and a count, and
   the menu now says "Sub-goals — 3" in a line that also says everything else the
   card can do. Hidden rather than removed, because it is still the thing that
   OPENS the checklist — the menu row clicks it (`daily_card_row.js`), and
   `subgoal_disclosure.js` keeps writing the count this reads back. `display:none`
   is safe for both: a click() on a hidden button still fires, and textContent
   still reads. */
.gb-card .gd-subgoals-head { display: none; }

/* Retired in place (a weekly goal ended mid-week, its earlier days kept). It is
   history, so it stops offering the edits that would rewrite it. */
.gb-card.gb-archived { opacity: 0.62; }
.gb-card.gb-archived .dlg-row-edit { display: none; }

/* ── The foot: chips left, rating right ───────────────────────────────
   ONE line, always. Wrapping cost a whole extra row on exactly the cards whose
   parent has a name worth reading, and a two-line tag row under a one-line goal
   is a card spending more height on its labels than on its subject. The parent
   chip is the only thing here that can give, so it is the thing that shrinks. */
.gb-card .gd-daily-foot {
  display: flex;
  align-items: center;
  gap: 4px;
  margin-top: 3px;
  min-width: 0;
}
.gb-card .gd-link-tags {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: 4px;
  min-width: 0;
}
.gb-card .gd-link-tags > .wlg-monthly-tag,
.gb-card .gd-link-tags > .mlg-quarterly-tag,
.gb-card .gd-link-tags > .mlg-yearly-tag {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.gb-card .gd-link-tags > .dlg-tag-add {
  flex: 0 0 auto;
}
/* A chip that RE-PARENTS says so; one that is only a label stays a label. */
.gb-card .is-pickable { cursor: pointer; }
.gb-card .is-pickable:hover { filter: brightness(1.15); }

/* Bottom-right, and nowhere else — `margin-left: auto` rather than a fixed
   position, so a card with a long chip row still cannot push it off the card. */
.gb-card .gb-rating-slot {
  flex: 0 0 auto;
  margin-left: auto;
}
.gb-card .gb-rating-slot .ltg-rating { margin: 0; }

/* ── The chip a card carries when it has no parent ────────────────────
   The parent chip is the tier's own (`wlg-monthly-tag`, `mlg-quarterly-tag`, …)
   and is styled by that tier. This one is the board's: the empty slot that
   stands in for a missing parent. A second board chip sat beside it — the
   outcome/skill READOUT, `.dlg-type-tag` and its two colours — until the
   outcome/process split was retired on 2026-08-30. */
/* Deliberately quiet, and deliberately not chip-shaped: it is the ABSENCE of a
   tag, and a filled pill would read as one more tag the goal already has. */
.gb-card .dlg-tag-add {
  background: none;
  border: 1px dashed var(--border-subtle);
  border-radius: 5px;
  color: var(--text-tertiary, var(--text-secondary));
  cursor: pointer;
  font-size: 11px;
  line-height: 1;
  padding: 3px 6px;
  white-space: nowrap;
}
.gb-card .dlg-tag-add:hover {
  border-color: rgba(var(--overlay-rgb), 0.45);
  color: var(--text-primary);
}

/* ── The card's type scale, pinned against its view's own ─────────────
   🔴 SPECIFICITY, not preference. The three period views load their stylesheet
   AT NAVIGATION (`router.js` appends the <link>), so their rules land AFTER this
   file and win every tie — and each of them sets `.{prefix}-view .ltg-text` to
   16px and its own parent chip to 14px, sizes chosen when a period goal was a
   full-width row rather than one card in a column. Left alone, the same card came
   out one size on the Daily tab and another on the other three, which is exactly
   the drift this folder exists to end.
   One class deeper is enough, and the values are the Daily board's own. */
.gb-card .gd-daily-line .ltg-text { font-size: 13px; }

/* The parent chip, whatever its tier calls it (`wlg-monthly-tag`,
   `mlg-quarterly-tag`, `qlg-yearly-tag`, …) — matched by the SHAPE of the name,
   so a new tier's chip is the right size without a new rule. It is the only thing
   on the line that can give, so it is the thing that ellipsises; the readout
   beside it is one word and never shrinks. */
.gb-card .gd-link-tags { padding: 0; }
/* `> *` rather than a list of chip class names, and rather than `[class$="-tag"]`:
   a chip that can RE-PARENT gains `is-pickable` after it is built, so its class
   attribute no longer ends in `-tag` and an ends-with match quietly stops applying
   to exactly the chips that matter. Everything on this line is a chip; they are
   all the same size. */
.gb-card .gd-link-tags > * {
  border-radius: 5px;
  font-size: 11px;
  line-height: 1;
  padding: 3px 6px;
}
.gb-card .gd-link-tags [class$="-tag-emoji"] { font-size: 11px; padding: 0; }
.gb-card .gd-link-tags > :not(.dlg-tag-add) {
  flex: 0 1 auto;
  min-width: 0;
  max-width: 100%;
}

/* ── The board: columns of cards, filling the tab ─────────────────────
   The Daily tab has drawn its week this way since 2026-08-04 — one box per day,
   a flat list of cards, an add button under it — and on 2026-08-07 the Weekly,
   Monthly and Quarterly tabs became the same board over a different period. The
   column chrome is therefore here rather than in any one tab's stylesheet.

   `.gb-board` is the GRID and it is not an island. It used to be a bordered,
   rounded, filled panel floating inside the view's 24px padding, with the page
   showing through around it — three surfaces stacked to say one thing. It is
   transparent now and it fills the tab: the columns ARE the structure, and the
   thin rules between them are all the frame a board needs. */
.gb-board {
  display: grid;
  align-content: stretch;
  background: transparent;
  border: none;
  border-radius: 0;
  gap: 0;
  column-gap: 12px;
  min-height: 0;
  /* 🔴 ONE row, flowing across. The three period grids inherited
     `grid-auto-flow: column` + `grid-template-rows: auto auto` from the shape they
     used to have — two stacked cards per column, outcome on row 1 and skill on
     row 2 — and a column contributes exactly ONE child now. Left as it was, the
     second column's card would land in row 2 of the FIRST column and the board
     would staircase across the screen. */
  grid-auto-flow: row;
  grid-template-rows: minmax(0, 1fr);
  /* The tracks are written inline by each view (one per period on screen), so a
     window showing three quarters is three tracks. `minmax(0, 1fr)` there is what
     makes the board FIT the page instead of scrolling sideways — the Daily tab is
     the one board that scrolls, because seven days do not fit and a day column
     narrower than its goal's words is not a day column. */
  overflow: visible;
}
/* One column = one card, so the column is the grid item and its card fills it.
   (The three period views used `display: contents` with two stacked cards per
   column — an outcome card on grid row 1 and a process card on row 2 — which is
   exactly the split the type chip replaced.) */
.gb-board .gb-col {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
}
/* `.gb-col >` for specificity, not for structure: the period views' own
   stylesheets load at navigation and therefore AFTER this file, and each carries
   a `.{prefix}-{unit}-grid .section-card` rule from the two-cards-per-column shape
   — hairline rules on two edges and 12px 14px 10px of padding. A plain
   `.gb-board .gb-col-card` ties with it and loses. */
.gb-board .gb-col > .gb-col-card {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
  background: transparent;
  border: none;
  border-radius: 0;
  box-shadow: none;
  backdrop-filter: none;
  /* Horizontal is not negotiable down past 10px: that inset is what makes a goal
     card read as an island in the period rather than as a filled row of a table. */
  padding: 8px 10px 10px;
}
/* The period containing today. Stated once, here, rather than four times: each
   view's own stylesheet still carries a `.{unit}-column.is-current > .section-card`
   rule from the old grid, and which of them wins depends on whether the router has
   re-appended that stylesheet — so the same "today" would look different on
   different tabs. These are the Daily tab's values, rounded because the board has
   no frame to square the tint off against. */
.gb-board .gb-col.is-current .gb-col-label { color: var(--accent); }
.gb-board .gb-col.is-current > .gb-col-card {
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  border-radius: 10px;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 45%, transparent);
}
.gb-board .gb-col-head {
  align-items: center;
  display: flex;
  gap: 6px;
  height: auto;
  margin-bottom: 8px;
  min-height: 20px;
}
.gb-board .gb-col-label {
  color: var(--text-secondary);
  flex: 1;
  font-size: 12px;
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.gb-board .gb-col-count {
  color: var(--text-tertiary, var(--text-secondary));
  font-size: 12px;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}
/* The list scrolls INSIDE the column, so a period with thirty goals does not make
   every other column that tall. ⚠️ `flex-grow: 0` on purpose — a growing list
   stretched to the bottom of the board and carried "+ Add goal" down with it, so
   the button for adding to a two-goal week sat hundreds of pixels below the second
   goal. Add-goal belongs at the END OF THE LIST, which is where it is in the DOM. */
.gb-board .gb-col-rows {
  display: flex;
  flex-direction: column;
  flex: 0 1 auto;
  gap: 5px;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior-y: contain;
}
/* The header "+" and the footer "+ Add goal" are the same action, deliberately
   twice: the footer one is what you reach for after reading the period, the header
   one is what you find before you have scrolled to the bottom of a full one. */
.gb-board .gb-add-goal {
  background: none;
  border: none;
  color: var(--text-secondary);
  cursor: pointer;
}
.gb-board .gb-add-head {
  border-radius: 5px;
  font-size: 17px;
  line-height: 1;
  padding: 1px 5px 3px;
}
.gb-board .gb-add-foot {
  align-items: center;
  border-radius: 6px;
  display: flex;
  flex: 0 0 auto;
  font-size: 11px;
  font-weight: 600;
  gap: 5px;
  letter-spacing: 0.06em;
  margin-top: 8px;
  padding: 6px 7px;
  text-transform: uppercase;
}
.gb-board .gb-add-goal:hover {
  background: rgba(var(--overlay-rgb), 0.1);
  color: var(--text-primary);
}
.gb-board .gb-add-plus { font-size: 14px; }

/* The column header's two glyphs — "⧉" copies this column's goals to the
   clipboard as text (js/goal-board/board_copy_text.js), "⟲" copies the PREVIOUS
   period's goals in (js/goal-board/board_carry_forward.js). One control shape, so
   the pair reads as two directions of the same thing rather than two unrelated
   buttons — exactly how the Daily tab's day header draws its own pair.

   Quieter than the "+" beside them: adding a goal is what a column is for, and a
   board shows three to seven of these at once. */
.gb-board .gb-copy,
.gb-board .gb-carry {
  background: none;
  border: none;
  color: var(--text-secondary);
  cursor: pointer;
  flex-shrink: 0;
  font-size: 13px;
  line-height: 1;
  opacity: 0.5;
  padding: 0 1px;
  position: relative;
}
.gb-board .gb-copy:hover,
.gb-board .gb-carry:hover {
  color: var(--text-primary);
  opacity: 1;
}
/* Disabled while a carry is reading the prior period — the read is a network
   round trip, and a second press mid-flight would copy the same period twice. */
.gb-board .gb-carry:disabled {
  cursor: default;
  opacity: 0.3;
}
/* Their transient status ("Copied" / "Nothing to copy" / "Copied 4"). No board
   has a toast surface, so the button says it itself — absolutely positioned so it
   cannot reflow the header, and pointer-events:none so it never eats the next
   click on the button. */
.gb-board .gb-copy[data-note]::after,
.gb-board .gb-carry[data-note]::after {
  background: color-mix(in srgb, var(--card) 92%, transparent);
  border: 1px solid var(--border-subtle);
  border-radius: 6px;
  color: var(--text-secondary);
  content: attr(data-note);
  font-size: 11px;
  margin-top: 4px;
  padding: 3px 6px;
  pointer-events: none;
  position: absolute;
  right: 0;
  top: 100%;
  white-space: nowrap;
  z-index: 901;
}

/* The "⟲" mode menu. It IS a .gb-menu — same panel, same rows, same placement
   helper (js/goal-board/anchored_menu.js) — so all it adds is the line naming the
   period it would copy FROM. On the Daily tab "the previous day" needs no saying;
   on a board showing four quarters at once, which one the ⟲ under your pointer
   reaches back to is the whole question. */
.gb-carry-menu { min-width: 168px; }
.gb-carry-from {
  color: var(--text-tertiary, var(--text-secondary));
  font-size: 11px;
  letter-spacing: 0.04em;
  padding: 5px 8px 4px;
  text-transform: uppercase;
}

/* The column under the pointer during a drag. Same treatment the Daily tab's day
   columns get, for the same gesture. */
.gb-board .gb-col.gb-drop-target > .gb-col-card {
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  border-radius: 10px;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 45%, transparent);
}

/* The card you are holding mid-drag. `position: fixed` so it can leave the
   column, and pointer-events off so `elementFromPoint` sees the board under it
   rather than the thing being carried over it. */
.gb-drag-carry {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 5000;
  opacity: 0.92;
  pointer-events: none;
  filter: drop-shadow(0 8px 20px rgba(0, 0, 0, 0.35));
}

/* ── The ⋯ dropdown ───────────────────────────────────────────────────
   `position: fixed` for the same reason the parent menu is: a menu that reflowed
   the column would push the remaining goals down, moving the very card you are
   aiming at. Mounted on <body>, so it is scoped by its own class only. */
.gb-menu {
  position: fixed;
  z-index: 4000;
  min-width: 184px;
  max-width: 260px;
  padding: 4px;
  /* Fallback is the PANEL, never a fixed hex: an unset token has to land on
     something the theme owns, or the menu goes dark under a light theme. */
  background: var(--bg-elevated, var(--card));
  border: 1px solid var(--border-subtle);
  border-radius: 8px;
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.38);
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.gb-menu-row {
  display: grid;
  grid-template-columns: 18px minmax(0, 1fr) auto;
  align-items: center;
  gap: 8px;
  background: none;
  border: none;
  border-radius: 6px;
  color: var(--text-primary);
  cursor: pointer;
  font-size: 13px;
  line-height: 1.2;
  padding: 7px 8px;
  text-align: left;
  width: 100%;
}
.gb-menu-row:hover,
.gb-menu-row:focus-visible {
  background: rgba(var(--overlay-rgb), 0.14);
  outline: none;
}
.gb-menu-icon { color: var(--text-tertiary, var(--text-secondary)); }
.gb-menu-row:hover .gb-menu-icon { color: var(--text-primary); }
.gb-menu-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gb-menu-detail {
  color: var(--text-tertiary, var(--text-secondary));
  font-size: 11px;
  font-variant-numeric: tabular-nums;
}
/* Last, and it looks last. The one row that must not be pressed by accident. */
.gb-menu-row--danger { color: #ff8a8a; margin-top: 3px; }
.gb-menu-row--danger .gb-menu-icon { color: #ff8a8a; }
.gb-menu-row--danger:hover { background: rgba(255, 90, 90, 0.16); }
.gb-menu-row--danger::before {
  content: '';
  position: absolute;
  left: 8px;
  right: 8px;
  margin-top: -5px;
  border-top: 1px solid var(--border-subtle);
}
/* The divider above it is a pseudo-element rather than a border on the row, so
   the row's own hover fill still runs edge to edge. */
.gb-menu-row--danger { position: relative; }

/* The colour palette, when "Change colour" turns the menu into one. Yearly only:
   every other horizon inherits, which is what leaves this menu room to be a menu. */
.gb-menu[data-submenu] { min-width: 0; padding: 8px; }
.gb-menu .gb-menu-palette {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 6px;
}
.gb-menu .gb-menu-palette.hidden { display: grid; }

/* ── Light mode ───────────────────────────────────────────────────────
   The card's surface is an overlay tint over whatever is behind it, so it comes
   out right on both grounds without a second palette. The menu's shadow does
   not: a 38%-black drop under a white panel reads as dirt. */
body.theme-light .gb-menu {
  box-shadow: 0 10px 28px rgba(15, 20, 30, 0.16);
}
body.theme-light .gb-card .gd-daily-line > .ltg-remove:hover {
  background: rgba(200, 40, 40, 0.14);
  color: #c22;
}
body.theme-light .gb-menu-row--danger,
body.theme-light .gb-menu-row--danger .gb-menu-icon { color: #c22; }
body.theme-light .gb-menu-row--danger:hover { background: rgba(200, 40, 40, 0.12); }

/* ── Phones ───────────────────────────────────────────────────────────
   Hover is not a thing a finger does, so the row controls cannot hide behind it.
   They stay visible, and the menu widens to a comfortable tap target. */
@media (hover: none) {
  .gb-card .gb-more,
  .gb-card .dlg-row-edit,
  .gb-card .gd-daily-line > .ltg-remove { opacity: 0.8; }
  .gb-menu-row { padding: 11px 10px; }
}

/* The indexed sub-goal checklist and its day pills: styles/goal-board-subgoals.css
   (split out 2026-09-20; loaded right after this sheet). */

/* ── The details block: notes and links, always on the card ───────────
   Below the tag row and above the sub-goal checklist, which is where the card's
   reading order puts it: the goal, what it is, what you need to know about it,
   then its parts. `js/goal-board/goal_details.js`.

   🔴 `white-space: pre-wrap` is load-bearing, not typographic. The renderer does
   NOT convert newlines to `<br>` — the text keeps its own shape here, which
   means the only markup the block ever contains is the anchors, and there is one
   less thing a paste can inject. */
.gb-card .gb-details {
  margin: 4px 0 0;
  padding: 4px 6px 4px 8px;
  /* A quiet left rule rather than a box: the details are part of the goal, not a
     second card inside it. It takes the card's inherited colour, so a goal's
     notes are visibly that goal's. */
  border-left: 2px solid var(--ltg-row-color, rgba(var(--overlay-rgb), 0.25));
  border-radius: 0 3px 3px 0;
  background: rgba(var(--overlay-rgb), 0.04);
}
.gb-card .gb-details-view {
  font-size: 11.5px;
  line-height: 1.45;
  color: rgba(var(--overlay-rgb), 0.66);
  white-space: pre-wrap;
  /* A pasted URL has no spaces in it and would otherwise push the whole column
     wide — every other card on the board reflowing because one goal has a long
     link on it. */
  overflow-wrap: anywhere;
  cursor: text;
}
/* The link reads as a link. Underlined, in the goal's own colour, and it keeps
   its own cursor — the block around it says "click to edit", and the one place
   that is not true has to say so before it is pressed. */
.gb-card .gb-details-link {
  color: var(--ltg-row-color, currentColor);
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-thickness: 1px;
  cursor: pointer;
}
.gb-card .gb-details-link:hover {
  text-decoration-thickness: 2px;
  filter: brightness(1.15);
}
.gb-card .gb-details-input {
  display: block;
  width: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  font-size: 11.5px;
  line-height: 1.45;
  /* Grown by `autosize` on every keystroke — a notes field that scrolls is a
     notes field you cannot see the end of. */
  resize: none;
  overflow: hidden;
  white-space: pre-wrap;
}
.gb-card .gb-details-input:focus { outline: none; }
/* 🔴 `display: block` above BEATS the `hidden` attribute — the UA sheet's
   `[hidden] { display: none }` is one specificity class weaker than this rule, so
   without these two lines the closed editor renders under the view text on every
   card that has details, and the empty block still takes a strip on every card
   that has none. Any `display:` on a node the JS toggles with `.hidden` needs its
   own `[hidden]` rule. */
.gb-card .gb-details[hidden],
.gb-card .gb-details-input[hidden],
.gb-card .gb-details-view[hidden] { display: none; }
.gb-card .gb-details-input::placeholder { color: rgba(var(--overlay-rgb), 0.32); }
/* Open, the block is the thing being typed in, so it stops being a quiet band. */
.gb-card .gb-details[data-editing='true'] {
  background: rgba(var(--overlay-rgb), 0.09);
}
body.theme-light .gb-card .gb-details-view { color: rgba(0, 0, 0, 0.62); }

/* ── Sub-goals inside their parent's card ─────────────────────────────
   A day goal filed under another (`subOf`, from a time block's goal list) is a
   line of its parent's card, one step in per level
   (`goals_dashboard_daily_nesting.js`). Its weekly goal, when it is not the
   card's, sits on its own row under the words — the grid above has exactly as
   many tracks as the line's controls, so a seventh child must span, not join. */
.gb-card .gd-daily-line[data-depth]:not([data-depth="0"]) {
  padding-left: calc(var(--gd-depth, 0) * 18px);
}
.gb-card .gd-daily-sub-parent {
  grid-column: 3 / -1;
  font-size: 11px;
  opacity: 0.7;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
