/* Goals pane — domain styling over the shared frame (.rhead, .osAppCard,
   .osAppBar/.osAppBody, .osAppEmpty/.osAppRefuse, .osAppFoot).
   ⛔ Never redefines the frame — same rule os-crypto.css works under. */

/* ═══ v3 (management surface, owner 2026-09-07): the tree docks LEFT as the
   navigator; the goal's deep view fills the RIGHT column at full height.
   The v2 drawer is gone with its click contract — a drawer that covers the
   tree it navigated from was the wrong answer. */

/* ═══ v4 (task 0c4f2026): field chips carry a HUE, not a colour. `--fh` is
   the served number; the chip composes it against the theme tokens, so the
   same declaration reads correctly in light and dark. ⛔ No raw hex from the
   pane — an undeclared field composes nothing and says so in words. */

/* ═══ v5 (task 03f6aed0, owner 2026-09-08 · SPEC-GOALS-V5-2026-09-08.md) ═══
   ⛔ THE 880px CAP IS NOT WHAT BOUND THE PANE — THE DOUBLED GUTTER WAS.
      Measured on the owner's 1490 screen: 1490 − 262 (--wh-nav) − 296
      (--wh-dock) = 932 pane, − 40 (.osApp padding, work-clay.css:611)
      = 892, − 68 (.wrap padding, os-app.css:154) = 824px of card. The cap
      never engages below 1600. A session that "fixes the width" by moving to
      .wrap.wide changes NOTHING here and reports a fix that is not there.
   ⛔ AND THE VIEWPORT IS THE WRONG RULER. The rails appear and vanish at 1180
      and 860 viewport, so a viewport media query cannot say how wide the pane
      is — the same reason os-app.css:16-24 threw its literals out. Every
      breakpoint below is a CONTAINER query on the card.
   ⛔ NO `var(--fh, 0)` FALLBACK. v4 wrote hsl(var(--fh, 0) …), which paints an
      UNDECLARED field red — hue 0. Undeclared carries no --fh at all and takes
      .gFNone, which redefines the tokens to greys.
   ⛔ AND v4's hsl(h 70% 38%) TEXT FAILS AA: at S70/L55 hues 38, 95, 150 and
      190 measure ~2.3–2.8:1 on cream #fffbf4. S46/L33 passes for all ten. */

/* ── the frame: one wrap modifier, and the card is the container ─────────── */
.osApp[data-app="goals"]{ overflow: hidden; }
.osApp[data-app="goals"] .rhead{ flex: 0 0 auto; }
.osApp[data-app="goals"] .rhIn{ max-width: 1320px; }
.osApp[data-app="goals"] .wrap.gWrap{
  max-width: 1320px; padding: 12px 16px 20px;
  display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }
.osApp[data-app="goals"] .osAppCard{
  container: goals / inline-size;
  display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }
.osApp[data-app="goals"] .osAppBar,
.osApp[data-app="goals"] .osAppFoot{ flex: 0 0 auto; }
@media (max-width: 640px){ .osApp[data-app="goals"] .wrap.gWrap{ padding: 8px 8px 12px } }
/* ⛔ THE FIRST TAB WAS CLIPPED BY THE CARD'S OWN ROUNDED EDGE — "Tree" read
   "ree" on the owner's surface. os-app.css gives .osAppBar `padding:0 0 14px`,
   no horizontal gutter, and the clay override that supplies one is scoped to
   `body.os.inthread`, which the company home is not. Same fix, and the same
   selector idiom, os-registry.css:24 and os-xray.css:312 already use. */
.osApp[data-app="goals"] .osAppBar{ padding: 10px 14px; gap: 6px;
  overflow-x: auto; scrollbar-width: thin; }
.osApp[data-app="goals"] .osAppBar button{ flex: 0 0 auto; }

/* ── the ten field hues, composed once ───────────────────────────────────── */
.gF{
  --f-ink:  hsl(var(--fh) 46% 33%);
  --f-mid:  hsl(var(--fh) 56% 46%);
  --f-tint: hsl(var(--fh) 70% 62% / .14);
  --f-line: hsl(var(--fh) 52% 52% / .38);
  --f-wash: hsl(var(--fh) 70% 64% / .07);
}
/* ⛔ Undeclared is its own value, never a tenth colour and never hue 0. */
.gF.gFNone{
  --f-ink: var(--muted); --f-mid: var(--line2); --f-tint: transparent;
  --f-line: var(--line2); --f-wash: transparent;
}

/* ═══ shared row vocabulary (tree + narrowed lists) ═══ */
.gSec { padding: 6px 0 2px; }
/* ⛔ 9.5px UPPERCASE IS A RAIL GRAMMAR. The detail column already moved off it
   for this reason and the same reader is looking at this column. */
.gSec h4 { font: 800 11.5px/1.2 var(--disp, Inter, sans-serif); letter-spacing: 0;
  text-transform: none; color: var(--muted); margin: 14px 0 4px 4px; }
.gSec:first-child h4 { margin-top: 4px; }
.gSec h4 span { color: var(--faint); font: 600 11px var(--body, Inter, sans-serif);
  margin-left: 5px; font-variant-numeric: tabular-nums; }
