/* ================= THE ALTERNATE UX — COMPONENTS =================
   Buttons, chips, inputs, menus, progress, avatars, and the three states every surface needs
   (loading, empty, error). Scoped to `.alt-app`; nothing here escapes to a production page.
   Five button roles, and the rule is emphasis rather than colour: exactly one primary per
   surface — learned on the OKR row, where five peer actions once painted five blue blocks. */

.alt-app{ font-family:var(--a-body); color:var(--a-text); background:var(--a-bg); }
.alt-app *,.alt-app *::before,.alt-app *::after{ box-sizing:border-box; }
.alt-app h1,.alt-app h2,.alt-app h3,.alt-app h4{ font-family:var(--a-display); margin:0; }

/* ---------- buttons ---------- */
.a-btn{
  display:inline-flex; align-items:center; justify-content:center; gap:6px;
  height:var(--a-ctl); padding:0 12px; border-radius:var(--a-r-sm);
  font-family:var(--a-body); font-size:13px; font-weight:600; line-height:1;
  background:var(--a-raised); color:var(--a-text); border:1px solid var(--a-line);
  cursor:pointer; white-space:nowrap; text-decoration:none;
}
.a-btn:hover{ border-color:var(--a-line-strong); }
.a-btn:focus-visible{ outline:2px solid var(--a-ring); outline-offset:2px; }
.a-btn[disabled]{ opacity:.5; cursor:not-allowed; }
/* An icon beside a word needs a size of its own — an icon-ONLY button worked by accident (the
   <svg> has no dimensions and stretches to fill the button), but beside text it's a flex item
   with no basis and collapses to 0×0 (measured: the search magnifier, Create, Focus and the
   calendar's Planning/+Task all drew nothing). `> svg` is a direct-child combinator so it can't
   reach glyphs already wrapped for a deliberate size (`.a-w-fold .cv svg` etc); `:not(.icon)`
   leaves icon-only buttons untouched. */
.a-btn:not(.icon) > svg,
.a-focus > svg,
.a-search-ico > svg{ width:15px; height:15px; flex:0 0 auto; }
/* `.a-more` IS THE SAME DEFECT WEARING A DIFFERENT CLASS. It is an inline-flex button with no
   svg sizing, declared in alt-tasks.css but drawn on note cards and checklist items too — the
   accidental cross-page sharing that comes of notes.html loading the task stylesheet. Its
   glyph measured 0×0 on fifteen visible controls on the notes page: an overflow menu on every
   card that a reader can hover, focus and press, and cannot see. Sized here, with the other
   shared primitives, rather than deepening the notes page's dependency on a task stylesheet. */