.gSecWhy { font-size: 11px; color: var(--faint); margin: 0 0 4px; font-style: italic; }
.gUnsorted h4 { color: var(--amber, #c2410c); }
/* ⛔ v4: the Goals section (and the Mērķi view) subgroup by field — the
   group header carries the field word and a hue border, "not declared" last
   with none. */
.gFieldGroup { margin: 4px 0 2px; padding: 2px 0 2px 4px;
  border-left: 2px solid var(--f-mid); background: var(--f-wash);
  border-radius: 0 var(--r-s, 12px) var(--r-s, 12px) 0; }
.gFieldGroup h5 { font: 700 11px/1.3 var(--body, Inter, sans-serif); letter-spacing: .01em;
  color: var(--f-ink); margin: 6px 0 2px; }
.gFieldGroup h5 span { color: var(--faint); margin-left: 4px; font-variant-numeric: tabular-nums; }
.gFieldGroupNone h5 { font-style: italic; font-weight: 500; }
/* ⛔ v5: A GRID, NOT A FLEX ROW. The v3/v4 row let the percent overflow —
   .gMain was min-width:0 but .gMeta inside it was not, and .gChips fought it
   for the same pixels, so at a 263px tree "100%" rendered as "10" with the
   digit cut at the column edge (owner's screenshot, 2026-09-08). The percent
   now has a RESERVED 4ch track on its own row: no title length can push it
   out. And the title takes two lines instead of an ellipsis. */
.gNode { display: grid; column-gap: 6px; row-gap: 0; align-items: start;
  grid-template-columns: 16px 8px minmax(0, 1fr) 32px;
  grid-template-areas: "fold dot ttl pct" ". . meta meta";
  /* ⛔ NO --d PADDING. The indent was drawn TWICE — this padding (12px/level)
     AND .gKids' margin+padding+border (17px) — so every level cost 29px of the
     title, which is exactly the measured 151 → 122 → 93 → 64 fall, and the
     64 → 47 step is 17 because the padding capped at depth 3 while the guide
     line did not. The guide line is the indent; this was a second copy of it. */
  padding: 5px 6px 5px 4px;
  border-radius: var(--r-s, 12px); cursor: pointer;
  transition: background .12s, box-shadow .12s; }
.gNode:hover { background: var(--surf); }
/* ⛔ 2px, not 3, and the padding is not eaten: at 3px the rule touched the
   chevron. At depth ≥1 it sits right of the guide line, which is correct —
   the guide line is structure, this is selection. */
.gNode.gOn { background: var(--lav2, var(--surf)); box-shadow: inset 2px 0 0 var(--violet, var(--ink)); }
.gNode > .gFold { grid-area: fold; align-self: start; margin-top: 1px; }
.gNode > .gDot  { grid-area: dot; align-self: start; margin-top: 5px; }
.gNode > .gTtl  { grid-area: ttl; }
.gNode > .gPct  { grid-area: pct; justify-self: end; margin-top: 1px; }
.gNode > .gMeta { grid-area: meta; margin-top: 3px; }
.gNode > .gMeta:empty { display: none; }
.gFold { width: 16px; height: 16px; border: 0; background: none;
  color: var(--faint); cursor: pointer; padding: 0; position: relative; }
.gFold::before { content: ''; position: absolute; inset: 4.5px;
  border: solid var(--faint); border-width: 0 1.5px 1.5px 0;
  transform: rotate(45deg); transition: transform .12s; }
.gFold[aria-expanded="false"]::before { transform: rotate(-45deg); }
.gFold:disabled { visibility: hidden; }
@media (pointer: coarse) {
  .gFold { width: 28px; height: 28px; }
  .gFold::before { inset: 10px; }
  .gNode { grid-template-columns: 28px 8px minmax(0, 1fr) 32px; }
}
/* ⛔ State keeps its OWN three colours and never borrows the field hue. */
.gDot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; background: var(--faint); }
.gDot.s-waiting_for_you { background: var(--rose, #d6455f);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--rose, #d6455f) 18%, transparent); }
.gDot.s-active { background: var(--mint, #2f7a5c); }
.gDot.s-quiet { background: var(--faint); }
.gDot.s-reached { background: var(--mint, #2f7a5c); opacity: .45; }
.gDot.s-parked { background: var(--faint); }
/* ⛔ THE BAR IS CUT FROM THE TREE. A 64px bar carried one bit the number
   already carries and cost ~70px of every title, because a grid column spans
   the whole row: the `auto` track sized itself to .gProg and subtracted that
   from the name at EVERY depth. The bar stays where the percent is the
   subject — the hero, the children list, "closest to finish". */
/* the hero's chips are not in a .gNode grid — they stay a plain flex row */
.gHeroTop .gChips { display: flex; gap: 4px; flex: 0 0 auto; }
.gPct { font: 700 11px/1.5 var(--body, Inter, sans-serif); font-variant-numeric: tabular-nums;
  color: var(--muted); font-style: normal; white-space: nowrap; }
.gNode[data-state="reached"] .gPct { color: var(--faint); }
.gMeta { display: flex; align-items: center; gap: 4px; min-width: 0; overflow: hidden;
  font-size: 11px; color: var(--muted); }
.gMeta i { font-style: normal; }
.gMeta .gChip:first-of-type { margin-left: auto; }
.gBar { display: inline-block; width: 64px; height: 4px; border-radius: 99px;
  background: var(--surf2, var(--line)); overflow: hidden; vertical-align: middle; flex: 0 0 auto; }
.gBar b { display: block; height: 100%; border-radius: inherit; background: var(--f-mid, var(--muted));
  transition: width .32s ease; }
.gBarBig { width: 160px; height: 6px; }
.gChip { font-size: 10.5px; font-variant-numeric: tabular-nums; border-radius: 999px;
  padding: 1px 7px; border: 1px solid var(--line2); color: var(--muted); white-space: nowrap;
  display: inline-flex; align-items: center; }
/* ⛔ A SIGNAL CHIP IS TEXT AND A DOT, NEVER AN EMOJI — the ⚠ glyph renders at
   a different size on every platform, so the chip's height moved per machine. */
.gChip.warn::before, .gChip.live::before { content: ''; width: 6px; height: 6px;
  border-radius: 50%; background: currentColor; margin-right: 4px; flex: none; }
.gChip.warn { border-color: var(--rose, #d6455f); color: var(--rose, #d6455f); }
.gChip.live { border-color: var(--mint, #2f7a5c); color: var(--mint, #2f7a5c); }
.gChip.dim { color: var(--faint); }
.gNode[data-state="reached"] .gName { color: var(--muted); font-weight: 500; }
/* weight tells depth, not size — nothing in a body row goes below 13px */
.gNode:not([data-d="0"]) .gName { font-weight: 500; }
/* ⛔ UNCAPPED. The cap existed to compensate for the double indent; with one
   indent, five depths cost 60px and a capped indent would draw depth 3 and
   depth 5 identically — a wrong picture, not a dense one. */
.gKids { margin-left: 11px; padding-left: 0; border-left: 1px solid var(--line); }
.gKids:hover { border-left-color: var(--line2); }
/* ⛔ State keeps its OWN three colours and never borrows the field hue. The
   #b3261e / #1a7f4b / #9a938a of v3 are pre-clay literals. */
/* ⛔ `grid-area` MUST BE SCOPED TO THE ROW THAT DEFINES THE AREAS. Unscoped,
   `.gDot{grid-area:dot}` followed the dot into the desk cards, whose grid
   names no areas — so each dot minted an implicit `dot` track, collapsed the
   text column to ~60px and rendered every title one character per line
   ("C / o"). Measured on the first real screenshot, 2026-09-08. */
.gNode > .gDot { grid-area: dot; }
.gDot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; background: var(--faint); }
.gDot.s-waiting_for_you { background: var(--rose, #d6455f);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--rose, #d6455f) 18%, transparent); }
.gDot.s-active { background: var(--mint, #2f7a5c); }
.gDot.s-quiet { background: var(--faint); }
.gDot.s-reached { background: var(--mint, #2f7a5c); opacity: .45; }
.gDot.s-parked { background: var(--faint); }
.gNode > .gTtl { grid-area: ttl; }
/* ⛔ THE NAME IS ONE LINE; THE REST IS A DIM SECOND LINE. Measured over the
   live 98 goals: the full title is a median 85 characters and 132 at p90, so
   two lines at 13px in a 300px tree truncated 96 of 98 rows — the owner's
   complaint. Split on the em dash the titles already carry (server-side, via
   goal-parts' splitTitle, so there is ONE definition), the name is a median 16
   and the lede can clip without losing what the goal IS. */
/* ⛔ THE NAME AND THE LEDE CLAMP SEPARATELY. The first version put the lede
   INSIDE the name's -webkit-box, so line-clamp:2 counted both and ate the
   lede entirely — "Company operating system" wrapped to two lines and its
   own description never appeared. A clamp box counts every line it contains;
   two independent limits need two boxes. */
.gTtl { min-width: 0; display: block; }
/* ⛔ `anywhere` LETS THE BROWSER COUNT ON BREAKING INSIDE WORDS when it sizes
   the box, so a minmax(0,1fr) track under pressure is shrunk below the longest
   word and then breaks it — "Compan y grap…". `break-word` keeps the longest
   word as the floor and only breaks a token wider than the whole line, which
   is right for a 40-character slug and wrong for "Compliance". ⛔ And the
   WIDTH fix above is what actually stopped the breaking; this stops the box
   from volunteering to be too narrow again. `hyphens: manual` so no engine
   hyphenates Latvian by an English dictionary. */
.gName { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; overflow-wrap: break-word; word-break: normal; hyphens: manual;
  font: 600 13px/1.3 var(--body, Inter, sans-serif); color: var(--ink); }
/* ⛔ A ROW WITH NO LEDE GETS THE LEDE'S LINE BACK. Twenty of the live 98 carry
   no em dash to split on, so clamping their name at two lines cut it for
   nothing — the box is the same height either way. */
.gName.gNameOnly { -webkit-line-clamp: 3; }
.gLede { font: 400 11px/1.35 var(--body, Inter, sans-serif); font-style: normal;
  color: var(--muted); margin-top: 1px; overflow-wrap: break-word; word-break: normal; hyphens: manual;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.gDeskTtl .gLede { -webkit-line-clamp: 3; }

/* ═══ v4 field + kind chips — field FIRST (hue-tinted), kind smaller/dim ═══ */
/* ⛔ 10.5px IS THE FLOOR. v4's 9-9.5px chips drop the word at a glance, and
   the word is the one thing the chip exists to carry — a colour-blind reader
   and a grayscale handover screenshot both have only the word. */
.gFieldChip { display: inline-flex; align-items: center; gap: 5px; height: 18px;
  padding: 0 8px 0 6px; border-radius: var(--r-pill, 999px);
  font: 600 10.5px/1 var(--body, Inter, sans-serif); letter-spacing: .01em;
  white-space: nowrap; color: var(--f-ink);
  background: var(--f-tint); border: 1px solid var(--f-line); }
.gFieldChip::before { content: ''; width: 6px; height: 6px; border-radius: 2px;
  background: var(--f-mid); flex: none; }
/* undeclared reads as undeclared even in grayscale: dashed, italic, no dot fill */
.gFieldChip.gFieldNone { font-weight: 500; font-style: italic; border-style: dashed; }
/* ⛔ a bordered square reads as an unticked CHECKBOX — a control, not a
   state. A dash says "none" and cannot be pressed. */
.gFieldChip.gFieldNone::before { background: var(--line2); width: 7px; height: 1.5px;
  border-radius: 1px; border: 0; }
.gFieldChipSm { height: 16px; font-size: 10.5px; padding: 0 6px 0 5px; }
.gKindChip { border-radius: 999px; padding: 0 6px; font-size: 10.5px; white-space: nowrap;
  color: var(--faint); border: 1px dotted var(--line2); font-style: italic; }

/* ═══ v4: docs as cards (v3 had a flat list) ═══ */
.gDocs { list-style: none; padding: 0; margin: 4px 0; display: grid; gap: 4px; }
.gDocCard { display: flex; gap: 8px; align-items: baseline; padding: 6px 9px;
  border: 1px solid var(--line); border-radius: 6px; background: var(--panel2, transparent); }
.gDocKind { font-size: 10.5px; color: var(--faint); text-transform: lowercase; letter-spacing: 0.02em;
  border: 1px dotted var(--line2); border-radius: 999px; padding: 0 6px; flex: 0 0 auto; }
.gDocTtl { font-size: 12.5px; }

/* ═══ the split: tree left, detail right ═══
   ⛔ v5: THE 62vh CAPS ARE GONE. They wasted the bottom third of a tall screen
      and could not grow. .osApp is already position:fixed top:54 bottom:0 flex
      column (os-app.css:11-14); the height just has to pass down through
      .wrap.gWrap → .osAppCard → here.
   ⛔ AND THE TREE IS A FIXED 300px, NOT minmax(230px, 32%). At the measured
      824px card, 32% gave 263 — the width at which every title ellipsised. */
/* ⛔ 300px FIXED WAS TOO NARROW FOR REAL TITLES and 32% was too narrow at the
   measured 824px card. It grows with the card and stops: at the 1288 card of a
   1440 screen that is 322, at a 1920 screen 400, and it never falls below the
   300 the row grid needs. */
.gSplit { display: grid; grid-template-columns: clamp(280px, 26%, 400px) minmax(0, 1fr);
  gap: 0; align-items: stretch; flex: 1 1 auto; min-height: 0; }
.gTreeCol { overflow-y: auto; padding: 6px 8px 12px 6px;
  border-right: 1px solid var(--line); min-height: 0; scrollbar-width: thin; }
.gBlocks { display: flex; flex-direction: column; gap: 1px; }
.gDetailCol { display: flex; flex-direction: column; min-width: 0; min-height: 0; }

/* ═══ the three sizes — measured on the CARD, never on the viewport ═══════
   ⛔ The rails come and go at 1180 and 860 VIEWPORT, so a viewport query
      cannot say how wide this pane is. Container queries can. */
@container goals (max-width: 1099px) {
  /* ⚡ the field chip STAYS at mid now: with the bar cut and the indent drawn
     once, a 280px tree has room for it, and hiding it dropped the only carrier
     of the field word on a row. */
}
@container goals (max-width: 759px) {
  /* ⛔ THE MAIL IDIOM, not an overlay and not a segmented control. A drawer
     covering the tree it navigated from is what v3 already rejected; a
     segmented control's second segment is dead until a selection exists.
     os-mail.css:376 already teaches this gesture to the same people. */
  .gSplit { grid-template-columns: minmax(0, 1fr); }
  .gTreeCol { border-right: 0; }
  .gSplit[data-reading="true"] .gTreeCol { display: none; }
  .gSplit:not([data-reading="true"]) .gDetailCol { display: none; }
  /* ⛔ THE ROW TEMPLATE NO LONGER CHANGES BY WIDTH. It used to grow a fourth
     area here so a count badge had somewhere to sit; with the bar gone and the
     percent in a fixed track, one template fits 322px and 1288px alike — and a
     template that differs by width is a second layout to keep true. */
}

/* ⚠ A short screen gets ONE long page, not two 200px scroll wells. */
@media (max-height: 600px) {
  .osApp[data-app="goals"] { overflow: auto; }
  .osApp[data-app="goals"] .osAppCard { flex: none; }
  .gTreeCol, .gDetail { overflow: visible; }
}

/* ═══ the detail column ═══ */
.gDetailEmpty { flex: 1 1 auto; display: flex; align-items: center; justify-content: center;
  padding: 30px 20px; }
.gDetailHead { display: flex; align-items: center; gap: 10px; padding: 8px 12px 6px;
  border-bottom: 1px solid var(--line); flex: 0 0 auto; min-height: 34px; }
.gBack { font: inherit; font-size: 12px; border: 0; background: none; color: var(--muted);
  cursor: pointer; padding: 3px 8px; border-radius: 999px; flex: 0 0 auto; }
.gBack:hover:not(:disabled) { background: var(--surf); color: var(--ink); }
.gBack:disabled { opacity: .35; cursor: default; }
.gCrumb { font-size: 11px; color: var(--faint); white-space: nowrap; overflow: hidden;
  text-overflow: ellipsis; min-width: 0; }
.gDetail { flex: 1 1 auto; overflow-y: auto; padding: 14px 18px 18px; min-height: 0;
  scrollbar-width: thin; }
/* Back and the pin stay reachable however long the goal is */
.gDetailHead { position: sticky; top: 0; z-index: 2;
  background: var(--panelT, var(--panel)); backdrop-filter: blur(8px); }
.gDetailLoad { color: var(--muted); font-size: 12.5px; }

.gHero { position: relative; padding: 14px 0 12px; margin-bottom: 6px; }
.gHero::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 4px;
  border-radius: 99px; background: linear-gradient(90deg, var(--f-mid), transparent 60%); }
.gHeroTop { display: flex; align-items: center; gap: 8px; min-width: 0; flex-wrap: wrap; }
.gHeroTtl { font: 800 20px/1.2 var(--disp, Inter, sans-serif); letter-spacing: -.01em;
  color: var(--ink); margin: 0; min-width: 0; overflow-wrap: break-word; word-break: normal; hyphens: manual; }
.gHeroMeta { margin-top: 4px; flex-wrap: wrap; }
.gPurpose { flex: 1 1 100%; font-size: 12px; color: var(--muted); }
.gPurposeNone { font-style: italic; color: var(--faint); }
.gStateW { border-bottom: 1px dotted var(--line2); cursor: help; }

/* ═══ v4 limits rail — hidden entirely when the goal declares none ═══ */
.gLimits { border: 1px dashed var(--f-line); border-radius: var(--r-s, 12px);
  padding: 8px 12px; margin: 0 0 10px; }
.gLimits h5 { font: 700 11px/1.2 var(--body, Inter, sans-serif);
  color: var(--muted); margin: 0 0 4px; }
.gLimits ul { list-style: none; margin: 0; padding: 0; }
.gLimit { font-size: 11.5px; color: var(--muted); padding: 2px 0 2px 14px; position: relative; }
.gLimit::before { content: '·'; position: absolute; left: 4px; color: var(--faint); }

/* the served next step — the column's hero line */
.gNext { border: 0; border-radius: var(--r-s, 12px); background: var(--cream, transparent);
  box-shadow: var(--clay, none); padding: 12px 14px; margin: 6px 0 14px; }
.gNext h5 { font: 800 11px/1.2 var(--disp, Inter, sans-serif); letter-spacing: .01em;
  color: var(--muted); margin: 0 0 4px; }
.gNext p { margin: 0; font-size: 13px; color: var(--ink); }
.gNext p span { display: block; color: var(--muted); font-size: 12px; margin-top: 2px; }
.gNextNone p { color: var(--faint); font-style: italic; }

/* ⛔ main + a 300px ASIDE, not two equal halves — and the aside belongs to the
   SELECTED GOAL, never a global context panel (which would restate rmGoals,
   live and people in the dock: two answers to one question). */
.gCols { display: grid; grid-template-columns: minmax(0, 1fr); gap: 0 22px; }
@container goals (min-width: 1100px) {
  .gCols { grid-template-columns: minmax(0, 1fr) 300px; }
  /* the aside is 300 wide — a full-bleed image stack was a 500px decision */
  .gImgStack .gImg { max-height: 260px; object-fit: cover; }
}
/* ⛔ THE THREE VERBS STAY IN THE FOOT AT EVERY WIDTH. The design called for
   moving them into the hero on a wide card, on the reasoning that a band under
   a 1000px column is not reached for — but .gDetailFoot is `flex: 0 0 auto` at
   the bottom of a column that scrolls INSIDE itself, so the band is pinned and
   always on screen. The rule that hid it was also dead twice over: .gHeroActs
   was never rendered, so hiding the foot would have left no verbs at all, and
   `.gDetailFoot{display:flex}` is declared later at equal specificity, so the
   hide never applied either. A rule that claims a behaviour the markup does
   not have is the same defect as a manifest naming a verb that does not
   exist. */
.gCol { min-width: 0; }

/* sections inside the detail */
/* ⛔ 9.5px UPPERCASE IS A RAIL GRAMMAR, not a column one. At 600-900px of
   width eleven of those labels vanish; x-ray already moved to a display face
   for exactly this reason (os-xray.css:52). */
.gDSec { margin: 16px 0 0; padding-top: 12px; border-top: 1px solid var(--line); }
.gDSec:first-child { border-top: 0; padding-top: 0; }
.gDSec h5 { font: 800 12.5px/1.2 var(--disp, Inter, sans-serif); letter-spacing: 0;
  color: var(--ink); margin: 0 0 6px; display: flex; align-items: baseline; gap: 6px; }
.gDSec h5 b { font: 600 11px var(--body, Inter, sans-serif); color: var(--faint);
  font-variant-numeric: tabular-nums; }

/* plan steps — served, numbered, each with what-you-get */
.gSteps { list-style: none; margin: 0; padding: 0; }
.gStep { display: flex; align-items: baseline; gap: 8px; padding: 5px 0;
  border-bottom: 1px dashed var(--line); }
.gStep:last-child { border-bottom: 0; }
.gStepN { font-size: 10.5px; color: var(--faint); font-variant-numeric: tabular-nums;
  flex: 0 0 auto; width: 16px; text-align: right; }
.gStepMain { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.gStepMain b { font-size: 12.5px; font-weight: 500; color: var(--ink); }
.gWhat { font-size: 11px; color: var(--muted); font-style: normal; }
.gStepTally { font-size: 10.5px; color: var(--faint); }
.gStepDone .gStepMain b { color: var(--muted); text-decoration: line-through; }

/* tasks / sessions / files */
.gTasks, .gNow, .gFiles, .gDecides, .gKidList, .gPeople { list-style: none; margin: 0;
  padding: 0; font-size: 12px; }
.gTasks li { padding: 3px 0 3px 14px; position: relative; color: var(--ink); }
.gTasks li::before { content: '○'; position: absolute; left: 0; color: var(--faint); }
.gTasks li.t-doing::before { content: '●'; color: var(--mint, #2f7a5c); }
.gNow li { padding: 3px 0; display: flex; align-items: baseline; gap: 6px; min-width: 0; }
.gNow li b { font-weight: 500; color: var(--ink); }
.gNow li span { color: var(--muted); flex: 1 1 auto; min-width: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.gNow li i { color: var(--faint); font-style: normal; font-size: 10.5px; }
.gNow li.gQuiet { opacity: .55; }
.gFiles li { padding: 2px 0; color: var(--ink); white-space: nowrap; overflow: hidden;
  text-overflow: ellipsis; }
.gFilesSrc li { font-family: ui-monospace, Menlo, monospace; font-size: 10.5px; color: var(--muted); }
.gSrcLine { margin: 2px 0; font-size: 11.5px; }
.gSrcLine b { font-weight: 500; color: var(--ink); }
.gSrcLine i { color: var(--faint); font-style: normal; font-variant-numeric: tabular-nums; }
.gDim { color: var(--faint); font-size: 11.5px; margin: 2px 0; }
.gWhy { font-style: italic; }

/* input files & images — the material renders as what it is */
.gInputs li.f-data { white-space: normal; }
.gFileLink { color: var(--ink); text-decoration: none; border-bottom: 1px dotted var(--line2); }
.gFileLink:hover { color: var(--ink); border-bottom-color: var(--muted); }
.gImgIt { display: flex; flex-direction: column; gap: 3px; padding: 4px 0; }
.gImg { max-width: 100%; max-height: 180px; border-radius: 8px; border: 1px solid var(--line);
  object-fit: cover; }
.gImgIt span { font-size: 11px; color: var(--muted); }

/* decisions, children, people */
.gDecides li { padding: 3px 0; color: var(--ink); font-size: 12px; }
.gKidList li { display: flex; align-items: center; gap: 8px; padding: 4px 6px;
  border-radius: 8px; cursor: pointer; }
.gKidList li:hover { background: var(--surf); }
.gKidList li .gTtl { flex: 1 1 auto; }
.gPeople li { display: flex; align-items: baseline; gap: 6px; padding: 2px 0; min-width: 0; }
.gPeople li b { font-weight: 500; color: var(--ink); flex: 0 0 auto; }
.gPeople li span { color: var(--muted); flex: 1 1 auto; min-width: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.gPeople li i { color: var(--faint); font-style: normal; font-size: 10.5px; flex: 0 0 auto; }

/* detail foot — the three verbs, pinned under the column */
.gDetailFoot { display: flex; gap: 6px; padding: 10px 14px; border-top: 1px solid var(--line);
  flex: 0 0 auto; }
.gAct { font: inherit; font-size: 12px; padding: 6px 12px; border-radius: 999px; cursor: pointer;
  border: 1px solid var(--line2); background: none; color: var(--muted); }
.gAct:hover { background: var(--surf); color: var(--ink); }
.gAct:disabled { opacity: .5; cursor: default; }
.gActWork { border: 0; background: var(--fill, var(--ink)); color: var(--onfill, #fff);
  font-weight: 800; box-shadow: var(--clay, none); }
.gActWork:hover { background: var(--fill, var(--ink)); color: var(--onfill, #fff);
  transform: translateY(-1px); box-shadow: var(--lift-2, none); }
.gActDone { border-color: var(--line2); }
.gActFull { margin-left: auto; }

/* ═══ v4 — activity timeline ═══ */
.gTimelineWrap { min-width: 0; }
.gFilterRow { display: flex; gap: 4px; flex-wrap: wrap; margin: 0 0 6px; }
.gFilterChip { font: inherit; font-size: 10.5px; padding: 2px 9px; border-radius: 999px;
  border: 1px solid var(--line2); background: none; color: var(--muted); cursor: pointer; }
.gFilterChip:hover { background: var(--surf); }
.gFilterChip.on { background: var(--fill, var(--ink)); color: var(--onfill, #fff); border-color: transparent; }
.gDay h6 { font: 700 10.5px/1.4 var(--body, Inter, sans-serif); letter-spacing: .02em;
  color: var(--faint); margin: 8px 0 2px; }
.gTimeline { list-style: none; margin: 0; padding: 0 0 0 12px; border-left: 1px solid var(--line); }
.gTimeline li { position: relative; display: flex; align-items: baseline; gap: 7px;
  padding: 3px 0; font-size: 12px; min-width: 0; }
.gTimeline li::before { content: ''; position: absolute; left: -15px; top: 9px;
  width: 5px; height: 5px; border-radius: 50%; background: var(--faint); }
.gTimeline li.a-done::before { background: var(--mint, #2f7a5c); }
.gTimeline li.a-decided::before { background: var(--rose, #d6455f); }
.gTimeline li.a-kept::before { background: var(--amber, #c2410c); }
.gTimeline li.a-proved::before { background: var(--cyan, #22d3ee); }
.gTimeline li.a-asked::before { background: var(--violet, #7c5cff); }
.gActKind { flex: 0 0 auto; font-size: 10.5px; color: var(--faint);
  border: 1px dotted var(--line2); border-radius: 999px; padding: 0 5px; }
.gActLine { flex: 1 1 auto; min-width: 0; color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.gTimeline li i { flex: 0 0 auto; color: var(--faint); font-style: normal; font-size: 10.5px; }

/* ═══ v4 — answered decisions fold ═══ */
.gAnswered { margin-top: 6px; }
.gAnsToggle { font: inherit; font-size: 10.5px; border: 0; background: none;
  color: var(--muted); cursor: pointer; padding: 2px 0; }
.gAnsToggle:hover { color: var(--ink); }
.gAnsToggle b { font-variant-numeric: tabular-nums; }
.gAnsList { margin-top: 3px; }
.gAnsIt { display: flex; flex-direction: column; gap: 1px; }
.gAnsIt b { font-weight: 500; color: var(--muted); font-size: 11.5px; }
.gAnsIt span { color: var(--faint); font-size: 11px; }

/* ═══ v4 — proof chip on tasks ═══ */
.gTasks li { display: flex; align-items: center; gap: 6px; }
.gTaskTtl { flex: 1 1 auto; min-width: 0; white-space: nowrap; overflow: hidden;
  text-overflow: ellipsis; }
.gProofChip { font: inherit; font-size: 10px; flex: 0 0 auto; border-radius: 999px;
  padding: 0 7px; border: 1px solid var(--f-line);
  color: var(--f-ink); background: var(--f-tint);
  cursor: pointer; font-variant-numeric: tabular-nums; }
.gProofChip:hover { filter: brightness(.97); }

/* ═══ the task view (owner, 2026-09-10) — a row is three lines: the title,
   what it is, what you can do to it. ⛔ The v4 rule above made every li a
   one-line flex row; this one stacks, so the selector below wins by order and
   never by !important. ═══ */
.gTasks li { display: flex; flex-direction: column; align-items: stretch; gap: 4px;
  padding: 7px 0 7px 14px; }
.gTasks li + li { border-top: 1px solid var(--f-line, rgba(75,72,120,.1)); }
.gTaskTop { display: flex; align-items: center; gap: 6px; min-width: 0; }
.gTaskTop .gTaskTtl { white-space: normal; }
.gTaskMeta { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
.gTaskState { display: inline-flex; align-items: center; gap: 5px; font-size: 11px;
  color: var(--muted); white-space: nowrap; }
.gTaskState::before { content: ''; width: 6px; height: 6px; border-radius: 99px;
  background: var(--faint); }
.gTaskState[data-state="doing"]::before { background: var(--mint, #2f7a5c); }
.gTaskState[data-state="waiting"]::before { background: #b8791f; }
/* the priority is a NUMBER, in a face that says "compared, not read" */
.gTaskPri { font: 400 10.5px var(--osMono, ui-monospace, Menlo, monospace); color: var(--faint);
  border: 1px solid var(--f-line, rgba(75,72,120,.14)); border-radius: 999px; padding: 2px 6px; }
.gTaskKind { font-size: 10.5px; color: var(--faint); border: 1px solid var(--f-line, rgba(75,72,120,.14));
  border-radius: 999px; padding: 2px 7px; }
.gTaskActs { display: flex; gap: 5px; flex-wrap: wrap; margin-top: 1px; }
.gTaskAct { font: inherit; font-size: 11px; border-radius: 9px; padding: 3px 9px;
  border: 1px solid var(--f-line, rgba(75,72,120,.14)); background: none; color: var(--muted);
  cursor: pointer; transition: color .15s, border-color .15s; }
.gTaskAct:hover:not(:disabled) { color: var(--ink); border-color: hsl(258 60% 62% / .5); }
.gTaskAct.isYes { color: var(--mint, #2f7a5c); border-color: color-mix(in srgb, var(--mint, #2f7a5c) 40%, transparent); }
.gTaskAct:disabled { opacity: .5; cursor: default; }

/* ═══ v4 — images as a full-width lazy stack (locked decision: no lightbox,
   no thumbnail rail — the column's width is the frame) ═══ */
.gImgStack { display: flex; flex-direction: column; gap: 8px; }
.gImgStack .gImg { width: 100%; max-height: none; height: auto; object-fit: contain;
  border-radius: 8px; border: 1px solid var(--line); margin: 0; }

/* ═══ v4 — notes ═══ */
.gScope { font-size: 10.5px; color: var(--faint); font-style: italic; font-weight: 400; }
.gNotes { list-style: none; margin: 0; padding: 0; font-size: 12px; }
.gNotes li { display: flex; align-items: baseline; gap: 6px; padding: 3px 0; min-width: 0; }
.gNoteDot { flex: 0 0 auto; width: 6px; height: 6px; border-radius: 50%;
  border: 1px solid var(--line2); background: none; }
.gNotes li.n-ans .gNoteDot { background: var(--mint, #2f7a5c); border-color: var(--mint, #2f7a5c); }
.gNoteBody { flex: 1 1 auto; min-width: 0; color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.gNotes li i { flex: 0 0 auto; color: var(--faint); font-style: normal; font-size: 10.5px; }

/* ═══ v4 — people as an avatar row (initials, hue = the goal's field) ═══ */
.gPeopleRow { list-style: none; margin: 0; padding: 0; display: flex; gap: 10px;
  flex-wrap: wrap; }
.gAvatar { display: flex; flex-direction: column; align-items: center; gap: 2px;
  width: 58px; cursor: default; }
.gAvatar b { width: 28px; height: 28px; border-radius: 50%; display: flex;
  align-items: center; justify-content: center; font-size: 10px; font-weight: 600;
  color: var(--onfill, #fff); background: var(--f-mid, var(--muted)); }
.gAvName { font-size: 11px; color: var(--muted); max-width: 58px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.gAvatar i { font-size: 9px; color: var(--faint); font-style: normal; }


/* ═══════════════════════════════════════════════════════════════════════════
   v5 · THE DESK — what fills the right column BEFORE a selection
   ⛔ An empty column saying "pick a goal" is the pane refusing the job it was
      opened for. Everything here is the LENGTH of an array /api/goals already
      served — the pane sums nothing and re-ranks nothing.
   ═══════════════════════════════════════════════════════════════════════════ */
.gDesk { display: grid; gap: 16px; grid-template-columns: minmax(0, 1fr); }
@container goals (min-width: 1100px) {
  .gDesk { grid-template-columns: minmax(0, 1fr) 300px; align-items: start; }
}
.gDeskCol { min-width: 0; display: flex; flex-direction: column; gap: 16px; }
.gDeskSec h5 { font: 800 12.5px/1.2 var(--disp, Inter, sans-serif); color: var(--ink);
  margin: 0 0 8px; display: flex; align-items: baseline; gap: 6px; }
/* ⛔ `margin-left:auto` pushed "3" six hundred pixels from "Waiting on you".
   A count belongs beside the thing it counts. */
.gDeskSec h5 b { font: 600 11px var(--body, Inter, sans-serif); color: var(--faint);
  font-variant-numeric: tabular-nums; }
.gDeskCards { display: grid; gap: 8px; }
.gDeskCard { display: grid; grid-template-columns: 8px minmax(0, 1fr); gap: 10px;
  align-items: start; padding: 12px 14px; border: 0; text-align: left;
  border-radius: var(--r-s, 12px); background: var(--cream, transparent);
  box-shadow: var(--lift, none); cursor: pointer; font: inherit;
  transition: transform .16s, box-shadow .16s; }
.gDeskCard:hover { transform: translateY(-1px); box-shadow: var(--lift-2, none); }
.gDeskCard .gDot { margin-top: 4px; }
.gDeskBody { min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.gDeskTtl { font: 600 13px/1.3 var(--body, Inter, sans-serif); color: var(--ink);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; overflow-wrap: break-word; word-break: normal; hyphens: manual; }
.gDeskMeta { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.gDeskWhy { font-size: 11.5px; color: var(--muted); font-style: italic; }

/* the ten categories, as tiles. ⛔ A tile that does nothing is a dead label —
   pressing one narrows the tree by that field. */
/* ⛔ auto-fill GAVE FOUR PER ROW IN A ~614px COLUMN, so the sixth tile — the
   dashed "field not declared" — wrapped alone under the grid and read as a
   bare number outside it (owner's screenshot). A fixed count keeps six tiles
   as 3x2, and the undeclared one lands bottom-right, in the grid. */
.gTiles { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
@container goals (max-width: 1099px) { .gTiles { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.gTile { display: flex; flex-direction: column; gap: 4px; padding: 10px 12px;
  min-height: 64px; justify-content: space-between;
  border: 1px solid var(--f-line); background: var(--f-wash); border-radius: var(--r-s, 12px);
  cursor: pointer; font: inherit; text-align: left;
  transition: transform .16s, box-shadow .16s; }
.gTile:hover { transform: translateY(-1px); box-shadow: var(--lift, none); }
.gTile.on { box-shadow: inset 0 0 0 2px var(--f-mid); }
.gTileWord { font: 700 11.5px/1.2 var(--body, Inter, sans-serif); color: var(--f-ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.gTileNone .gTileWord { font-style: italic; font-weight: 500; }
/* ⛔ a dashed line2 border on a transparent ground reads as NO box beside five
   tinted ones. It needs a ground of its own to be a tile at all. */
.gTileNone { background: var(--surf); border: 1.5px dashed var(--line2); }
.gTileNone .gTileN { color: var(--muted); }
.gTileN { font: 800 20px/1 var(--disp, Inter, sans-serif); color: var(--ink);
  font-variant-numeric: tabular-nums; }
.gTileBar { height: 3px; border-radius: 99px; background: var(--surf2, var(--line)); overflow: hidden; }
.gTileBar b { display: block; height: 100%; background: var(--f-mid); }
.gDeskRows { list-style: none; margin: 0; padding: 0; }
.gDeskRow { display: grid; grid-template-columns: 8px minmax(0, 1fr) auto; gap: 8px;
  align-items: center; padding: 6px 4px; border-radius: var(--r-s, 12px);
  cursor: pointer; border: 0; background: none; font: inherit; width: 100%; text-align: left; }
.gDeskRow:hover { background: var(--surf); }
.gDeskRow span { min-width: 0; font-size: 12.5px; color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.gDeskRow i { font-style: normal; font-size: 11px; color: var(--muted);
  font-variant-numeric: tabular-nums; }

/* ═══ v5 · the pin control — only inside a room ═══ */
.gPinBtn { font: inherit; font-size: 11.5px; padding: 4px 11px; border-radius: var(--r-pill, 999px);
  border: 1px solid var(--line2); background: none; color: var(--muted);
  cursor: pointer; margin-left: auto; flex: 0 0 auto; white-space: nowrap; }
.gPinBtn:hover { background: var(--surf); color: var(--ink); }
.gPinBtn.on { border-color: var(--violet, var(--ink)); color: var(--violet, var(--ink));
  background: var(--lav2, transparent); }
.gPinBtn:disabled { opacity: .5; cursor: default; }

/* ═══ v5 · the loading skeleton — three bars, no layout shift on arrival ═══ */
.gSkel { display: flex; flex-direction: column; gap: 8px; padding: 6px 0; }
.gSkel span { display: block; height: 12px; border-radius: 99px; background: var(--surf2, var(--line)); }
.gSkel span:nth-child(1) { width: 60%; }
.gSkel span:nth-child(2) { width: 90%; }
.gSkel span:nth-child(3) { width: 40%; }

/* ═══ v5 · motion. ⛔ The tree never moves while a detail loads. ═══ */
.gDetail > .gGoalView, .gDetail > .gDesk { animation: gIn .14s ease-out both; }
@keyframes gIn { from { opacity: 0; transform: translateY(4px) } to { opacity: 1; transform: none } }
@container goals (max-width: 759px) {
  .gSplit[data-reading="true"] .gDetailCol { animation: gSlide .16s ease-out both; }
}
@keyframes gSlide { from { opacity: 0; transform: translateX(12px) } to { opacity: 1; transform: none } }
.gChip.live::after { content: ''; display: inline-block; width: 5px; height: 5px;
  margin-left: 4px; border-radius: 50%; background: currentColor;
  animation: gPulse 2s ease-in-out infinite; }
@keyframes gPulse { 0%, 100% { transform: scale(1); opacity: 1 }
                    40% { transform: scale(1.35); opacity: .4 } }
@media (prefers-reduced-motion: reduce) {
  .osApp[data-app="goals"] *, .osApp[data-app="goals"] *::before,
  .osApp[data-app="goals"] *::after {
    animation: none !important; transition: none !important; }
}