.a-more > svg{ width:15px; height:15px; flex:0 0 auto; }
.a-btn.primary{ background:var(--a-blue-fill); border-color:transparent; color:#fff; }
.a-btn.primary:hover{ filter:brightness(1.08); }
.a-btn.secondary{ background:transparent; border-color:var(--a-line-strong); }
.a-btn.tertiary{ background:transparent; border-color:transparent; color:var(--a-dim); }
.a-btn.tertiary:hover{ background:var(--a-raised); color:var(--a-text); }
.a-btn.ghost{ background:transparent; border-color:transparent; color:var(--a-dim); padding:0 8px; }
.a-btn.ghost:hover{ background:var(--a-raised); color:var(--a-text); }
.a-btn.danger{ background:transparent; border-color:var(--a-red); color:var(--a-red); }
.a-btn.danger:hover{ background:var(--a-red-soft); }
.a-btn.icon{ width:var(--a-ctl); padding:0; }
/* ---------- AND THE GLYPH IS SIZED, NOT MERELY PAINTED AT A SIZE ----------

   REPORTED AS "the collapse arrow on the Task details pane does not show in Safari". It is not
   one arrow: `.a-btn.icon` without `.sm` had NO svg rule at all. `.a-btn:not(.icon) > svg` above
   excludes it by construction, and `.a-btn.icon.sm > svg` below is scoped to `.sm` — so on
   **27 controls** the glyph had a size only because altIcon() emits `<svg viewBox="0 0 24 24">`
   with no width or height and Chromium happens to resolve an unsized flex item onto the
   button's content box. Nothing in this stylesheet ever asked for 30px; Chromium chose it.

   MEASURED, signed in, both themes: Chrome and Edge both draw 30x30 in a 32x32 button, with an
   opaque non-default stroke — so the colour explanation is ruled out, and the two screenshots
   differing in theme as well as browser was a red herring. Safari was NOT measured: enabling
   safaridriver needs interactive sudo, which this environment cannot give. So the SIZE this
   rule states is verified; that Safari was failing for want of it is inference from the
   mechanism, not observation.

   30px RATHER THAN calc(var(--a-ctl) - 2px), matching `.icon.sm`'s fixed 15px directly below:
   two sibling rules describing the same kind of control should not disagree about whether the
   glyph tracks the control variable. Same value Chromium already produced, so this changes
   nothing where it currently works — confirmed byte-identical geometry before and after in both
   Chromium browsers, both themes.

   `flex:0 0 auto` IS PART OF THE FIX, not decoration. A sized svg that may still shrink is a
   sized svg that shrinks, which is exactly how `.icon.sm`'s glyph fell to 12px inside a 12px
   content box — see the note below. */
.a-btn.icon > svg{ width:30px; height:30px; flex:0 0 auto; }
/* ---------- A RASTER GLYPH THAT STILL PAINTS IN currentColor: THE CSS-MASK ICON ----------

   Every glyph above is hand-drawn SVG on `currentColor` (JS/components/alt-icons.js) precisely
   so it survives dark mode and a custom accent. That stops working the moment the artwork is a
   supplied raster rather than a path list — the Quick Start Guide button's book icon
   (img/quick-start-guide2.png) is the reader's own PNG, and a fixed-colour bitmap is invisible on
   a near-white light theme and can never follow a custom accent either, which is the exact
   property that glyph was asked to keep. `filter:` can shift a colour but cannot GUARANTEE
   currentColor across arbitrary theme colours; a CSS mask can, because the image supplies only
   the SHAPE (its alpha channel) and `background-color` supplies the paint — pixel-identical
   artwork, full theme correctness, with no second colour system introduced. This is the
   canonical way to theme any future raster icon this application needs, not a one-off for this
   button.

   BOTH THE UNPREFIXED AND `-webkit-` MASK PROPERTIES ARE SET: `mask-*` still needs the prefix in
   some current Chromium/Safari builds this application targets, and the unprefixed form is what
   a future engine expects — dropping either silently blanks the icon in that engine. */
.a-ico-mask{
  display:inline-block;
  background-color:currentColor;
  -webkit-mask-repeat:no-repeat; mask-repeat:no-repeat;
  -webkit-mask-position:center; mask-position:center;
  -webkit-mask-size:contain; mask-size:contain;
}
/* Sized exactly like `.a-btn.icon > svg` above — same row, same 30x30 box, so the masked glyph
   reads as one more topbar icon rather than a control that quietly measures differently. */
.a-btn.icon > .a-ico-mask{ width:30px; height:30px; flex:0 0 auto; }
/* THE QUICK START GUIDE BUTTON'S OWN ARTWORK. img/quick-start-guide2.png is 90x84 — encoded for
   a 2x/3x display rendering at this control's 30 CSS px, never upscaled — transparent, and the
   only source of its shape; nothing here recolours it, `currentColor` does.

   THE OWNER REPLACED THE PICTURE ON 2026-09-10: "Replace the quick start guide icon with the one
   in img labeled quick-start-guide2." Same subject — an open book, a play triangle, three
   sparks — drawn heavier, with fewer and thicker text rules. `img/quick-start-guide.png`, the
   previous artwork, is superseded and no longer referenced by anything.

   AND IT IS THE FIRST ASSET HERE WHOSE SHAPE WAS NOT ITS ALPHA CHANNEL, WHICH THIS TECHNIQUE
   TAKES AS THE SHAPE. The supplied file was a 1536x1024, 687,795-byte render: the book's pages
   are OPAQUE WHITE and the drawing is dark slate ink on top of them, so the alpha channel is a
   solid book-shaped silhouette. Masking on it directly — the previous artwork's alpha was a line
   drawing with a transparent interior, so it needed nothing else — paints a featureless blob:
   no play triangle, no text rules, no spine, verified by rendering the alpha before shipping
   anything. THE MASK IS THEREFORE DERIVED FROM THE ARTWORK'S INK, not from its alpha:
   coverage = alpha x (page_luminance - Y) / (page_luminance - ink_luminance), clamped, with the
   two constants measured off the source itself (ink Y ~= 75 at its 5th percentile, page Y ~= 252)
   rather than assumed. Every antialiased edge — ink against page AND ink against transparency —
   keeps its gradient, and the white pages become "no paint" so the button's own ground shows
   through them, which is what makes one currentColor value correct in both themes. THE RULE THIS
   LEAVES: check what an incoming raster carries its detail IN before masking it, because an
   alpha-shaped icon and a colour-shaped one look identical in a picture viewer and only one of
   them survives this technique.

   CROPPED TO ITS OWN INK, NOT TO THE SUPPLIED CANVAS. The source had the book+sparks occupying
   the centre ~29%/41% of a much larger frame, most of it a soft glow at alpha <= 3.
   `mask-size:contain` fits the WHOLE image frame into the 30px box, empty margin included, so
   shipping that frame verbatim would render the artwork at roughly 9px inside a 30px box.
   This crop keeps the artwork's own ink bounding box plus a small deliberate margin, sized to
   land in the same 74-87% fill range `refresh`/`bell` occupy in their own boxes: 82x76 of ink on
   a 90x84 canvas renders 27.3 x 25.3 CSS px inside the 30x30 box — 91% of its width, 84% of its
   height, matching the previous artwork's 91%/81% almost exactly, so the swap does not change
   how heavy this control reads beside the two icons it now sits with. Non-square, because the
   content is not square (the sparks sit above a wider book) and padding it back out to a square
   would shrink the drawing for nothing. 3,607 bytes. Verified at the real 30px render size
   beside `refresh`/`bell`, both themes, magnified. */
.a-ico-mask.qg-book{
  -webkit-mask-image:url(../../img/quick-start-guide2.png);
  mask-image:url(../../img/quick-start-guide2.png);
}
.a-btn.sm{ height:26px; font-size:12px; padding:0 9px; }
/* ---------- AN ICON-ONLY SMALL BUTTON NOW HAS A GEOMETRY RATHER THAN A LEFTOVER ----------

   NOBODY EVER CHOSE 12px. `.a-btn.sm{padding:0 9px}` is declared immediately above
   `.a-btn.icon{padding:0}` in the source but wins on order at equal specificity, so an
   icon-only small button keeps 9px of side padding it has no word to hold: 32px wide, less
   2px of border, less 18px of padding, leaves a 12px content box — and altIcon() emits an
   <svg> with a viewBox and no width or height, which as a flex item with no basis collapses
   into whatever is left. The size was an arithmetic residue of a rule meant for a labelled
   control.

   MEASURED, SIGNED IN, AT 1440: 22 of the suite's 24 visible `.a-btn.icon.sm` drew a 12px
   glyph in a 32x26 box. Against the two that were right that is an 18.8% smaller hit target
   (832px^2 against 1024) and a stroke rendering 20% thinner (0.950px against 1.188) than the
   identical refresh icon a few inches away in the top bar. Hover, focus ring, colour, spacing,
   accessible name and keyboard behaviour already matched exactly; only the geometry did not.

   THE TWO THAT WERE RIGHT WERE RIGHT BY PAGE-LOCAL RULE, and both said so. `.a-strip-end
   .a-btn svg` in alt-notes.css and `#altControls .a-btn svg,#altQuickStrip .a-btn svg` in
   alt-tasks.css each sized the strip their own file owned, under a comment naming this file as
   the right home. This is that home. A control is not a capability of the application while it
   is correct on one page and wrong on the next four.

   SCOPED TO `.sm`, AND THAT IS A DECISION. `.a-btn.icon` without it is the top bar — Refresh,
   the bell, the theme switch — measured 32x32 with a 30x30 glyph on all five suite pages,
   agreeing with each other so the row reads as one row. Sizing those to 15px would be a
   redesign of the header rather than a defect fix, which is the same reason `:not(.icon)`
   exists on the svg rule above. */
.a-btn.icon.sm{ height:var(--a-ctl); }
.a-btn.icon.sm > svg{ width:15px; height:15px; flex:0 0 auto; }
.a-btn.on{ background:var(--a-blue-fill); border-color:transparent; color:#fff; }
/* A SWITCH THAT IS NORMALLY ON IS NOT AN ACTION. `.on` is the loud lit state — right for
   "Clear filters", which exists only when something is wrong. Details and Utilities are on
   almost always, and two solid blue buttons in the toolbar read as the two most important
   controls on the page rather than as the state of two panels. Same signal, quieter voice, and
   `aria-pressed` says it either way. */
.a-btn.tog.on{
  background:var(--a-blue-soft); border-color:var(--a-blue); color:var(--a-blue-text);
}
.a-btn.tog.on:hover{ background:var(--a-blue-soft); border-color:var(--a-blue); }

/* ================= THE FOLD-ALL TOGGLE — ONE TREATMENT, SIX PAGES =================

   THE CONTROL. JS/alt/alt-view-controls.js's altFoldAllHtml()/altFoldAllEl() build one
   expand-all/collapse-all button and six hosts draw it: tasks.html, notes.html, calendar.html,
   team_board.html, okrs.html and admin.html. It replaced a documented `Expand all` /
   `Collapse all` PAIR on 2026-09-23 — the component's own header carries the reasoning, and the
   short version is that this button names the ACTION rather than the state, so the objection the
   old two-button rule raised does not reach it.

   WHY THE RULE IS IN THIS FILE. It is the only stylesheet all six hosts load. okrs.html and
   admin.html are not suite pages but both carry `class="alt-app"` on <body> and both load
   alt-tokens.css, so `.a-btn` and every --a-* token resolve there exactly as they do on the
   other four: one class set serves both vocabularies and the `.btn.small` pairs those two pages
   used to draw are a legacy choice rather than a constraint. A page-local dressing would be six
   answers to one question, which is the drift `.a-focus-btn` below exists to prevent.

   NO NEW GEOMETRY, DELIBERATELY. `.a-btn.sm` sizes it, `.a-btn:not(.icon) > svg` sizes the glyph
   beside the word, and `.a-btn.icon.sm > svg` sizes it when `compact` drops the word — all three
   already in this file. A host that must match a particular neighbour passes `cls` and dresses
   that class in its own sheet; notes.html does exactly that to keep the 18px box it was measured
   against (CSS/alt/alt-notes.css).

   THE PRESSED PAINT IS THE SUITE'S ESTABLISHED SELECTED TRIPLE AND NOT A FOURTH BLUE — the same
   three values `.a-btn.tog.on` directly above, `.a-chip.on`, `.a-stat.on`, `.alt-app .at-tab.on`
   and notes' own earlier version of this control already carry. The owner's spec asks for "blue
   border, tint, icon and glow": the border is `--a-blue`, the tint `--a-blue-soft`, the icon
   `--a-blue-text`, and the word "glow" is answered by those three moving together as a whole
   lightness step. That is not an improvisation — it is the decision this file already recorded
   for the watch pair a few blocks down, where an outer shadow was refused because these buttons
   sit one control-gap apart and an outer ring would touch its neighbour. Measured on the tightest
   instance (notes' strip, 6px from `.a-list-h-more`) before reusing it here.

   KEYED ON THE ATTRIBUTE, NEVER A CLASS, and that is the whole reason this is not simply
   `.a-btn.tog.on`. `aria-pressed` is what a screen reader is told; keying the paint off anything
   else lets the visual state and the announced state drift apart, which is the same rule stated
   above `.a-focus-btn` below and the reason the component emits an identical class list in both
   states. */
.a-btn.a-fold-all[aria-pressed="true"]{
  background:var(--a-blue-soft); border-color:var(--a-blue); color:var(--a-blue-text);
}
/* Hovering something already lit acknowledges the pointer without reading as an unselect. */
.a-btn.a-fold-all[aria-pressed="true"]:hover{
  background:var(--a-blue-soft); border-color:var(--a-blue);
}

/* ================= THE FOCUSED-MODE CONTROL — ONE TREATMENT, THREE PAGES =================

   WHY IT IS HERE AND NOT IN A PAGE STYLESHEET, WHICH IS THE WHOLE POINT OF THE CLASS.
   Focused mode has a button on tasks.html, on team_board.html and on notes.html, and each of
   those pages owns a stylesheet that could dress it. Three dressings is three answers to one
   question, and the first time the accent moves two of them follow and one does not. So the
   look lives with the other shared primitives and the three pages consume it. The page sheets
   keep only what is genuinely theirs — where the control SITS in its row (`.a-focused-btn` in
   alt-tasks.css, `.at-focused-btn` in alt-team.css are `margin`/`flex` and nothing else).

   DESIGN SOURCE: `_docs/wire_frames/Focused/` — the owner-approved board plus three reference
   crops, one per state. Measured off those crops rather than eyeballed; the numbers that
   mattered are recorded against the declarations below.

   NOT TO BE CONFUSED WITH `.a-focus`, one file over in alt-shell.css. That is the SHELL's
   control — the header menu that narrows every page to one tag — and it shares nothing with
   this but six letters. Two near-identical class names is not ideal and it is deliberate: the
   name is the contract three pages already call this control by. If you are here to change the
   tag filter, you are in the wrong rule.

   THE ACTIVE STATE IS CARRIED BY `aria-pressed`, NOT BY `.on`, AND THAT IS THE CONTRACT.
   `.a-btn.on` above paints the same blue off a CLASS, which means the visible state and the
   state a screen reader is told can drift apart — and they have, elsewhere in this suite: see
   the "`.on` WAS MISSING HERE" note at alt-tasks.js:989, where a chip set `aria-pressed`
   correctly and nothing painted it. Keying the paint off the attribute makes that failure
   impossible to express. It is the pattern CSS/okr.css already uses for the OKR pin, so this is
   the established answer rather than a new one. A host that also leaves `.on` on the element
   is harmless — every rule below carries `.a-btn.a-focus-btn`, which outranks `.a-btn.on` — but
   it is redundant and should come off.

   THE HIERARCHY IS NEUTRAL -> INTERACTIVE -> SELECTED, and only the middle two are declared:
   the DEFAULT state is `.a-btn`'s own base, unchanged, because the reference's "neutral surface,
   restrained border, light text" IS `--a-raised` on `--a-line`. An inactive Focused button
   should look like every other button in its toolbar; that it needed no rule is the evidence
   that the shared primitive was already right.

   NOT ONE SIZE IS RESTATED, AND THAT IS THE OWNER'S INSTRUCTION RATHER THAN A SHORTCUT: keep
   the control the size it is now, a little bigger at most. So height, radius, padding, gap and
   the 15px glyph all stay on the tokens the row already agrees on. There is no geometry below at
   all: every rule in this block sets colour, and the control's box is `.a-btn`'s box.

   THE REFERENCE CROPS ARE DELIBERATELY NOT FOLLOWED ON PROPORTION, which is worth writing down
   because the numbers are seductive. Measured off them the icon-to-label gap is 0.22-0.25 of
   button height and the glyph box 0.57 — which lands on 6px/15px for the 26px `.sm` control two
   of the three hosts use (so `.a-btn`'s existing values are already right there), but asks for
   ~7.5px and ~18px on the 32px host (team_board.html uses `.a-btn` without `.sm`). It keeps
   6px/15px there too. A design board shows a button ALONE; this one lives in a dense toolbar,
   where matching the controls beside it is what "native to the top navigation" means — and
   `.a-btn:not(.icon) > svg` is a shared rule this must not move in any case.

   THEMES COME FREE, WHICH IS WHY EVERY VALUE BELOW IS A ROLE. `--a-blue-soft`, `--a-blue` and
   `--a-blue-fill` point at `--accent-soft`/`--accent`/`--accent-fill` in CSS/alt/alt-tokens.css,
   so light mode and a team's custom palette both arrive with nothing here to change. `#fff` is
   the one literal and it is the same literal `.a-btn.on` and `.a-btn.primary` use: `--accent-fill`
   is the token that exists specifically to hold white text (4.52:1 dark, 5.86:1 light, measured
   in CSS/tokens.css), so white on it is the measurement rather than a guess.
   ONE THING LIGHT MODE DOES NOT GET: the reference's "brighter blue edge" on the active state.
   In dark, `--accent` #3b82f6 sits a step brighter than `--accent-fill` #1e6ff5 and draws it; in
   light the two tokens are the same #2560c8, so the edge goes flat. That is correct rather than
   broken — on a light ground the fill is already the contrast — and inventing a lighter blue
   here would be the second colour system this file exists to avoid.

   FOCUS-VISIBLE IS UNTOUCHED, DELIBERATELY. `.a-btn:focus-visible` above draws the 2px
   `--a-ring` outline and nothing below sets `outline`, so a keyboard reader sees the ring in all
   three states — hover styling changes `background` and `border-color` only and cannot stand in
   for it. (CSS/alt/alt-focus.css, which loads last on every page, is the backstop.)

   WHAT CARRIES THE STATE WHEN THE LABEL DOES NOT. This control reads "Focused" in both states —
   see the next block for why — so unlike most toggles it has no word that changes, and the two
   states are told apart visually by the surface alone. That is not hue-only, which is the part
   that matters: `--a-raised` against `--a-blue-fill` is a large LIGHTNESS step (a relative
   luminance of 0.020 against 0.180 in dark) and the label flips to `#fff` with it, so the
   difference survives any colour-vision deficiency and greyscale. `aria-pressed` carries it for
   anything that cannot see the button at all. Nothing here asks a reader to distinguish two
   blues, which is the failure this note exists to rule out. */

/* ---------- THE LABEL IS A CONSTANT, SO THERE IS NOTHING HERE TO SIZE ----------

   THE CONTROL READS "Focused" IN BOTH STATES, and that is a naming decision rather than an
   oversight: the suite header already carries a control labelled **Focus** (`.a-focus-l` in
   alt-shell.css, rendered by alt-shell.js), which narrows every page to one tag and shares
   nothing with this. A second control resting on that same word puts two different "Focus"
   controls in one viewport, which is the discoverability defect core rule 5 exists to prevent.

   SO THE BUTTON HAS NO SIZING RULE OF ITS OWN, and that is the whole entry. A label that never
   changes never changes width, so the control is exactly `.a-btn` plus `.sm` where its host uses
   it: height, radius, padding, font and the 6px icon-to-label gap all come from the primitive
   every neighbour in the same toolbar already wears. Measured headless at 1440 on both hosts —
   26px beside a 26px `.a-btn.sm`, 32px beside a 32px `.a-btn`, glyph and label each 0.00px from
   the button's vertical centre.

   `.a-focus-btn-t` IS A HOOK, NOT A BOX. The three page files wrap the label in that span and
   this stylesheet deliberately gives it no rule: as a flex item of `.a-btn` it is already
   blockified, content-width and centred, so any declaration here would be restating the
   primitive. If you are about to add one, check first that `.a-btn` has not already answered it.

   ---------------------------------------------------------------------------------------------
   IF A TOGGLING LABEL EVER COMES BACK, THE RESERVATION TRICK HAS TWO TRAPS. Both cost a
   debugging cycle here and neither is discoverable from reading CSS.

   A label that changes between two words changes width, which moves every neighbour along the
   toolbar on each toggle. The fix is to render the longer word a second time at zero height and
   let the box size to the wider of the two — and:

   1. **`attr()` reads only the attribute of the element its own pseudo belongs to.** Put the
      reservation on the BUTTON while the word lives in an attribute on the LABEL SPAN and it
      resolves to nothing at all — no error, no warning, just a reservation that silently
      reserves zero. The pseudo has to sit on the element that carries the attribute.

   2. **A zero-height ITEM is not a zero-height TRACK.** `align-content` defaults to `normal`,
      which on a grid behaves as stretch, so free space is distributed across the auto tracks
      including the one holding the invisible item. Measured when this was built on the button:
      `grid-template-rows` computed to `22.5px 7.5px` on the 32px host, so the visible row
      centred its contents inside 22.5px instead of 30px and lifted the glyph and label 3.75px
      and 4.25px above the button's true middle — 13% of the control, in a row where every flex
      neighbour sat within 0.50px of centre. `align-content:center` is what stops it.

   Neither shows up in a stylesheet review; both were found by measuring a real browser, which is
   why that step is not optional for this control. */

/* HOVER — the interactive middle tier: a muted steel-blue surface on a brighter blue edge.
   Text stays `--a-text` from the base rule: the crop shows a light label, and the token is light
   in dark mode and dark in light mode, which is the same intent expressed once.
   `:not([aria-pressed="true"])` RATHER THAN PLAIN `:hover` so this rule and the active rule
   below are mutually exclusive and neither depends on the order they appear in.

   ---------------------------------------------------------------------------------------------
   IT MIXES ITS OWN SURFACE RATHER THAN WASHING OVER THE ROW, AND THAT IS A MEASURED FIX.

   `--a-blue-soft` is `rgba(59,130,246,.20)` — translucent. Setting it as a `background` REPLACES
   the button's opaque `--a-raised`, so the wash then composites over whatever the page happens to
   put behind the control. That makes the hover colour a property of the TOOLBAR rather than of
   the button, and the three hosts do not agree on their toolbar ground. Measured, hover against
   default:

     notes / tasks   #16283f -> #18325c   1.170
     TEAM BOARD      #16283f -> #142c51   1.071   <- the surface doing no work at all
     light           #dee4ee -> #cfdaed   1.103
     reference crops #1e262f -> #263a5a   1.336

   One shared class producing a visibly different hover on one of its three hosts is the exact
   drift this class exists to prevent, and 1.071 is not a design decision anybody made — it is
   where the maths landed.

   THE RATIO IS UNCHANGED AT 20%. Only what it composites over changed: `--a-raised` is the
   button's own surface, so the result no longer depends on the page behind it. Computed, and
   then the same in all three dark hosts:

     dark    #16283f -> #1d3a64   1.307      (crops 1.336)
     light   #dee4ee -> #b9cae6   1.299

   HOVER STILL DOES NOT APPROACH ACTIVE, which was the constraint: active leads it 2.52:1 in dark
   and 3.53:1 in light, and carries white text where hover keeps `--a-text`, so the two differ by
   more than a fill. The design board's own 1.628 was deliberately NOT matched — that board shows
   the control alone, and the crops are the closer reference; 20% is the canonical number and
   raising it to chase the board would push hover toward active for no reader's benefit.

   AND THE PLAIN DECLARATION AHEAD IS NOT DECORATION — the same rule CSS/alt/alt-tokens.css
   states for `--a-float`. An engine that does not know `color-mix()` discards the whole
   declaration at parse time, so without the line above it there would be no background at all.
   It falls back to exactly the wash this used to be, which is the correct thing to degrade to.

   THE WASH IS STILL THE SUITE'S ANSWER ELSEWHERE. `.a-btn.tog.on` and `.a-chip.on` keep
   `--a-blue-soft` and are untouched; this is not a second muted blue, it is the same one made
   independent of its host. Those two have the same latent composition behaviour, on surfaces
   where nobody has measured a problem — worth knowing, not worth changing here. */
.a-btn.a-focus-btn:not([aria-pressed="true"]):hover{
  background:var(--a-blue-soft);
  background:color-mix(in srgb, var(--a-blue) 20%, var(--a-raised));
  border-color:var(--a-blue);
}
/* ACTIVE — selected, and stronger than hover by a whole step: a filled surface rather than a
   wash. `.a-btn.on` paints the same fill with a transparent edge; the approved design keeps a
   blue edge on it, which is the only reason this is not simply that rule. */
.a-btn.a-focus-btn[aria-pressed="true"]{
  background:var(--a-blue-fill); border-color:var(--a-blue); color:#fff;
}
/* Hovering something already selected should acknowledge the pointer without unselecting it,
   so the fill brightens rather than stepping back down to the hover wash — `.a-btn.primary:hover`
   above is the same gesture, and `filter` is already in this file's micro-transition list. */
.a-btn.a-focus-btn[aria-pressed="true"]:hover{ filter:brightness(1.08); }

/* ================= WATCHING A TASK SAYS SO — THE LIT EYE AND ENVELOPE =================

   THE CONTROL. JS/components/task-watch-mail.js builds one eye-plus-envelope pair and three
   surfaces host it: team_board.html's task modal, the framed editor on
   tasks.html?task=…&embed=task, and the Suite's task drawer on tasks.html. Assigning a task now
   subscribes the actor automatically (the auto-watch section of JS/services/task-assign.js), and
   until this block the pair said nothing about whether that had happened — the eye's only
   difference between watching and not was a 2.7px pupil filling in.

   WHY IT IS IN THIS FILE AND NOT IN A HOST'S. These are the only two rules in the repository
   that reach all three hosts from one place: alt-components.css is an unconditional <link> on
   BOTH tasks.html (line 274) and team_board.html (line 225), while the sheets that dress the
   buttons' own classes are split — `.icon-btn`'s only base rule lives in CSS/task-editor-panel.css,
   which tasks.html loads only inside `<template id="altEmbedTaskCss">` and team_board.html never
   loads at all. Three dressings of one lit state is the drift `.a-focus-btn` above exists to
   prevent, and this control has already been caught by the same split once (the eye measured
   0x0 on team_board because no sheet there carried `.icon-btn`).

   NO NEW COLOUR, AND NO BESPOKE GLOW. This is the suite's established "this is the state you are
   in" wash — `--a-blue-soft` over a `--a-blue` edge with `--a-blue-text` ink — the same signal
   `.a-chip.on`, `.a-btn.tog.on`, `.a-stat.on` and `.alt-app .at-tab.on` already carry in this
   file, and `.okr-stat.active`, `.okr-qg-cell.on` and the report card's band buttons carry
   elsewhere. The request said "glow"; an outer shadow would be a second opinion about what
   selected looks like, in a header row where the buttons sit one control-gap apart and an outer
   ring would touch its neighbour. The word is answered by the wash, the edge and the ink moving
   together, which is a whole lightness step rather than a hue change.

   THE EDGE IS AN INSET SHADOW RATHER THAN A BORDER, AND THAT IS WHAT MAKES IT ONE EDGE ON THREE
   HOSTS. `html .icon-btn` is `border:none`, so `border-color` draws literally nothing on
   team_board and in the framed editor; `.a-btn.ghost` is `border-color:transparent`, so setting
   it there AND drawing a ring would give the Suite a 2px edge against the other two hosts' 1px.
   An inset ring is the one declaration that renders identically on a borderless button and a
   transparent-bordered one — and it is the idiom both neighbourhoods already use: `.a-stat.on`
   in the figure band below draws its own selected ring this way, and `html .icon-btn:hover`
   draws its hover boundary with `inset 0 0 0 1px` too.

   IT IS THE TOKEN AND NOT `.a-focus-btn:hover`'S COLOR-MIX, AND THAT WAS A MEASUREMENT RATHER
   THAN A PREFERENCE. The mix one block up is `color-mix(in srgb, var(--a-blue) 20%, var(--a-raised))`,
   and 20% is right for a HOVER whose label stays `--a-text`. `--a-blue-soft` is 20% in dark but
   only 12% in LIGHT (CSS/tokens.css), because the light-mode selected wash was tuned to sit
   under `--a-blue-text`. Reusing the hover mix here reproduced the hover surface in both
   themes — measured light #f1f4f9 -> #b9cae6 — and put `--a-blue-text` on it at 4.07:1, under
   the design system's 4.5:1 floor for TEXT. That matters on two of the three hosts, which draw
   the word "Watch"/"Email" beside the glyph at 12px/600. The token restores the pairing the
   suite already ships everywhere else: `.a-chip.on`, `.a-btn.tog.on`, `.alt-app .at-tab.on`
   and `.okr-qg-cell.on` all put `--a-blue-text` on `--a-blue-soft` and nothing else.

   THE TRANSLUCENCY THAT MADE `.a-focus-btn` MIX IS MEASURED HERE, NOT ASSUMED AWAY. That note
   records a wash going flat over one host's ground (1.071:1 on the team board). The same
   question was asked of this control on all three of its hosts, in both themes, headless at
   1440/768/390 — the numbers are in the delivery report, and the smallest step measured is the
   one recorded against the declaration below. Where that note's control had three toolbars with
   different grounds, this one's three headers are close enough that the token holds.

   THE SELECTOR IS AN ID AND AN ATTRIBUTE, DELIBERATELY. The ids are fixed on all three hosts
   (the component's header says why), and the state cannot be a CLASS: panelWatchSkin()
   reassigns `className` outright on every sync so a host change cannot inherit the previous
   host's skin, which would erase any class added here on the next repaint.

   AND THE ID IS WRITTEN TWICE, WHICH IS WEIGHT AND NOT A TYPO. MEASURED, NOT SUSPECTED: with a
   single id this rule is (1,1,0), which clears every hover rule any host states — the strongest
   is `:root.embed-task .panel-head .icon-btn:hover` at (0,5,0) — but LOSES on team_board.html to
   `.alt-app #taskModal button.icon-btn` (CSS/alt/alt-team.css:943), which is (1,3,1). That rule
   exists because team_board.html does not load CSS/task-editor-panel.css and so has to restate
   `.icon-btn`'s base for its modal; it sets `background` and `color` and not `box-shadow`, so
   the lit ring appeared on that host while the wash and the blue ink did not — measured in
   headless Chrome at 1440 in both themes, `background rgba(0,0,0,0)` and `color rgb(61,74,92)`
   against the other two hosts' wash and `--a-blue-text`. Exactly the "a capability on one page
   is not a capability the application has" failure, found by measuring rather than by reading.

   REPEATING THE ID IS THE SMALLEST HONEST ANSWER. It makes the rule (2,1,0), which outranks any
   single-id page rule while staying an ORDINARY cascade value a host could still deliberately
   beat — where `!important` would take the declaration out of the cascade altogether and leave
   no way back. It is the same move `html .icon-btn` makes in CSS/task-editor-panel.css, where
   the `html` prefix carries no meaning either and its own comment says so: a selector segment
   added for weight. Adding `#taskModal` instead would have made one shared rule host-specific,
   which is the drift this whole block exists to prevent.

   KEYED ON `aria-pressed`, WHICH IS THE CONTRACT `.a-focus-btn` STATES ABOVE: the painted state
   and the announced state are then the same fact, and cannot drift. The envelope is NOT a
   toggle — it opens a dialog — so it carries its state on `data-watch-mail` instead, and that
   attribute is a CONJUNCTION (watching AND account email on), never the account switch alone.
   panelWatchMailPaintBtn() argues the whole distinction.

   AND `pending` IS NOT `off`. Until the reader's teams have answered, the envelope knows
   neither, so it dims rather than painting either state — Definition of Done 4f. `.45` is what
   `.a-stat[disabled]` and `.a-stat.is-zero` already use for "present, not yet actionable"; `.55`
   here because this control is 32px rather than a figure tile and has to stay legible. */
#panelWatchBtn#panelWatchBtn[aria-pressed="true"],
#panelWatchMailBtn#panelWatchMailBtn[data-watch-mail="on"]{
  color:var(--a-blue-text);
  background:var(--a-blue-soft);
  box-shadow:inset 0 0 0 1px var(--a-blue);
}
/* Hovering something already lit acknowledges the pointer without unlighting it — the identical
   gesture `.a-focus-btn[aria-pressed="true"]:hover` and `.a-btn.primary:hover` make, and the
   reason `filter` is in this file's micro-transition list at all. It is needed HERE rather than
   merely nice: the rule above outranks every host's own `:hover`, so without this a lit button
   would give no response to the pointer at all. */
#panelWatchBtn#panelWatchBtn[aria-pressed="true"]:hover,
#panelWatchMailBtn#panelWatchMailBtn[data-watch-mail="on"]:hover{ filter:brightness(1.08); }
#panelWatchMailBtn#panelWatchMailBtn[data-watch-mail="pending"]{ opacity:.55; }

/* ---------- inputs ---------- */
.a-field{ display:flex; flex-direction:column; gap:5px; min-width:0; }
.a-field > span{ font-size:10px; font-weight:700; letter-spacing:.09em; text-transform:uppercase; color:var(--a-dim); }
.a-input,.a-select,.a-textarea{
  height:var(--a-ctl); min-width:0; padding:0 10px; border-radius:var(--a-r-sm);
  background:var(--a-bg); border:1px solid var(--a-line); color:var(--a-text);
  font-family:var(--a-body); font-size:13px;
}
.a-textarea{ height:auto; padding:8px 10px; line-height:1.5; resize:vertical; }
.a-input:focus,.a-select:focus,.a-textarea:focus{ outline:none; border-color:var(--a-blue); }
.a-input:focus-visible,.a-select:focus-visible{ outline:2px solid var(--a-ring); outline-offset:1px; }
/* `.sm`, REHOMED HERE ON 2026-09-12. This size modifier was written twice — once in
   CSS/alt/alt-team-kanban.css for the team board's card select, once in CSS/alt/alt-okrs.css
   for the OKR context strip, the second copy carrying a comment that named the FIRST copy's
   OLD filename (alt-team.css) as the reason it had to restate rather than share. A third page
   wanted it and had neither: JS/alt/alt-tasks.js and JS/alt/alt-notes.js build `class="a-av
   sm"` avatars, but tasks.html and notes.html load neither sheet, so those avatars rendered
   with no size modifier at all. Three pages want a shared primitive's modifier; one canonical
   copy, beside the primitive it modifies, is what alt-team-kanban.css's own comment already
   said should happen "the moment a second page wants them". */
.alt-app .a-input.sm,.alt-app .a-select.sm{ height:26px; font-size:12px; padding:0 7px; }
/* opacity:1 is load-bearing, and its absence here was the last place in the suite still
   showing a UA-dimmed placeholder. Firefox applies its OWN opacity to ::placeholder on top
   of whatever colour is set — roughly 54% — so a token measured to pass at --a-faint is
   rendered lighter than the number it was signed off at. help.css:276 and okr-suite.css:320
   both carry the pair; this rule, which is the one every suite page actually loads, did not.
   Design-system §18: never leave a placeholder unstyled, and never set it below --text-faint. */
.a-input::placeholder{ color:var(--a-faint); opacity:1; }
.a-search{ position:relative; display:flex; align-items:center; min-width:0; }
.a-search .a-input{ width:100%; padding-left:30px; }
/* CENTRED ON THE INPUT, NOT ON WHATEVER THE STATIC POSITION HAPPENS TO BE. `top`/`left` alone
   left the icon riding its containing block's TOP edge rather than its middle — invisible in
   the two search drawers (`JS/alt/alt-notes.js`), where the icon is a direct child of this
   flex row and an out-of-flow absolute child's static position is centred by
   `align-items:center` on its own — but wrong in the shell (`JS/alt/alt-shell.js`), where the
   scope-pill refactor nested the icon inside `.a-search-inner`, a block box whose top is not
   its middle. Measured before this rule: shell icon 10.5px above the input's own centre line;
   drawer icon already exact. `top:50%; transform:translateY(-50%)` centres the icon on
   whichever containing block it resolves against — `.a-search-inner` in the shell,
   `.a-search` itself in a drawer — both of which are exactly as tall as the input, so this is
   correct either way rather than a fix for one shape that happens to also work for the other.
   Same technique `.a-kbd` already uses on the opposite edge (CSS/alt/alt-shell.css). */
.a-search .a-search-ico{
  position:absolute; left:9px; top:50%; transform:translateY(-50%);
  color:var(--a-faint); display:flex; pointer-events:none;
}

/* ---------- chips and badges ---------- */
.a-chip{
  display:inline-flex; align-items:center; gap:5px; height:22px; padding:0 9px;
  border-radius:var(--a-r-pill); font-size:11px; font-weight:600; white-space:nowrap;
  background:var(--a-raised); border:1px solid var(--a-line-soft); color:var(--a-dim);
  cursor:default; max-width:170px; overflow:hidden;
}
/* ---------- THE CAP TRUNCATES ON `.lb`, BECAUSE IT CANNOT TRUNCATE ON THE CHIP ----------

   `text-overflow:ellipsis` USED TO SIT ON `.a-chip` ITSELF, ONE LINE UP, AND HAD NEVER ONCE
   FIRED. `text-overflow` applies to a BLOCK CONTAINER; `.a-chip` is `display:inline-flex`, so
   text written straight into it is wrapped in an ANONYMOUS FLEX ITEM whose own `text-overflow`
   is the initial `clip` and whose own `overflow` is `visible`. The declaration on the chip
   therefore styles a box that holds no text, the anonymous item spills, and the chip's
   `overflow:hidden` cuts it — mid-glyph, with no ellipsis. Reported from production by the
   product owner against build 8b9234e9: the Watching tab's reason chip read
   "Owned but opt'd to watch onl".

   MEASURED, NOT ARGUED FROM THE SPEC. Each chip was captured twice at 2x — as authored, and with
   `text-overflow:clip` forced onto the same element — and the two PNGs compared byte for byte. A
   declaration that does nothing renders identically to its absence, and all four over-cap labels
   hashed identically in both themes, at 1440/1280/1024/390, in all three densities. There is no
   selector for an anonymous flex item, so this cannot be fixed on the chip: the text has to be in
   a real child.

   `.lb` IS THAT CHILD, AND IT IS ALREADY THE SUITE'S NAME FOR IT — `.a-menu-item .lb`,
   `.a-group-btn .lb`, `.a-admin-nav .lb`. TWO PAGE STYLESHEETS HAD ALREADY RE-DERIVED THIS EXACT
   RULE for this exact component and neither knew about the other: `.alt-app .a-okr-glance
   .a-chip .lb` (CSS/alt/alt-okrs.css) and `.alt-app .at-part .a-chip .lb` (CSS/alt/alt-team.css),
   the same three declarations in a different order. Two copies of one answer to one question is
   what core rule 4 forbids and what §3252 of sys_design-system.md ("A CONTROL THAT EXISTS ON MORE
   THAN ONE PAGE IS STYLED IN ONE PLACE — THE SHARED COMPONENT STYLESHEET") names outright, so the
   rule is hoisted here and those two become redundant rather than load-bearing.

   `min-width:0` IS THE HALF THAT IS EASY TO LOSE. A flex item's automatic minimum size is its
   content, so `overflow:hidden; text-overflow:ellipsis` alone leaves `.lb` at full width and the
   chip clips it exactly as before — the fix looks applied and changes nothing. All three
   declarations or none.

   THIS RULE DOES NOT REACH BARE TEXT, AND NOTHING IN CSS CAN. A caller that writes a label
   straight onto a `.a-chip` is still un-ellipsable; the guard is
   `_docs/tests/chip-truncation.test.js`, which fails any chip label that can outgrow the cap and
   is not in a `.lb`.

   `.l` IS IN THE SELECTOR BECAUSE THE TWO GLYPH CHIPS ALREADY CALL THEIR LABEL THAT. The
   recurrence and reminder markers (`.a-chip-rep`, `.a-chip-rem`, JS/alt/alt-tasks.js) were built
   as glyph + `.a-sr` sentence + visible `.l`, and Compact hides `.l` by name
   (CSS/alt/alt-tasks.css) — renaming it to `.lb` would break that pair for no gain. Inside a chip
   `.l` is only ever those two labels; the name is not reused there. `.a-chip .n` is deliberately
   NOT in the list: a count is the one part of a chip that must survive the squeeze, and giving
   the LABEL the whole shrink allowance is what stops a long label pushing the figure out of the
   box — the failure CSS/alt/alt-org-people.css records for its category chips.

   AND THE SHRINK ITSELF IS THE LABEL'S, NOT THE CHIP'S. `min-width:0` here only lets the label
   fall below its own text; whether the CHIP may fall below its cap is a per-chip decision, and
   `.a-chip-watchreason` (CSS/alt/alt-tasks.css) is the one that takes it. */
.a-chip .lb,
.a-chip .l{ min-width:0; overflow:hidden; text-overflow:ellipsis; }
button.a-chip{ cursor:pointer; }
button.a-chip:hover{ border-color:var(--a-line-strong); color:var(--a-text); }
.a-chip .dot{ width:6px; height:6px; border-radius:50%; background:currentColor; flex:0 0 auto; }
.a-chip.on{ background:var(--a-blue-soft); border-color:var(--a-blue); color:var(--a-blue-text); }
.a-chip .n{ font-variant-numeric:tabular-nums; opacity:.75; }
/* THE CHIP IS PRESSABLE, SO IT HAS TO SHOW WHERE THE KEYBOARD IS. Every other interactive
   primitive in this file draws the 2px ring; the chips were relying on the browser default,
   which on a dark panel is nearly invisible. */
button.a-chip:focus-visible{ outline:2px solid var(--a-ring); outline-offset:1px; }

/* ---------- STATUS IS THE TAG CHIP WEARING A STATE'S COLOUR ----------

   THE DEFECT THIS CLOSES. A status filter chip and a tag filter chip sit in one strip an inch
   apart, and they were two unrelated primitives: the tag a bordered pill on a raised ground,
   the status a borderless tinted slab. Two answers to one question, drawn side by side — and
   every later change to one of them drifted a little further from the other.

   SO STATUS IS A MODIFIER OF `.a-chip`, NOT A SECOND CHIP. Geometry, radius, padding, type,
   height, the count treatment, hover, the `.on` selected state, the focus ring and the overflow
   clamp all arrive from the chip itself and therefore CANNOT diverge. The state class
   contributes only the two things that are about meaning: the outline and the text colour,
   carried on two custom properties so hover and `.on` can restate them without a rule each.

   THE `-text` VARIANT, NOT THE ROLE. `--a-blue` on a panel measures 4.05:1 and the design
   system's floor for text is 4.5:1, so blue text is `--a-blue-text`. The other four roles
   already point at their canonical `-text` colour and are used as they stand. To-do is the one
   status with no colour of its own: a neutral outline, dim text — quiet is what it means.

   AFTER `.a-chip.on` DELIBERATELY. A selected chip takes the blue wash and the blue text that say
   "this is the filter in force" and keeps its own outline — which is precisely what a selected
   tag does, because the colour a tag carries is painted on its border inline. */
.a-chip-st{ border-color:var(--a-st-line); color:var(--a-st-text); }
.a-chip-st.st-active  { --a-st-line:var(--a-blue-text);   --a-st-text:var(--a-blue-text); }
.a-chip-st.st-todo    { --a-st-line:var(--a-line-strong); --a-st-text:var(--a-dim); }
.a-chip-st.st-hold    { --a-st-line:var(--a-amber);       --a-st-text:var(--a-amber); }
.a-chip-st.st-blocked { --a-st-line:var(--a-red);         --a-st-text:var(--a-red); }
.a-chip-st.st-monitor { --a-st-line:var(--a-purple);      --a-st-text:var(--a-purple); }
.a-chip-st.st-complete{ --a-st-line:var(--a-green);       --a-st-text:var(--a-green); }
/* HOVER AND `.on` RESTATE THE OUTLINE ONLY, which is what makes this exactly a tag rather than
   nearly one. A tag's colour is painted on its border inline, so hovering or selecting it leaves
   the border alone and takes the chip's own text colour; status now behaves the same way. It is
   also the safer half: `--a-red` on the selected blue wash measures 3.8:1, under the floor,
   while `--a-blue-text` on it is the pairing the rest of this file already uses. */
button.a-chip-st:hover,.a-chip-st.on{ border-color:var(--a-st-line); }

/* THE STATIC STATUS BADGE, which is a different job from the filter chip above: it labels a row
   rather than offering to narrow the list, and a tinted fill is what lets the eye pick the
   status out of a line of tags without reading it. Task rows and the notes page's task list
   both draw it, so the two stay one component. */
/* `.a-st` IS GONE, MERGED INTO `.a-chip-st` ABOVE. It was the status badge on task and note
   rows — a tinted fill with no border — sitting beside outlined `.a-chip` tags in the same
   row, on every row. The filter strip had already been unified; leaving the row badge behind
   kept the very inconsistency this pass closed, on two hundred instances instead of five.
   Both render sites now emit `a-chip a-chip-st st-<kind>`. */

/* ---------- avatars ---------- */
.a-av{
  width:24px; height:24px; border-radius:50%; flex:0 0 auto;
  display:inline-flex; align-items:center; justify-content:center;
  font-size:10px; font-weight:700; letter-spacing:.02em;
  background:var(--a-blue-fill); color:#fff; overflow:hidden;
}
.a-av img{ width:100%; height:100%; object-fit:cover; }
.a-av.lg{ width:30px; height:30px; font-size:11px; }
/* `.a-av.sm`, REHOMED HERE ON 2026-09-12 alongside `.a-input.sm`/`.a-select.sm` above — same
   defect, same fix. team_board.html's kanban card and notes.html's collaborator row both draw
   an 18px avatar; tasks.html's row asks for one too (JS/alt/alt-tasks.js) and, loading neither
   of the two sheets that used to define it, got none. One shared modifier beside `.a-av.lg`,
   not a third copy. */
.alt-app .a-av.sm{ width:18px; height:18px; font-size:9px; }
/* The placeholder avatar on an unassigned row: present so the column still lines up, and
   silent because there is nobody to name. */
.alt-app .a-av.sm.none{ background:var(--a-line-soft); }

/* ---------- progress ---------- */
.a-bar{ height:6px; border-radius:3px; background:var(--a-line-soft); overflow:hidden; display:flex; }
.a-bar > i{ display:block; height:100%; background:var(--a-blue-fill);
  transition:width var(--dur-progress, 420ms) var(--a-ease); }
.a-seg{ height:8px; border-radius:4px; overflow:hidden; display:flex; background:var(--a-line-soft); }
.a-seg > span{ display:block; height:100%; transition:flex-grow var(--dur-progress, 420ms) var(--a-ease); }
/* THE SAME COLOUR THE STATUS CHIP USES, so the bar and the chips under it are one legend
   rather than two. Without these the segments were transparent and the whole distribution
   read as an empty grey rule. */
.a-seg > span.k-active{ background:var(--a-blue); }
.a-seg > span.k-todo{ background:var(--a-dim); }
.a-seg > span.k-blocked{ background:var(--a-red); }
.a-seg > span.k-hold{ background:var(--a-amber); }
.a-seg > span.k-monitor{ background:var(--a-purple); }
.a-seg > span.k-complete{ background:var(--a-green); }
.a-ring{ position:relative; width:34px; height:34px; flex:0 0 auto; }
.a-ring svg{ transform:rotate(-90deg); }
.a-ring .a-ring-t{
  position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
  font-size:10px; font-weight:700; font-variant-numeric:tabular-nums; color:var(--a-text);
}

/* ---------- menus and popovers ---------- */
.a-menu{
  position:absolute; z-index:var(--z-dialog, 300); min-width:230px; max-width:min(360px, 92vw);
  padding:6px;
  background:var(--a-float); border:1px solid var(--a-line-strong); border-radius:var(--a-r);
  box-shadow:var(--a-shadow); display:flex; flex-direction:column; gap:2px;
}
/* A MENU IS FOR READING, so it is set at the body size rather than the chip size — 13px in a
   floating panel with a dark ground was reported as too small to see, and the Focus menu, whose
   entries are the reader's own tag names, was the worst of them. 14px, more room per row, and a
   hit state that is a background AND a ring rather than a wash alone. */
.a-menu-item{
  display:flex; align-items:center; gap:10px; width:100%; padding:9px 11px;
  border-radius:var(--a-r-sm); min-height:34px;
  background:none; border:none; color:var(--a-text); font:inherit; font-size:14px;
  line-height:1.25; text-align:left; cursor:pointer; text-decoration:none;
}
.a-menu-item:hover{ background:var(--a-raised); }
.a-menu-item:focus-visible{
  background:var(--a-blue-soft); outline:2px solid var(--a-ring); outline-offset:-2px;
}
.a-menu-item .k{ margin-left:auto; font-family:var(--a-mono); font-size:11px; color:var(--a-faint); }
/* FIGURES IN A MENU ROW, IN COLUMNS THAT LINE UP. `.k` is one cell and cannot do this: two
   figures of different widths in one span wander as the numbers change, and a column nobody
   can scan is the reason for having a column at all. Fixed minimums plus tabular numerals
   are what hold the decimal points over each other; `text-align:right` is what makes "3/5"
   and "12/14" end on the same pixel. Only `.first` takes the auto margin, so the name keeps
   the space it needs and the whole group still sits hard right. */
.a-menu-item .km{
  flex:0 0 auto; font-family:var(--a-mono); font-size:11px; color:var(--a-faint);
  font-variant-numeric:tabular-nums; text-align:right;
}
.a-menu-item .km.first{ margin-left:auto; min-width:36px; }
.a-menu-item .km:not(.first){ min-width:40px; }
/* MERGED FROM CSS/alt/alt-shell.css 2026-09-11 — same collision as `.a-toolbar` above:
   `.a-menu-head` and `.a-menu-sep` were each declared once here and once in alt-shell.css,
   both bare single-class rules of equal specificity, and alt-shell.css links after this file
   on every page that mounts the shell — so alt-shell.css's copy was the one actually
   rendering (padding:6px 10px 3px, font:600 10px/1 var(--a-display), margin:5px 6px on the
   separator), and this file's copy (padding:6px 9px 2px, margin:4px 2px on the separator) was
   dead weight nobody saw. The winning declarations move here; the alt-shell.css copies are
   deleted. Verified via computed-style probe (altMenuOpen() opened directly) on
   tasks.html/notes.html/team_board.html/calendar.html/okrs.html, light and dark: identical
   before and after. */
.a-menu-head{
  padding:6px 10px 3px; font:600 10px/1 var(--a-display); letter-spacing:.09em;
  text-transform:uppercase; color:var(--a-dim);
}
.a-menu-sep{ height:1px; margin:5px 6px; background:var(--a-line); }

/* ---------- the three states every surface owes the reader ---------- */
.a-empty{
  display:flex; flex-direction:column; align-items:center; justify-content:center; gap:10px;
  padding:44px 20px; text-align:center;
}
.a-empty-h{ font-size:14px; font-weight:600; color:var(--a-dim); }
.a-empty-p{ font-size:12px; line-height:1.6; color:var(--a-faint); max-width:46ch; margin:0; }
.a-load{ display:flex; align-items:center; justify-content:center; gap:10px; padding:34px 20px;
  font-size:12px; color:var(--a-faint); }
.a-err{
  display:flex; align-items:flex-start; gap:10px; padding:12px 14px; border-radius:var(--a-r);
  background:var(--a-red-soft); border:1px solid var(--a-red); color:var(--a-text); font-size:12px; line-height:1.5;
}
.a-err .a-btn{ margin-left:auto; }
/* A widget that fails must not take the page with it — see alt-shell.js altWidgetFail(). */
.a-widget-err{ font-size:11px; color:var(--a-faint); padding:10px 12px; line-height:1.5; }

/* ---------- the standing callout ----------
   A STATEMENT THE READER HAS TO SEE, WHICH IS NOT A PROBLEM. The settings panes already had two
   ways to say something and neither fits: `.a-hint` is grey body text that a reader scanning for
   controls skips, and `.a-err-note` is the amber chip alt-shell.css draws for a fault — and the
   email pane draws that one TWICE already, for the sending ceiling and for an unreadable
   profile. A third amber block that is merely explanatory would flatten the one distinction
   those two rely on, and a permanent warning colour in a settings pane is how people learn to
   read past real warnings.

   SO IT IS THE ACCENT ROLE, WHICH IS THE PRODUCT'S "READ THIS" AND NOT ITS "SOMETHING IS
   WRONG" — the same blue the selected note card, the chosen note group and the active tab wear.

   COLOUR IS NOT THE SIGNAL (sys_design-system.md §18). The tint and the rule down the left edge
   are reinforcement; what carries the meaning is the `<b>` lead word every caller writes, which
   a reader who cannot see either still gets. The glyph beside it is decorative and altIcon()
   already ships it `aria-hidden`, so it is not a second unnamed announcement of the same thing.

   PLAIN DECLARATIONS FIRST, BOTH OF THEM — `.a-err-note`'s rule, for its reason: an engine
   without color-mix() discards the whole declaration, and the block would otherwise keep its
   text on the bare panel with no tint and no edge and stop reading as a block at all. The
   `--a-*-soft` token is the same hue at a fixed alpha, which is what the mix approximates.

   EVERY VALUE IS A TOKEN, so light, dark and a reader's custom palette are all free: the accent
   is redefined upstream and nothing here has to know. */
.a-callout{
  display:flex; align-items:flex-start; gap:var(--a-2);
  padding:10px var(--a-3); margin:0 0 var(--a-3);
  border:1px solid var(--a-blue);
  border:1px solid color-mix(in srgb, var(--a-blue) 42%, transparent);
  border-left-width:3px;
  border-radius:var(--a-r);
  background:var(--a-blue-soft);
  background:color-mix(in srgb, var(--a-blue) 10%, transparent);
  font-size:12px; line-height:1.55; color:var(--a-text);
}
/* The glyph keeps its own column and does not shrink when the sentence wraps — at the narrow
   widths the settings drawer reaches, a flex icon collapses to a sliver before the text does. */
.a-callout > .ic{
  flex:0 0 auto; display:flex; width:15px; height:15px; margin-top:1px; color:var(--a-blue-text);
}
.a-callout > .ic svg{ width:15px; height:15px; }
.a-callout > .bd{ min-width:0; }
/* THE LEAD WORD IS THE NON-COLOUR SIGNAL, so it is a block of its own rather than a bold run
   inside the sentence — it has to survive being the only thing a reader gets. */
.a-callout b{ display:block; color:var(--a-blue-text); font-weight:600; margin-bottom:2px; }
.a-callout p{ margin:0; }
.a-callout p + p{ margin-top:4px; }
.a-callout strong{ font-weight:600; color:var(--a-text); }

/* ---------- the horizontal grip ----------
   THE VERTICAL ONE IS `.a-grip` IN alt-shell.css, and this is the same control turned ninety
   degrees: a boundary between two rows rather than two columns. It is keyed on the ARIA
   ORIENTATION rather than on a second class name, because altSplitter() already writes
   `aria-orientation="horizontal"` for `axis:'y'` — so a grip is styled by what it IS, and there
   is no way to build one that behaves horizontally and is drawn vertically.

   THE SAME THREE PROPERTIES ARE LOAD-BEARING as on the vertical grip, for the same reasons
   recorded there: the line is drawn AT REST (a control nobody can see is a control nobody has),
   the hit target is bigger than the line, and the cursor and the colour both change before the
   drag starts. `touch-action:none` is what stops a touch drag scrolling the panel instead. */
.alt-app .a-grip[aria-orientation="horizontal"],
.alt-app .a-w-grip[aria-orientation="horizontal"]{
  flex:0 0 auto; width:auto; align-self:auto; height:8px; cursor:ns-resize;
  position:relative; background:transparent; border:0; padding:0; touch-action:none;
}
.alt-app .a-grip[aria-orientation="horizontal"]::before,
.alt-app .a-w-grip[aria-orientation="horizontal"]::before{
  content:''; position:absolute; inset:3px 10px; border-radius:2px;
  background:var(--a-line-strong);
  transition:background var(--a-quick) var(--a-ease), inset var(--a-quick) var(--a-ease);
}
/* The three-dot affordance the vertical grip carries, laid along the other axis, so a grip
   reads as "drag me" rather than as a border somebody left between two panels. */
.alt-app .a-grip[aria-orientation="horizontal"]::after,
.alt-app .a-w-grip[aria-orientation="horizontal"]::after{
  content:''; position:absolute; top:50%; left:50%; height:2px; width:22px;
  transform:translate(-50%,-50%); border-radius:2px;
  background:repeating-linear-gradient(90deg, var(--a-faint) 0 2px, transparent 2px 5px);
}
.alt-app .a-grip[aria-orientation="horizontal"]:hover::before,
.alt-app .a-grip[aria-orientation="horizontal"]:focus-visible::before,
.alt-app .a-grip[aria-orientation="horizontal"]:active::before,
.alt-app .a-w-grip[aria-orientation="horizontal"]:hover::before,
.alt-app .a-w-grip[aria-orientation="horizontal"]:focus-visible::before,
.alt-app .a-w-grip[aria-orientation="horizontal"]:active::before{
  background:var(--a-blue); inset:3px 4px;
}
.alt-app .a-grip[aria-orientation="horizontal"]:focus-visible,
.alt-app .a-w-grip[aria-orientation="horizontal"]:focus-visible{
  outline:2px solid var(--a-ring); outline-offset:-2px;
}

/* ---------- toolbars and section furniture ---------- */
/* MERGED FROM CSS/alt/alt-shell.css 2026-09-11 — CANONICALLY ONE DECLARATION, WHERE IT ALREADY
   BELONGED. `.a-toolbar` collided across this file and alt-shell.css since at least
   2026-09-04: both declared it as a bare single-class rule, both specificity (0,1,0), and
   alt-shell.css always links after this file on every page that mounts the shell — so the
   effective rule was a HYBRID, `display`/`align-items`/`gap`/`flex-wrap` agreeing in both
   (no visible difference either way) but `min-width:0` surviving only because alt-shell.css
   never redeclared it, and `flex:0 0 auto` plus `padding:var(--a-2) 0` winning from
   alt-shell.css only because this file never declared them. alt-admin.css and alt-team.css
   both already document `.a-toolbar` as a component with `padding:var(--a-2) 0` built in
   (see alt-team.css's `.at-ctx-btns` comment), which is this file's job, not the shell's — so
   the complete rule moves here and the alt-shell.css copy is deleted, not the other way round.
   Verified identical via computed-style probe on tasks.html/notes.html/team_board.html before
   and after, light and dark: nothing changed. */
.a-toolbar{
  display:flex; align-items:center; gap:var(--a-2); flex-wrap:wrap; flex:0 0 auto;
  min-width:0; padding:var(--a-2) 0;
}
.a-toolbar > *{ align-self:center; }
.a-spacer{ flex:1 1 auto; min-width:0; }
.a-rule{ width:1px; align-self:stretch; background:var(--a-line-soft); margin:0 2px; }
.a-card{ background:var(--a-panel); border:1px solid var(--a-line-soft); border-radius:var(--a-r-lg); }
.a-sec-h{
  display:flex; align-items:center; gap:8px; font-size:10px; font-weight:700;
  letter-spacing:.1em; text-transform:uppercase; color:var(--a-dim);
}
/* ---------- the panel band header: icon square + title, one right-aligned action cluster ----------

   A CARD HEADER SHAPE SHARED BY MORE THAN ONE PAGE, STATED ONCE. Proven first on
   team_board.html's "Team at a glance" (`.at-panel-ico` / `.at-band-h` in CSS/alt/alt-team.css):
   a small tinted icon square beside the title, then every trailing control gathered flush
   against the right edge, ending in a bordered Hide/Show pill whose chevron SWAPS glyph rather
   than rotating — see that file's own comment on why a swap reads right there and a rotation
   would not. tasks.html's "Work at a glance" adopts the SAME shape via these two classes
   (`altRenderAnalytics()` in JS/alt/alt-tasks.js) rather than inventing a second header layout,
   which is what CLAUDE.md's one-canonical-implementation rule asks for.

   NOT YET FULLY COLLAPSED WITH alt-team.css'S OWN COPY. alt-team.css was owned by a different
   lane while this change was made and could not be repointed at these classes here — so
   `.at-band-h` / `.at-panel-ico` remain a second, page-owned declaration of this same shape
   until whoever next holds that file migrates its markup onto `.a-panel-h` / `.a-panel-ico` and
   deletes the duplicate. Co-apply `.a-panel-h` alongside `.a-sec-h` (not instead of it) — it
   supplies the uppercase label type, this only says how the row's children divide.

   NO `margin-bottom` HERE, DELIBERATELY — team_board.html's `.at-band-h` carries one because
   its card is plain block flow with no other spacing between the header and the body under it.
   tasks.html's `.a-analytics` is `display:flex; flex-direction:column; gap:var(--a-2)` (CSS/alt/
   alt-tasks.css), so a margin-bottom here would stack ON TOP of that gap rather than replace it
   — measured: the card grew 2px TALLER after this redesign, the opposite of the ask, until this
   was removed. One spacing mechanism belongs to the host, not two disagreeing about the same
   8px. A future host that adopts `.a-panel-h` without its own gap between header and body states
   that spacing itself, the way `.a-analytics` does. */
.a-panel-h{ gap:var(--a-3); flex-wrap:wrap; }
.a-panel-ico{
  flex:0 0 auto; display:inline-flex; align-items:center; justify-content:center;
  width:22px; height:22px; border-radius:var(--a-r-sm);
  background:var(--a-blue-soft); color:var(--a-blue-text);
}
.a-panel-ico svg{ width:14px; height:14px; display:block; }
/* THE TRAILING CONTROLS ARE ONE WRAPPER, NOT A CHAIN OF SIBLING MARGINS. Team's own header
   pushes its controls right by giving the first non-title child `margin-left:auto` and
   resetting whatever comes after it back to 0 — exact and simple when at most one such control
   ever appears. A host with a control that a narrower breakpoint hides via `display:none`
   (rather than removing from the DOM) cannot rely on that trick: a structural sibling selector
   cannot distinguish a hidden control from a visible one, so whichever child is first in markup
   would claim the auto margin whether or not anything is drawn there. Pushing the WRAPPER right
   instead means the cluster's own rendered width decides the gap, regardless of which of its
   children happen to be hidden. */
.a-panel-h > .a-panel-actions{
  margin-left:auto; display:flex; align-items:center; gap:var(--a-2); flex-wrap:wrap;
}
/* THE DISCLOSURE CHEVRON IS SIZED BY WHAT IT IS, NOT BY WHERE IT SITS (widened 2026-09-14).
   This was `.a-panel-actions .a-btn > .cv`, scoped to the header cluster because that is where
   the first three of these controls happened to live (#altAnalyticsToggle, #altGlanceToggle,
   #altPeopleToggle). Moving tasks.html's own Hide/Show into the filter card put a fourth one
   outside that cluster, and the chevron came out 0x0 — the file's own "an inline SVG with no
   size is not an icon" rule, met again by a control that had simply moved house.

   SAFE TO WIDEN, CHECKED RATHER THAN ASSUMED: every `.cv` in this repository is either a direct
   child of a `.a-btn` — and every one of those is this exact ghost disclosure — or it hangs off
   `.a-w-fold`, `.a-group-h`, `.a-disc-h` or `.a-okr-*-h`, none of which carry `.a-btn`, so this
   selector cannot reach the rules that size those at 13px or rotate them. */
.a-btn > .cv{
  display:inline-flex; align-items:center; width:14px; height:14px; flex:0 0 auto;
}
.a-btn > .cv svg{ width:14px; height:14px; display:block; }
.a-mono{ font-family:var(--a-mono); font-variant-numeric:tabular-nums; }
.a-sr{ position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden;
  clip:rect(0 0 0 0); white-space:nowrap; border:0; }

/* ---------- THE UNDO TOAST'S BUTTON — A CLASSIC `.btn` THE SUITE HAD NO ANSWER FOR ----------

   THE DEFECT. JS/components/undo-toast.js:51 emits `class="btn small undo-toast-btn"`, and
   `.btn` geometry has never existed in CSS/ at all — it is declared in the inline <style> of
   each classic page and nowhere else. CSS/components.css owns the toast card (`:2240`) and gives
   the button one declaration, `flex:0 0 auto`, on the reasoning that `.btn` would arrive from
   the page. On the four suite pages nothing arrives, so the Undo button rendered as the user
   agent's own: `rgb(239,239,239)` ground, a 2px black inset border, square corners and
   13.33px/400 platform type, sitting on a themed panel in both light and dark.

   It is the only control in the toast and the whole reason the toast exists — an offer to
   reverse something, available for five seconds — so it being the one element on screen that
   does not look like the application is the worst place for this to land.

   THE HOST IS THE SCOPE, AND THAT IS DELIBERATE. `.btn` is a CLASSIC class, and this file is
   loaded by every suite page, so a blanket `.alt-app .btn` here would restyle hosted markup this
   file does not own. Two rules in CSS/components.css prove the cost rather than merely suggest
   it: `.btn.has-unread` (:1196) and `.btn[aria-current="page"]` (:1288) both set background,
   border-colour and colour at specificity (0,2,0), and components.css loads BEFORE this file —
   so a base `.alt-app .btn` would tie with them and win on source order, flattening the unread
   glow and the you-are-here chip. CSS/alt/alt-shared-dialogs.css:115 reached the same conclusion
   for the OKR editors and wrote it down: name the hosts, never `.alt-app`.

   THE VOCABULARY IS `.a-btn`, NOT A NEW LOOK. `.btn small` is the classic pages' small
   secondary button, so it is translated into this experience's small secondary button — the
   `.a-btn` declarations above with the `.sm` sizes — rather than being given a treatment of its
   own. Same height, radius, weight, ground and hover, so the toast reads as part of the suite
   and cannot drift from the buttons beside it. 26px clears the 24px pointer floor.

   THE ONE DELIBERATE DIFFERENCE FROM `.a-btn` IS THE BORDER, AND IT IS MEASURED. `.a-btn` uses
   `--a-line`, which is `--border` at 1.91:1 — fine for a button in a toolbar full of buttons
   whose grouping and hover already say it is a control. This one is the ONLY control on a
   floating card that exists for five seconds, so its edge is the whole affordance, and §18 sets
   3:1 for anything that identifies a control. Measured on the live toast, edge against the
   card: 1.27:1 for the user agent's black edge on the dark card, 2.11:1 dark / 2.22:1 light
   with `--a-line`, and 3.33:1 dark / 3.48:1 light with `--a-line-strong`. Only the last one
   clears the floor. It is also what the suite's two OTHER translations of
   classic `.btn` already chose — alt-shared-dialogs.css:154 uses `--a-line-strong` and
   alt-team.css:810 uses `--border-strong` — so the strong edge is the established answer for
   `.btn` specifically, and only `.a-btn` sits on the softer line.

   THE FOCUS RING COMES FROM CSS/alt/alt-focus.css. It is not restated here: that file's bare
   `:focus-visible` is the floor for exactly this case — a control with no ring of its own — and
   a second declaration would be a second answer to maintain. */
.alt-app .undo-toast .btn{
  display:inline-flex; align-items:center; justify-content:center; gap:6px;
  height:26px; padding:0 9px; border-radius:var(--a-r-sm);
  font-family:var(--a-body); font-size:12px; font-weight:600; line-height:1;
  background:var(--a-raised); color:var(--a-text); border:1px solid var(--a-line-strong);
  cursor:pointer; white-space:nowrap; text-decoration:none;
}
/* `.small` is the size the component asks for and `.a-btn.sm` is what that size is here, so the
   modifier is named even though the base above already carries its values — a `.btn` without
   `.small` in this host would otherwise silently inherit the small geometry. */
.alt-app .undo-toast .btn.small{ height:26px; padding:0 9px; font-size:12px; }
/* The blue edge is what `.btn:hover` already means in this suite
   (alt-shared-dialogs.css:161). With a strong border at rest, a strong border on hover
   would say nothing at all. */
.alt-app .undo-toast .btn:hover{ border-color:var(--a-blue); color:var(--a-blue-text); }

/* ---------- the figure tile ----------

   LIFTED OUT OF alt-tasks.css, because it is a component and not a page: Tasks, Notes and
   Calendar all drew it, the Admin console's Overview draws it too, and its own note already
   said the 62px height is a number three pages agree on. A page-local copy is how three
   answers to one question got there in the first place. `.a-glance .a-stats` stays in
   alt-tasks.css — that one really is the Tasks strip's. */
/* THREE ACROSS, TWO DOWN. Six figures in one flex row overflowed a 320px column and the last
   three were clipped off the side — a reader with any overdue work could not see the count of
   it. A grid wraps by design rather than by accident. */
.a-stats{
  display:grid; grid-template-columns:repeat(3, minmax(0,1fr)); gap:var(--a-3); flex:0 0 auto;
  min-width:0;
}
/* ONE TILE HEIGHT, ON ALL THREE PAGES. The same component measured 76px on Tasks, 62 and 76
   mixed in a single grid on Notes and 62 on Calendar — visibly ragged where two sat side by side,
   and three different answers to a question with one answer. 62 is the number, because it is what
   the calendar strip and the wireframe's compact strip already draw; it is also 14px of the
   window per row handed back to the task list, which needed it. */
.a-stat{
  display:flex; flex-direction:column; justify-content:center; gap:3px; min-width:0;
  /* 8px, not 9: letting the numeral's line box hold its own glyphs (see `.a-stat b`) gave the
     pair 2px more height, and 62 is a number three pages agree on. The padding stays symmetric —
     an asymmetric one would be the alignment bug wearing a disguise. */
  min-height:62px; padding:8px 12px;
  border-radius:var(--a-r); background:var(--a-raised); border:1px solid var(--a-line-soft);
  cursor:pointer; text-align:left; font:inherit; color:inherit;
}
.a-stat:hover{ border-color:var(--a-line-strong); }
/* THE LIT TILE. Its wash lifts the ground, so the label has to come up with it — `--a-faint` on
   the blue soft measures 3.9:1, under the 4.5 the design system requires for text, and the label
   is the half that says what the number counts. */
.a-stat.on{
  border-color:var(--a-blue); background:var(--a-blue-soft);
  box-shadow:0 0 0 1px var(--a-blue) inset;
}
.a-stat.on b{ color:var(--a-blue-text); }
.a-stat.on span{ color:var(--a-dim); }
.a-stat[disabled]{ opacity:.45; cursor:default; }
/* A ZERO IS QUIET WHETHER OR NOT IT IS STILL A DESTINATION. "0 Overdue" has nowhere to go and is
   disabled; "0 No date" is still somewhere you can look. They were therefore drawn differently —
   one dimmed, one at full strength beside it — which made a strip of figures read as though the
   difference meant something about the data rather than about the handler behind it. Same
   condition, same weight. */
.a-stat.is-zero{ opacity:.45; }
.a-stat.is-zero:hover{ opacity:.7; }
/* THE FIGURE'S LINE BOX MUST HOLD ITS OWN GLYPHS. `line-height:1` gave a 21px Space Grotesk
   numeral a 21px box for a 27px glyph band, so the ink hung 3px above the box and 2px below it —
   asymmetrically, because a font's ascent overflow is not its descent overflow. `justify-content`
   then centred the BOXES perfectly and the text still read 3px high: measured topGap 9 against
   botGap 12 on a 62px tile, against a ceiling of 2. Letting the font size its own box makes ink
   and box the same rectangle, and centring places it honestly. Do not shrink this below `normal`
   in a compact control. The tile is still 62px: the 2px the honest line box costs came off the
   padding, symmetrically, rather than out of the height three pages agree on. */
.a-stat b{ font-family:var(--a-display); font-size:21px; line-height:normal; font-variant-numeric:tabular-nums; }
.a-stat span{ font-size:11px; color:var(--a-faint); overflow-wrap:anywhere; }
.a-stat.warn b{ color:var(--a-red); }
.a-stat.good b{ color:var(--a-green); }

/* ---------- view tabs ----------

   LIFTED OUT OF alt-tasks.css for the same reason as the figure tile above it: a tab strip
   is a component, not a page. Nothing in these selectors names an element, alt-okrs.css
   already documents them as shared primitives it extends, and the admin console's team
   detail draws one. A page-local copy is how one component becomes four. */
.a-tabs{ display:flex; align-items:center; gap:2px; flex-wrap:wrap; padding:0 var(--a-1);
  border-bottom:1px solid var(--a-line); }
.a-tab{
  height:38px; padding:0 14px; background:none; border:none; border-bottom:2px solid transparent;
  color:var(--a-faint); font:inherit; font-size:13px; font-weight:600; cursor:pointer;
  display:inline-flex; align-items:center; gap:7px;
}
.a-tab:hover{ color:var(--a-dim); }
.a-tab[aria-selected="true"]{ color:var(--a-text); border-bottom-color:var(--a-blue); }
.a-tab .n{ font-size:11px; color:var(--a-faint); font-variant-numeric:tabular-nums; }
.a-tabs-end{ margin-left:auto; display:flex; align-items:center; gap:var(--a-2); }

/* ---------- the boxed segmented tab strip ----------

   LIFTED OUT OF CSS/alt/alt-team.css, 2026-09-05, for exactly the reason the two components
   above it were lifted out of alt-tasks.css: a tab strip is a component, not a page. It was
   team_board.html's View strip and nothing else's — then okrs.html's context bar was asked
   for "the same design and styling", copied the markup, and shipped UNSTYLED, because okrs.html
   does not load alt-team.css and must not: that file is 1600 lines of one page's layout.

   MOVED, NOT COPIED. A second declaration of a control in a second sheet is how one component
   becomes two that drift apart in one direction at a time, and nobody sees it until the two
   pages are put side by side. The selectors are byte-identical to the ones alt-team.css
   declared — `.alt-app .at-tabs`, `.alt-app .at-tab` — so no specificity and no cascade order
   changed for the page that already had them.

   THIS IS THE BOXED SIBLING OF `.a-tabs` ABOVE, NOT A REPLACEMENT FOR IT. `.a-tabs` is the
   underlined strip that HEADS a panel and names its sections; this is the compact bordered
   TRACK that sits inside a toolbar row and chooses which state the surface is in. Two jobs,
   two components, one file — and the second one is why `.a-tabs` was not simply reused.

   TABS WITH LIVE COUNTS, in one bordered track so they read as one control choosing between
   states rather than as buttons that happen to be adjacent. The lit tab uses the shared blue
   wash — the same signal `.a-btn.tog.on` and `.a-chip.on` use — so "this is the state you are
   in" means one thing across the suite. */
.alt-app .at-tabs{
  display:inline-flex; align-items:center; gap:2px; padding:2px;
  height:var(--a-ctl); border:1px solid var(--a-line); border-radius:var(--a-r-sm);
  background:var(--a-bg); min-width:0;
}
.alt-app .at-tab{
  display:inline-flex; align-items:center; gap:6px; height:100%; padding:0 10px;
  border:1px solid transparent; border-radius:calc(var(--a-r-sm) - 1px);
  background:transparent; color:var(--a-dim); cursor:pointer; white-space:nowrap;
  font-family:var(--a-body); font-size:13px; font-weight:600;
  /* `normal`, NOT 1. A 13px Inter glyph band is taller than a 13px line box, so `line-height:1`
     leaves the ink hanging asymmetrically inside a flex-centred row and the label measures
     high. Letting the font size its own box makes ink and box the same rectangle, and
     `align-items:center` then places it honestly — the lesson `.a-stat b` records. */
  line-height:normal;
}
.alt-app .at-tab:hover{ color:var(--a-text); background:var(--a-raised); }
.alt-app .at-tab:focus-visible{ outline:2px solid var(--a-ring); outline-offset:-1px; }
.alt-app .at-tab.on{
  background:var(--a-blue-soft); border-color:var(--a-blue); color:var(--a-blue-text);
}
/* THE COUNT IS SUBORDINATE TO THE NAME. Same size would make "Board 41" read as two words. */
.alt-app .at-tab .n{ font-size:11px; opacity:.72; font-variant-numeric:tabular-nums; }
.alt-app .at-tab.on .n{ opacity:.9; }

/* THE TRACK GIVES UP BEING CONTENT-SIZED BEFORE IT GIVES UP ANYTHING ELSE. Below 1024 the row
   it sits in has already wrapped, so the track takes the line it has been given and the
   segments share it equally rather than huddling at one end. */
@media (max-width:1023px){
  .alt-app .at-tabs{ width:100%; justify-content:space-between; }
  .alt-app .at-tab{ flex:1 1 0; justify-content:center; }
}
/* AND ON A PHONE THE TRACK SCROLLS INSIDE ITSELF RATHER THAN WIDENING THE PAGE. Measured on
   the team board: four tabs and four counts need 316px and a 320px phone has 296, and the strip
   was the one thing on that page that made the PAGE scroll sideways. A contained scroll is the
   honest answer, because the alternative is truncating "Completed" to "Comp…" or dropping a
   view from the strip entirely. */
@media (max-width:819px){
  .alt-app .at-tabs{ overflow-x:auto; overscroll-behavior-x:contain; justify-content:flex-start; }
  .alt-app .at-tab{ flex:0 0 auto; padding:0 6px; font-size:12px; }
}

/* ================= MOTION: THE MICRO RESPONSE =================

   THIS IS THE BLOCK THAT MATTERS MOST, AND IT IS LAST BECAUSE IT IS ABOUT EVERYTHING ABOVE.
   `.a-btn`, `.a-chip`, `.a-input`, `.a-stat`, `.a-tab` and `.a-menu-item` are the primitives
   every surface in this suite is built out of — the toolbar, the filter strip, the figure
   band, the tab row, every menu and every dialog. All six changed state between two frames,
   which meant that on all five pages the MAJORITY of what a reader touches gave no response at
   all, while the classic application eased the identical control.

   THE PRIMITIVE IS `.micro` IN CSS/components.css, AND IT IS ALREADY ON EVERY PAGE HERE — but
   it is a CLASS, and nothing in JS/alt/ emits it on the markup it builds. So the selectors are
   this file's and the TIMING is the application's, which is the same division the progress
   bars above already make with `--dur-progress`. Nothing here invents a duration.
   `classic_tasks.html:1842` — `transition:border-color var(--dur-micro) linear` — is the classic twin
   of the line below, declaration for declaration.

   `--dur-micro`, 140ms, `linear`. Past about 150ms a hover stops reading as feedback and starts
   reading as lag, which is why this is the one duration the 2026-08-29 retune deliberately left
   alone (sys_design-system.md §2b). `--a-quick` is for travel — the grip above keeps it, and so
   do the chevrons and drop targets in the page stylesheets.

   `filter` IS IN THE LIST BECAUSE `.a-btn.primary:hover` IS A `brightness()`. It is a
   compositor property and it only ever runs on one button at a time — the modifier that changes
   it exists on a single control per surface. It is NOT here as a general licence: nothing may
   ease a filter or a shadow across a LIST.

   WHAT IS DELIBERATELY ABSENT. `box-shadow` — `.a-stat.on` draws its ring with an inset shadow
   and the figure band is six tiles wide, so easing it would be exactly the paint this file
   cannot afford. `transform` — nothing in this vocabulary moves on hover, and a transition on a
   property no rule sets reads as intent and does nothing. Every layout property, always.

   IT COSTS NOTHING AT FIRST PAINT. A transition does not fire on initial render; it exists only
   from the moment a value changes. No keyframe, no `will-change`, nothing added to boot.

   REDUCED MOTION IS ANSWERED AT THE TOKEN, WHICH IS WHY THERE IS NO @media BLOCK HERE.
   CSS/alt/alt-tokens.css zeroes `--dur-micro` on `.alt-app` under
   `prefers-reduced-motion: reduce`; `.alt-app` is the <body> of every page that loads this
   file, so all of it inherits 0s — including the next primitive somebody adds. Measured with
   Chrome rather than assumed: every selector below reports `0s` with the media feature
   emulated. `0s` strands nothing, because none of this is a keyframe and nothing in JS/alt/
   waits on `transitionend`. */
.a-btn,
.a-input,
.a-select,
.a-textarea,
.a-chip,
.a-chip-st,
.a-menu-item,
.a-stat,
.a-tab,
.alt-app .at-tab,
.alt-app .undo-toast .btn{
  transition:background-color var(--dur-micro, 140ms) linear,
             border-color     var(--dur-micro, 140ms) linear,
             color            var(--dur-micro, 140ms) linear,
             opacity          var(--dur-micro, 140ms) linear,
             filter           var(--dur-micro, 140ms) linear;
}
