/* ================= THE ALTERNATE UX — THE SHELL =================
   One shell, five pages. The top bar, global search, Create, Today, Focus, alerts and the
   account menu are identical on tasks.html, notes.html, calendar.html, team_board.html and
   okrs.html — markup built once by alt-shell.js, styled once here.

   The first row (brand, nav, identity) gives way in a fixed order as it narrows: captions go
   first, then nav labels fold to icons. The search field is not part of that order — it takes
   its own permanent second row at every width (see "the search always takes the second row"
   below) so it never competes with the nav or identity for room. No nav destination is ever
   clipped: enforced by `.a-nav{flex:0 0 auto}` plus alt-responsive.css's label breakpoints. */

/* The shell IS the viewport; each column scrolls inside it, not the page — with only
   min-height, a long note's own checklist and conversation could push 1800px down the page,
   rendered but invisible. Below the three-column breakpoint the regions stack and the page
   scrolls normally, which is correct on a phone. */
.alt-app{ min-height:100vh; display:flex; flex-direction:column; }
@media (min-width:1024px){
  .alt-app{ height:100vh; height:100dvh; overflow:hidden; }
}

/* ---------- top bar ---------- */
/* ---------- THE SEARCH ALWAYS TAKES THE SECOND ROW ----------
   PRODUCT OWNER: "Always keep the universal search in the second row. That is visually better
   all the way around and avoids buttons from colliding or wrapping." This is now the base,
   unconditional rule rather than a conditional override living in alt-responsive.css —
   css-class-collision-check.py is what caught the old arrangement as a second, silently
   overriding definition of `.a-top`. */
.a-top{
  display:flex; align-items:center; gap:var(--a-2); height:auto; flex:0 0 auto;
  padding:8px var(--a-3); flex-wrap:wrap; background:var(--a-panel); border-bottom:1px solid var(--a-line);
  position:sticky; top:0; z-index:var(--z-sticky, 20);
}
.a-brand{ display:flex; align-items:center; gap:9px; flex:0 0 auto; text-decoration:none; color:var(--a-text); }
.a-brand svg{ width:26px; height:26px; }
/* A team's own logo swaps in via altPaintBrand(), which replaces only this span's contents. The
   span is a FIXED size so the corner does not shift 4px mid-boot when the answer arrives late. */
.a-brand-mark{ display:flex; align-items:center; justify-content:center; flex:0 0 auto;
  width:30px; height:28px; }
/* 30x28 with object-fit:contain so no logo is distorted or cropped — a wordmark and a crest both
   letterbox inside the same box. Ground is a token so a transparent logo sits on the bar's own
   surface in both themes. */
.a-brand-logo{ display:block; width:30px; height:28px; object-fit:contain;
  background:var(--a-raised); border-radius:var(--a-r-sm); }
/* The wordmark is brand, not a preference — `--app-brand-font` is pinned to Space Grotesk and
   does not follow the reader's chosen application font, so "STRIDE" reads the same under all
   ten choices. */
.a-brand b{ font-family:var(--app-brand-font, 'Space Grotesk', system-ui, sans-serif); font-size:17px; font-weight:700; letter-spacing:.04em; }

/* The product mark is an <img>, not inline SVG — see altProductMark() (alt-shell.js). Sized here
   and in the tag's own attributes, so the box is reserved before the file arrives. */
.a-brand-s{ display:block; width:26px; height:26px; }

/* ---------- the team, beside the product ----------
   A separate, quieter slot — the product reads first, the team is context, set smaller and
   dimmer behind a hairline so the two are never mistaken for one lockup. A <span>, not a link:
   clicking a team's logo must not navigate. Carries its own margin/border rather than a flex
   gap, since the chip is `hidden` until a team resolves and a gap would hold space open. */
/* No `.a-team[hidden]` rule here — CSS/base.css's `[hidden]{display:none!important}` already
   outranks this `display:flex`, and a local copy is the duplication
   responsive-layout.test.js checks every stylesheet for. */
.a-team{ display:flex; align-items:center; gap:8px; flex:0 0 auto;
  margin-left:10px; padding-left:11px; border-left:1px solid var(--a-line);
  color:var(--a-dim); min-width:0; }
/* Same 30x28 box as the logo, so switching teams never moves the bar; collapses when empty so a
   name-only team doesn't leave a mysterious gap. */
.a-team-mark{ display:flex; align-items:center; justify-content:center; flex:0 0 auto;
  width:30px; height:28px; }
.a-team-mark:empty{ display:none; }
.a-team-name{ font-family:var(--a-display, inherit); font-size:13px; font-weight:600;
  letter-spacing:.01em; max-width:170px; overflow:hidden; text-overflow:ellipsis;
  white-space:nowrap; }
/* Navigation is never the thing that gets cut. The nav takes the width it needs
   (`flex:0 0 auto`) and the search yields instead — the reverse (nav shrinks + overflow:hidden,
   search holds a hard min-width) silently amputated destinations like Messages on ordinary
   laptop widths, with no visible affordance that anything was missing.
   `overflow:visible` so if anything ever does overflow it is seen and fixed rather than hidden;
   the deliberate horizontal-scroll affordance below 820px sets its own overflow regardless. */
.a-nav{ display:flex; align-items:center; gap:2px; flex:0 0 auto; min-width:0; overflow:visible; }
.a-nav a,.a-nav button{
  display:inline-flex; align-items:center; gap:7px; height:36px; padding:0 11px;
  border-radius:var(--a-r-sm); background:none; border:1px solid transparent;
  color:var(--a-dim); font:inherit; font-size:13px; font-weight:600; text-decoration:none; cursor:pointer;
  position:relative; white-space:nowrap;
}
.a-nav a:hover,.a-nav button:hover{ color:var(--a-text); background:var(--a-raised); }
.a-nav a:focus-visible,.a-nav button:focus-visible{ outline:2px solid var(--a-ring); outline-offset:1px; }
/* Brand, Today and Focus used to fall through to Chrome's own default outline — a thin blue line
   on a navy header, invisible in practice. Same ring, same offset as everything else here. */
.a-brand:focus-visible,
.a-today:focus-visible,
.a-focus:focus-visible{ outline:2px solid var(--a-ring); outline-offset:2px; border-radius:8px; }
/* Active is blue and underlined — colour alone is never the signal (§18), aria-current carries
   it for anyone who cannot see either. */
.a-nav [aria-current="page"]{ color:var(--a-blue-text); background:var(--a-blue-soft); }
.a-nav [aria-current="page"]::after{
  content:''; position:absolute; left:11px; right:11px; bottom:-1px; height:2px;
  background:var(--a-blue); border-radius:2px 2px 0 0;
}
.a-nav svg{ width:15px; height:15px; flex:0 0 auto; }
/* ---------- row 2: search, then the three utility icons ----------
   PRODUCT OWNER: "Change the universal navigation button layout to be two rows." Row 1 keeps
   Create/Today/Focus/the AI pill/theme/account; row 2 holds the search field and the three
   icon-only utilities (refresh, messages, quick guide), right-aligned.

   `flex:1 1 100%` here is what forces this box onto its own full-width row at any width — the
   same mechanism the search field itself carries one level down (see `.a-top-search`), so
   putting the icons in this row could not be done by resizing the search; the container takes
   the 100% basis instead and `.a-top-utils{flex:0 0 auto}` pins the icons to the end, with the
   search absorbing the remaining width.

   `order:10` matters because later-mounted controls (e.g. quick-guide-launcher.js) can append
   into `#altTop` without an order of their own and must land on row 1, not push this row up.
   `min-width:0` lets this box's first child shrink below its min-content width, the same
   horizontal-scroll defect class alt-responsive.css's phone block removes elsewhere. */
.a-top-row2{ order:10; flex:1 1 100%; max-width:none; min-width:0;
  display:flex; align-items:center; gap:var(--a-2); }
/* `order:11` keeps reading/visual/focus order in sync with the markup. `flex:0 0 auto` is what
   makes the row work: these are fixed controls with nothing to give up, so the search — the
   elastic member — yields instead of the icons silently clipping.
   No `margin-left:auto`: `.a-top-search{flex-grow:1}` already pushes this cluster to the edge,
   and an auto margin on top would be a second mechanism for the same job that breaks once the
   search grows past the space available. */
.a-top-utils{ order:11; display:flex; align-items:center; gap:var(--a-2); flex:0 0 auto; }
/* ---------- the three utility icons match the account control's height ----------
   PRODUCT OWNER: "size the icons consistent with the size of the BH control height above it.
   Apply to the universal nav bar so all pages load the same size of the 3 buttons on the right
   in the second row."
   Reads `--a-ctl-lg` (the account control's own token) rather than a literal pixel value, so the
   two can never drift apart across densities — topbar-two-rows.test.js pins that relationship,
   not a number. Scoped to this cluster only: `.a-btn.icon` is a shared primitive used across
   ~30 controls elsewhere that were not part of this request. */
.a-top-utils .a-btn.icon{ width:var(--a-ctl-lg); height:var(--a-ctl-lg); }
/* ---------- and the glyph inside matches `.a-av`'s 24px, not the button's own 30px ----------
   The owner's screenshot (BH avatar above these three icons) is what settled the size: these
   glyphs were a 30px Chromium sizing artefact (alt-components.css explains the Safari bug it
   fixes), double every other 15px glyph on the bar. 24px matches `.a-av` exactly. The number is
   a literal because this app has no icon-size token yet (alt-tokens.css would own one);
   topbar-two-rows.test.js cross-checks both numbers and fails if they diverge, offered as the
   requirement for that token. Both `<svg>` and CSS-mask icons are sized together, since one of
   the three controls draws its mark as a mask, not an svg. */
.a-top-utils .a-btn.icon > svg,
.a-top-utils .a-btn.icon > .a-ico-mask{ width:24px; height:24px; }
/* ---------- the search field itself ----------
   `flex:1 1 100%` here means "take the whole of this row (not the whole bar — the parent
   `.a-top-row2` now carries that guarantee), then shrink by whatever the utility icons need."
   `gap:6px` is the one property this rule always owned: the space between the scope pill and
   `.a-search-inner` inside the box. */
.a-top-search{ order:10; flex:1 1 100%; max-width:none; gap:6px; }
/* ---------- one search, two scopes ----------
   The wrapper is still `.a-top-search.a-search` (search-field-chrome.test.js pins that class
   string). Inside it: a scope pill (`flex:0 0 auto`, sized to its own label) ahead of
   `.a-search-inner`, which is the flexible one and the `position:relative` anchor the magnifier,
   keycap and results list resolve against.

   PRODUCT OWNER: "reduce the text box to the size of the red box" — the field should not span
   the full second row.

   The cap goes on `.a-search-inner`, not on `.a-top-search` itself, and this is load-bearing:
   per the flexbox spec an item's flex-basis is clamped by its OWN min/max-width before the
   browser decides which line it lands on, so a max-width directly on `.a-top-search` would
   shrink its hypothetical size below "the whole row" and risk it fitting back onto row 1 beside
   the nav if ever moved there by mistake. Capping the inner box instead changes nothing about
   which line the outer box occupies, only how much of that line the visible field fills — the
   remainder renders as empty header background.

   The cap itself is measured and published at runtime by altSearchAlign() (alt-shell.js) as
   `--a-search-cap`, tracking the right edge of the Messages nav item at every width (a length
   that cannot be expressed in CSS since the nav sits on the row above). 730px is the var()'s
   fallback for a host where that function never ran — comfortably above the ~265px floor needed
   to hold the owner's stated "5 average words" without scrolling, confirmed against two
   independent owner screenshots (`_docs/tests/search-second-row.test.js` holds every required
   width). Below the cap nothing changes: `flex:1 1 auto; min-width:0` still lets the field
   shrink to whatever the row actually has as the bar narrows. */
/* Declared here, not only consumed: altSearchAlign()'s inline `--a-search-cap` outranks this at
   runtime, but a CSS home for the token is what alt-experience.test.js's "no alt token is used
   without being defined" rule requires — a token defined only in JS reads as a typo otherwise. */
.a-search-inner{ --a-search-cap:730px;
                 flex:1 1 auto; min-width:0; max-width:var(--a-search-cap, 730px); }
/* Same shape as `.a-theme-seg` on purpose, renamed rather than shared because this pill sizes to
   a WORD ("Global", or the page name) where the theme pill sizes to a fixed icon. */
.a-search-scope{
  display:flex; align-items:center; gap:2px; flex:0 0 auto;
  padding:2px; border-radius:var(--a-r-pill);
  background:var(--a-panel); border:1px solid var(--a-line);
}
.a-search-scope-b{
  display:inline-flex; align-items:center; justify-content:center;
  height:24px; padding:0 9px; flex:0 0 auto; white-space:nowrap;
  border:1px solid transparent; border-radius:var(--a-r-pill);
  background:transparent; color:var(--a-faint); font:inherit; font-size:11px; font-weight:600;
  cursor:pointer;
  transition:background var(--a-quick) var(--a-ease), color var(--a-quick) var(--a-ease),
             border-color var(--a-quick) var(--a-ease);
}
.a-search-scope-b:hover{ color:var(--a-text); }
/* Same raised-chip treatment as the theme pill's pressed segment, so which scope is active
   never depends on hue alone. */
.a-search-scope-b[aria-pressed="true"]{
  background:var(--a-float); color:var(--a-blue-text);
  border-color:var(--a-line-strong); box-shadow:var(--a-shadow-sm);
}
.a-search-scope-b:focus-visible{ outline:2px solid var(--a-ring); outline-offset:1px; }
/* Room for the keycap on the right and the magnifier on the left, or the placeholder runs
   underneath both. */
.a-top-search .a-input{ height:var(--a-ctl-lg); background:var(--a-bg); padding-right:38px; }
/* The keycap prints the reader's own modifier — `Ctrl+K` on Windows/Linux, three times the width
   of `⌘K`. altShellMount() adds `kbd-wide` when the label is longer than two characters. */
.a-top-search.kbd-wide .a-input{ padding-right:54px; }
.a-kbd{
  position:absolute; right:9px; top:50%; transform:translateY(-50%);
  font-family:var(--a-mono); font-size:10px; color:var(--a-dim); pointer-events:none;
}
/* Below 1179, the keycap hint and scope toggle drop — a standalone narrow/touch simplification
   (§12b's give-way order for secondary controls), independent of the search row's own width
   now that the field never shares a row with anything else. Costs the ability to switch scope
   below this width; the selected scope keeps working. */
@media (max-width:1179px){
  .a-kbd{ display:none; }
  .a-top-search .a-input,
  .a-top-search.kbd-wide .a-input{ padding-right:var(--a-3); }
  .a-search-scope{ display:none; }
}
.a-top-right{ display:flex; align-items:center; gap:var(--a-2); flex:0 0 auto; margin-left:auto; }
/* Today/Focus hold text that can be longer than the control (a tag name, an account name), so
   each elides inside its own box — `min-width:max-content` plus a ceiling, so the readout is
   never clipped mid-word the way a bare `min-width:0; overflow:hidden` once let it be. */
.a-focus-l,.a-today-l{ min-width:max-content; }
.a-focus-l span,.a-today-l span{
  max-width:150px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
/* The button is not the flex child that shrinks — each sits in a positioned wrapper. */
.a-today,.a-focus{ flex:0 0 auto; }
.a-top-right > div{ flex:0 0 auto; min-width:max-content; }

/* Today and Focus: two compact readouts, not two more buttons. */
.a-today{
  display:flex; align-items:center; gap:9px; height:var(--a-ctl-lg); padding:0 10px;
  border-radius:var(--a-r-sm); background:var(--a-raised); border:1px solid var(--a-line);
  cursor:pointer; color:inherit; font:inherit;
}
.a-today:hover{ border-color:var(--a-line-strong); }
.a-today-l{ display:flex; flex-direction:column; align-items:flex-start; line-height:1.15; }
.a-today-l b{ font-size:11px; font-weight:700; letter-spacing:.06em; text-transform:uppercase; color:var(--a-faint); }
.a-today-l span{ font-size:12px; color:var(--a-text); }
.a-focus{
  display:flex; align-items:center; gap:8px; height:var(--a-ctl-lg); padding:0 10px;
  border-radius:var(--a-r-sm); background:var(--a-raised); border:1px solid var(--a-line);
  cursor:pointer; color:inherit; font:inherit; max-width:210px;
}
.a-focus:hover{ border-color:var(--a-line-strong); }
.a-focus.on{ border-color:var(--a-blue); background:var(--a-blue-soft); }
/* This stack is Focus's only identification — no glyph, unlike every sibling control. The
   caption and the value must never both be hidden at once (shell-identity.test.js resolves the
   cascade at a sweep of widths and fails if neither is visible). */
.a-focus-l{ display:flex; flex-direction:column; align-items:flex-start; line-height:1.15; }
.a-focus-l b{ font-size:11px; font-weight:700; letter-spacing:.06em; text-transform:uppercase; color:var(--a-faint); }
.a-focus-l span{ font-size:12px; color:var(--a-text); }

/* ---------- the AI Assistant status badge: a third quiet readout, same tier as Today/Focus ----------
   See altPaintAi()/altAiPopoverToggle() in alt-shell.js for the rationale — presentation only,
   one canonical accessor (JS/services/ai-status.js).
   PRODUCT OWNER (from a screenshot): a rounded, bordered capsule carrying a mark, words and a
   state dot. `--a-r-pill`, not `--a-r-sm` like its neighbours, is the one deliberate difference
   — it is the one control in this cluster carrying a live status. */
.a-ai{
  display:flex; align-items:center; gap:8px; height:var(--a-ctl-lg); padding:0 10px;
  border-radius:var(--a-r-pill); background:var(--a-raised); border:1px solid var(--a-line);
  cursor:pointer; color:inherit; font:inherit; flex:0 0 auto;
}
.a-ai:hover{ border-color:var(--a-line-strong); }
.a-ai:focus-visible{ outline:2px solid var(--a-ring); outline-offset:2px; }
/* A CSS mask, not an <img> or a new altIcon() entry — the same currentColor-safe technique
   `.a-ico-mask` uses, applied to an inline vector so no asset file is needed. Painted
   `--a-faint`, never a state colour: the dot alone carries state (§18), so the mark doesn't
   compete with it. */
.a-ai-mark{
  width:14px; height:14px; flex:0 0 auto; background-color:var(--a-faint);
  -webkit-mask-repeat:no-repeat; mask-repeat:no-repeat;
  -webkit-mask-position:center; mask-position:center;
  -webkit-mask-size:contain; mask-size:contain;
  -webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2C12.8 8 16 11.2 22 12C16 12.8 12.8 16 12 22C11.2 16 8 12.8 2 12C8 11.2 11.2 8 12 2Z'/%3E%3C/svg%3E");
  mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2C12.8 8 16 11.2 22 12C16 12.8 12.8 16 12 22C11.2 16 8 12.8 2 12C8 11.2 11.2 8 12 2Z'/%3E%3C/svg%3E");
}
/* The dot is a second carrier of state, never the only one (§18) — green/amber/faint reuse this
   app's existing done/warning/neutral semantics rather than new colours for one control. */
.a-ai-dot{ width:8px; height:8px; border-radius:50%; flex:0 0 auto; background:var(--a-faint); }
.a-ai.a-ai-on .a-ai-dot{ background:var(--green); }
.a-ai.a-ai-warn .a-ai-dot{ background:var(--a-amber); }
.a-ai.a-ai-off .a-ai-dot{ background:var(--a-faint); }
/* The popover stays closed until the Tasks-by-email card is known, rather than opening small and
   growing (or opening early and missing the card) — see altAiPopoverToggle(). The breathing dot
   acknowledges the press at once without dimming or disabling the control, since the press is
   never refused, only delayed. */
.a-ai[aria-busy="true"] .a-ai-dot{ animation:a-ai-wait 900ms ease-in-out infinite; }
@keyframes a-ai-wait{ 0%,100%{ opacity:.3; } 50%{ opacity:1; } }
@media (prefers-reduced-motion: reduce){
  .a-ai[aria-busy="true"] .a-ai-dot{ animation:none; opacity:.55; }
}
.a-ai-l{ display:flex; flex-direction:column; align-items:flex-start; line-height:1.15; }
.a-ai-l b{ font-size:11px; font-weight:700; letter-spacing:.06em; text-transform:uppercase; color:var(--a-faint); }
.a-ai-l span{ font-size:12px; color:var(--a-text); }
/* Folds at the same two breakpoints `.a-today-l`/`.a-focus-l` already use (alt-responsive.css),
   not a third schedule for a control in the same cluster. Below 640 the mark folds away too,
   rather than surviving alone beside the dot — once the word naming the state is gone, a lone
   glyph beside a bare dot would read as a second, unexplained indicator. The full state stays on
   the control's title/aria-label, kept current by altPaintAi(). */
@media (max-width:1699px){
  .a-ai-l b{ display:none; }
}
@media (max-width:639px){
  .a-ai-l span{ display:none; }
  .a-ai-mark{ display:none; }
  .a-ai{ padding:0 8px; }
}

.a-bell{ position:relative; }
.a-bell .a-badge{
  position:absolute; top:-3px; right:-3px; min-width:16px; height:16px; padding:0 4px;
  border-radius:var(--a-r-pill); background:var(--red); color:#fff;
  font-size:10px; font-weight:700; display:flex; align-items:center; justify-content:center;
}
/* Restated from CSS/components.css's `[data-mc-open].has-unread` because this shell's bell
   carries no `data-mc-open` (initMessageCenter() takes an early return at mount on most suite
   pages, per altBellPaint()'s own note) — so the bell was otherwise the one Messages control
   with no lit state at all. Steady, never animated: emphasis the eye finds once. */
.a-bell.has-unread{
  background:var(--a-blue-soft); border-color:var(--a-blue); color:var(--a-blue-text);
  box-shadow:0 0 0 1px var(--a-blue), 0 0 12px -3px var(--a-blue);
}
.a-bell.has-unread:hover{
  background:var(--a-blue-fill); border-color:transparent; color:#fff;
}
/* On the lit button the red pip becomes the accent's own solid fill — still a count, no longer
   a second alarm — the same trade CSS/components.css makes for its own badge. */
.a-bell.has-unread .a-badge{ background:var(--a-blue-fill); }

/* ---------- the theme control: a segmented slider ----------
   Reproduces login.html's `.lg-seg` shape in this experience's own `--a-*` tokens (login.html
   loads no application stylesheet, so nothing can be shared directly). A segmented control
   rather than the icon button it replaced — §14a: three states behind one button means
   pressing it twice to find the one you want.

   The pressed state is a raised chip (own surface, border, shadow) plus `aria-pressed`, never
   colour alone (§18); the border is `--a-line-strong`, the 3:1 control edge.

   Named `.a-theme-seg`, not `.a-seg` — `.a-seg` already means the segmented distribution bar
   (alt-components.css, six status colours, extended by the team board); the two collided at
   equal specificity when this control was first built under that name, each silently losing
   properties to source order on different pages. The newcomer was renamed rather than winning
   by specificity, which would leave the collision easier to miss next time. */
.a-theme-seg{
  display:inline-flex; align-items:center; gap:2px; flex:0 0 auto;
  padding:3px; border-radius:var(--a-r-pill);
  background:var(--a-panel); border:1px solid var(--a-line);
}
.a-theme-seg-b{
  display:inline-flex; align-items:center; justify-content:center;
  width:34px; height:26px; padding:0; flex:0 0 auto;
  border:1px solid transparent; border-radius:var(--a-r-pill);
  background:transparent; color:var(--a-faint); cursor:pointer;
  transition:background var(--a-quick) var(--a-ease), color var(--a-quick) var(--a-ease),
             border-color var(--a-quick) var(--a-ease);
}
.a-theme-seg-b > svg{ width:15px; height:15px; flex:0 0 auto; }
.a-theme-seg-b:hover{ color:var(--a-text); }
.a-theme-seg-b[aria-pressed="true"]{
  background:var(--a-float); color:var(--a-blue-text);
  border-color:var(--a-line-strong); box-shadow:var(--a-shadow-sm);
}
/* The ring goes on the segment, not the pill — a group is not focusable, each segment is. */
.a-theme-seg-b:focus-visible{ outline:2px solid var(--a-ring); outline-offset:1px; }
@media (max-width:1365px){
  .a-theme-seg-b{ width:30px; }
}
/* No label inside — identity is the button's own accessible name (§16, §83), so there is no
   `.nm` to size here. */
.a-account{ display:flex; align-items:center; gap:8px; height:var(--a-ctl-lg); padding:0 8px 0 4px;
  border-radius:var(--a-r-pill); background:var(--a-raised); border:1px solid var(--a-line);
  cursor:pointer; color:inherit; font:inherit; font-size:13px; font-weight:600; }
.a-account:hover{ border-color:var(--a-line-strong); }
/* A bare `<svg viewBox>` with no text sibling collapses to 0×0; with one it over-grows to double
   every other glyph's 15px. This states the size the rest of the header already agrees on.
   Canonical home is alt-components.css's `> svg` list alongside the other three controls with
   the same defect; folded in here only because that file has another owner this wave. */
.a-account > svg{ width:15px; height:15px; flex:0 0 auto; }

/* ---------- global search results ---------- */
.a-results{
  position:absolute; z-index:var(--z-dialog, 300); top:calc(100% + 6px); left:0; right:0;
  max-height:60vh; overflow:auto; padding:6px;
  background:var(--a-float); border:1px solid var(--a-line-strong); border-radius:var(--a-r);
  box-shadow:var(--a-shadow);
}
.a-res-group{ padding:6px 9px 2px; font-size:10px; font-weight:700; letter-spacing:.09em;
  text-transform:uppercase; color:var(--a-dim); }
.a-res{
  display:flex; align-items:center; gap:10px; width:100%; padding:8px 9px; border-radius:var(--a-r-sm);
  background:none; border:none; color:var(--a-text); font:inherit; text-align:left; cursor:pointer;
}
.a-res:hover,.a-res.sel{ background:var(--a-blue-soft); }
.a-res .t{ flex:1 1 auto; min-width:0; font-size:13px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.a-res .m{ font-size:11px; color:var(--a-faint); white-space:nowrap; }
.a-res .kind{ font-family:var(--a-mono); font-size:9px; letter-spacing:.08em; text-transform:uppercase;
  color:var(--a-dim); width:44px; flex:0 0 auto; }

/* ---------- page frame ---------- */
.a-page{ flex:1 1 auto; min-height:0; display:flex; flex-direction:column; }
.a-band{ flex:0 0 auto; padding:var(--a-3) var(--a-4) 0; }
.a-main{
  flex:1 1 auto; min-height:0; display:grid; gap:var(--a-3);
  grid-template-columns:var(--a-left, 320px) minmax(0,1fr) var(--a-right, 300px);
  padding:var(--a-3) var(--a-4) var(--a-4);
}
.a-main.no-left{ grid-template-columns:minmax(0,1fr) var(--a-right, 300px); }
.a-main.no-right{ grid-template-columns:var(--a-left, 320px) minmax(0,1fr); }
.a-main.no-left.no-right{ grid-template-columns:minmax(0,1fr); }
.a-col{ min-width:0; min-height:0; display:flex; flex-direction:column; gap:var(--a-3); }
/* `position:relative` so an absolutely-positioned `.a-sr`/`.sr-only` descendant resolves against
   this column, not the page — without it such a descendant counts toward the PAGE's scrollable
   overflow instead, which is how the team board once scrolled sideways by 1325px.
   `_docs/tests/teamboard-responsive-overflow.test.js` holds every horizontal scroll container
   in this sheet and the kanban sheet to it. */
.a-col.scrolls{ overflow:auto; position:relative; }

/* ---------- quick actions strip ---------- */
.a-quick{
  display:flex; align-items:center; gap:var(--a-2); flex-wrap:wrap;
  padding:var(--a-2) var(--a-4) var(--a-4);
}
.a-quick .a-btn{ background:var(--a-panel); }

/* ---------- toast / status line ----------
   `left:0; right:0; margin-inline:auto` with `width` auto, not `left:50%` with a transform — the
   latter runs CSS 2.1's shrink-to-fit through a halved available width (containing block minus
   `left` itself), so at narrow viewports the box got half the room `max-width` promised and
   wrapped a one-line message across several lines. Both margins auto is what fills the gap first
   and lets `max-width` clamp it correctly. */
.a-status{
  position:fixed; left:0; right:0; bottom:18px; margin-inline:auto;
  z-index:var(--z-alert); max-width:min(560px, 92vw);
  padding:9px 14px; border-radius:var(--a-r); font-size:13px;
  background:var(--a-float); border:1px solid var(--a-line-strong); box-shadow:var(--a-shadow);
}
.a-status.warn{ border-color:var(--a-amber); }
.a-status.err{ border-color:var(--a-red); }

/* ---------- the dialog scrim ----------
   Opaque by default — `hidden` is the only thing that hides it. It used to be `opacity:0` with a
   `.show` class nothing ever added, so Settings, task/note previews and other dialogs sat fully
   in the DOM, sized, layered and focused, yet invisible — reported as "does not launch" when the
   dialog was in fact open. The fade now belongs to the card inside, which animates independently
   and cannot take the scrim's visibility with it if a class is forgotten again. */
.a-scrim{
  position:fixed; inset:0; background:rgba(0,0,0,.55); z-index:var(--z-dialog, 300);
}
.a-scrim > *{ animation:a-rise var(--a-quick) var(--a-ease); }
@keyframes a-rise{ from{ opacity:0; transform:translateY(6px) scale(.99); } }
@media (prefers-reduced-motion: reduce){
  .a-scrim > *{ animation:none; }
}
@media (prefers-reduced-motion: reduce){
  .a-bar > i,.a-seg > span{ transition:none; }
}

/* ---------- the three columns ----------
   Each scrolls itself; the page does not — a single page scroll would carry the list, the
   numbers and the details away together. */
.a-left,.a-centre,.a-right{ min-width:0; min-height:0; display:flex; flex-direction:column; gap:var(--a-3); }
.a-left{ overflow-y:auto; }
.a-right{ overflow-y:auto; }
.a-centre{ overflow:hidden; }
.a-actions-card{
  background:var(--a-panel); border:1px solid var(--a-line-soft); border-radius:var(--a-r-lg);
  padding:var(--a-3);
}
/* Every control in this row is the same height, middle on the same line — a field with a
   stacked label is taller than a button, so a field in a toolbar puts its label alongside
   instead. `.a-toolbar` itself lives in alt-components.css; do not re-add it here. */
.a-field.inline{
  flex-direction:row; align-items:center; gap:7px;
}
.a-field.inline > span{ flex:0 0 auto; }
.a-field.inline .a-select{ height:var(--a-ctl); }
.a-vrule{ width:1px; align-self:stretch; margin:2px 4px; background:var(--a-line); }
.a-hint{ font-size:12px; color:var(--a-dim); line-height:1.45; }
.a-hint.err{ color:var(--a-red); }
.a-err-note{
  /* Plain declarations first, both of them: an engine without color-mix() discards the whole
     declaration rather than just the one value, so the chip still gets a tint and an edge. */
  font-size:12px; color:var(--a-amber);
  background:var(--a-amber-soft);
  background:color-mix(in srgb, var(--a-amber) 12%, transparent);
  border:1px solid var(--a-amber);
  border:1px solid color-mix(in srgb, var(--a-amber) 40%, transparent);
  border-radius:var(--a-r); padding:8px 10px; margin:6px 0;
}
.a-row-actions{ display:flex; align-items:center; gap:var(--a-2); flex-wrap:wrap; }

/* ---------- the settings dialog ----------
   Sized in viewport units, capped, never taller than the screen — a dialog that grows with its
   content once pushed its own tabs off the top.
   The framed copy (`.a-preview.a-preview-modal`) shares this selector rather than its own
   numbers, because team_board.html hosts the real dialog while okrs.html frames
   `tasks.html?embed=settings` instead — one dialog seen from two pages must be sized identically.
   Team configuration is a DIFFERENT dialog (`#adminModal > .modal-card`), sized in JavaScript by
   sizeSettingsDialog(); its inline width/height outranks this rule, which only gives it
   centring, border, radius and shadow. Do not add team-specific numbers here — that would be two
   answers in two languages. */
.a-modal,
.a-preview.a-preview-modal{
  position:fixed; top:50%; left:50%; right:auto; bottom:auto;
  transform:translate(-50%,-50%);
  width:min(920px, 92vw); height:min(760px, 88vh);
  display:flex; flex-direction:column; z-index:calc(var(--z-dialog, 300) + 1);
  background:var(--a-float); border:1px solid var(--a-line-strong);
  border-radius:var(--a-r-lg); box-shadow:var(--a-shadow-lg); overflow:hidden;
}
body.a-modal-open{ overflow:hidden; }
.a-pane-h{
  font:600 12px/1.2 var(--a-display); letter-spacing:.08em; text-transform:uppercase;
  color:var(--a-dim); margin:18px 0 8px;
}
.a-pane-h:first-child{ margin-top:0; }
/* A static heading with a semantic tile, not a card — `.stg-sec-ico` (alt-settings-stride.css)
   reused as-is, beside a plain non-collapsible heading: no chevron, no disclosure, no body
   wrapper. `.has-icon` only adds the flex row `.a-pane-h` needs to hold the tile. */
.a-pane-h.has-icon{ display:flex; align-items:center; gap:10px; margin-top:22px; }
.a-pane-h.has-icon:first-child{ margin-top:0; }
.a-checks{ display:flex; flex-direction:column; gap:7px; margin:10px 0; }
.a-check-l{ display:flex; align-items:center; gap:8px; font-size:13px; cursor:pointer; }
.a-taglist,.a-tagrow{ display:flex; }
.a-taglist{ flex-direction:column; gap:6px; }
.a-tagrow{
  align-items:center; gap:8px; padding:6px 8px; border-radius:var(--a-r);
  background:var(--a-raised); border:1px solid var(--a-line);
}
.a-tagrow .nm{ flex:1 1 auto; min-width:0; }
.a-tagrow .col{ width:44px; flex:0 0 auto; padding:2px; }
.a-tagrow input[type="color"]{ height:var(--a-ctl); }

/* ---------- the momentum chip ----------
   The canonical component (renderMomentum()) in this design's clothes. classic_tasks.html's own
   inline style carried the original production styling, until its retirement (2026-09-11); this
   is now the only definition, tasks.html's body is unconditionally `.alt-app`, and the chip
   would otherwise arrive unstyled. */
.alt-app .momentum{
  display:inline-flex; align-items:center; max-width:100%;
  background:var(--a-panel); border:1px solid var(--a-line); border-radius:999px;
}
.alt-app .momentum .mo-main{
  display:flex; align-items:center; gap:8px; min-width:0;
  background:transparent; border:0; cursor:pointer; color:var(--a-dim);
  font:500 12px/1 var(--a-body); padding:7px 13px;
}
.alt-app .momentum .mo-label{
  font:600 11px/1 var(--a-display); letter-spacing:.08em; text-transform:uppercase;
  color:var(--a-faint);
}
.alt-app .momentum .mo-msg{
  min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; color:var(--a-text);
}
.alt-app .momentum .mo-count{ font:600 11px/1 var(--a-mono); color:var(--a-faint); }
.alt-app .momentum.collapsed .mo-msg{ display:none; }
.alt-app .momentum.on-break{ border-color:var(--a-gold); }
.alt-app .momentum .mo-break{
  border:0; border-left:1px solid var(--a-line); background:transparent; cursor:pointer;
  color:var(--a-gold); font:600 12px/1 var(--a-body); padding:8px 12px; border-radius:0 999px 999px 0;
}
.alt-app .momentum .mo-main:focus-visible,
.alt-app .momentum .mo-break:focus-visible{ outline:2px solid var(--a-ring); outline-offset:-2px; }

/* ---------- the grips ----------
   Big enough to grab, small enough to ignore: 8px wide with a 2px line inside. A real focusable
   separator, so arrows and Home work on it — a resize only a mouse can perform is one half of
   readers cannot make. The line is always drawn (not hover-only) and the column is 10px, because
   a resize control nobody can see is a control nobody has. */
.a-grip{
  flex:0 0 auto; width:10px; align-self:stretch; cursor:col-resize; position:relative;
  background:transparent; border:0; padding:0; touch-action:none;
}
.a-grip::before{
  content:''; position:absolute; inset:10px 4px; border-radius:2px; background:var(--a-line-strong);
  transition:background var(--a-quick) var(--a-ease), inset var(--a-quick) var(--a-ease);
}
.a-grip::after{
  /* Three dots at the middle, the same grammar as the widget handle, so this reads as "drag me"
     rather than a border between two panels. */
  content:''; position:absolute; top:50%; left:50%; width:2px; height:22px;
  transform:translate(-50%,-50%); border-radius:2px;
  background:repeating-linear-gradient(var(--a-faint) 0 2px, transparent 2px 5px);
}
.a-grip:hover::before,.a-grip:focus-visible::before,.a-grip:active::before{
  background:var(--a-blue); inset:4px 4px;
}
.a-grip:focus-visible{ outline:2px solid var(--a-ring); outline-offset:-2px; }
.a-grip[aria-disabled="true"]{ display:none; }
.a-vgrip{
  flex:0 0 auto; height:9px; cursor:row-resize; position:relative; touch-action:none;
  background:transparent; border:0; padding:0; margin:2px 0;
}
.a-vgrip::before{
  content:''; position:absolute; inset:3px 24px; border-radius:2px;
  background:var(--a-line-strong); transition:background var(--a-quick) var(--a-ease);
}
.a-vgrip:hover::before,.a-vgrip:focus-visible::before{ background:var(--a-blue); }
.a-vgrip:focus-visible{ outline:2px solid var(--a-ring); outline-offset:-2px; }
.a-vgrip[aria-disabled="true"]{ display:none; }
/* While a drag is in flight nothing else may claim the pointer or select text under it. */
body.a-resizing{ cursor:col-resize; user-select:none; }
/* A vertical drag otherwise showed horizontal arrows end to end. */
body.a-resizing.a-resizing-y{ cursor:ns-resize; }
body.a-resizing *{ pointer-events:none; }

/* ---------- dragging: THE RULES ARE IN CSS/drag-list.css ----------
   `body.a-dragging-on`, `.a-dragging`, `.a-dragover`, `[data-nodrop="1"].a-dragover` and
   `.a-drop-line` moved out of this file on 2026-09-08, beside the engine that adds those classes
   (JS/components/drag-list.js). classic_tasks.html mounted that engine on its task checklist and
   loaded none of CSS/alt/, so the clothes had to be reachable from outside the Suite too — and a
   copy of five rules inside that page is exactly the second answer this project keeps deleting.
   classic_tasks.html is retired (2026-09-11); every current consumer of drag-list.js also loads
   CSS/alt/, but the separation still stands as one shared definition rather than a copy per page.

   Every Suite page loads CSS/drag-list.css BEFORE the CSS/alt/ sheets, so anything here that
   dresses a dragged element in a particular list still wins on source order. Do not re-add them. */

/* ---------- menu extras the shared launcher draws ---------- */
.a-menu-item .lb{ flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.a-menu-item .dot{ width:8px; height:8px; border-radius:50%; flex:0 0 auto; }
.a-menu-item .tick{ color:var(--a-blue-text); font-size:12px; }
.a-menu-item.on{ color:var(--a-blue-text); }
.a-menu-item.danger{ color:var(--a-red); }
.a-menu-item[disabled]{ opacity:.45; cursor:default; }
/* `.a-menu-sep` and `.a-menu-head` moved to alt-components.css — they were declared here AND
   there. Do not re-add them. */
/* Dresses as an ordinary `.a-menu-item` (carried alongside this class by altMenuSubmenuRow()) —
   this only adds a trailing chevron and a highlighted/expanded look while its flyout is open.
   `.cv` never rotates here, unlike every other `.cv` in this file: it marks a side-opening
   flyout, not a disclosure, so there's no second orientation to rotate into. */
.a-menu-item-sub .cv{
  display:flex; flex:0 0 auto; color:var(--a-faint);
  transition:color var(--a-quick) var(--a-ease);
}
.a-menu-item-sub .cv svg{ width:14px; height:14px; }
.a-menu-item-sub{ transition:background var(--a-quick) var(--a-ease), color var(--a-quick) var(--a-ease); }
/* Keyed to `.open` (set alongside `aria-expanded`) rather than the attribute alone, since a row
   a mouse opened hasn't necessarily taken focus; colour is never the only signal either way. */
.a-menu-item-sub.open{ background:var(--a-blue-soft); color:var(--a-blue-text); }
.a-menu-item-sub.open .cv{ color:var(--a-blue-text); }

/* A quiet fact at the foot of a menu, not a heading — the faint tier at the 11px floor (§18),
   letterspacing and capitals dropped since this is a sentence, not a label. */
.a-menu-note{
  padding:3px 10px 5px; font:400 11px/1.45 var(--a-body);
  letter-spacing:normal; text-transform:none; color:var(--a-dim);
}
/* Same quiet tier, in `--a-amber` ("attention, nothing is broken" — the tab still works, it's
   simply not the newest build). Wraps rather than truncating: half a sentence plus "…" is worse
   than nothing. */
.a-menu-warn{
  padding:3px 10px 4px; font:400 11px/1.45 var(--a-body);
  letter-spacing:normal; text-transform:none; color:var(--a-amber);
  max-width:280px; white-space:normal;
}
.a-menu-item.a-menu-refresh{ color:var(--a-amber); }
.a-menu-item.a-menu-refresh .lb{ font-weight:600; }

/* ---------- TASKS BY EMAIL, INSIDE THE AI ASSISTANT POPOVER ----------
   altTbeCard() (alt-shell.js) draws beneath the assistant's own sentence on the five suite
   pages. One wider menu, not a wider `.a-menu`: `.a-menu` caps at 360px, right for a list of
   nouns but too narrow for a 35-character address beside a Copy button, so the width is scoped
   to this menu by its own class and nothing else in the product moves. */
.a-menu.a-menu-ai{ max-width:min(430px, 92vw); }

.a-tbe{
  display:flex; flex-direction:column; gap:10px;
  margin:4px 2px 2px; padding:10px;
  border:1px solid var(--a-line); border-radius:var(--a-r-sm); background:var(--a-raised);
}
.a-tbe-h{ display:flex; align-items:flex-start; gap:9px; }
/* The shared dictionary's envelope on the blue-soft feature-mark tile. Decorative/aria-hidden. */
.a-tbe-ico{
  flex:0 0 auto; width:26px; height:26px; display:flex; align-items:center; justify-content:center;
  border-radius:var(--a-r-sm); background:var(--a-blue-soft); color:var(--a-blue-text);
}
.a-tbe-ico svg{ width:15px; height:15px; }
.a-tbe-ht{ display:flex; flex-direction:column; gap:2px; flex:1 1 auto; min-width:0; }
.a-tbe-ht b{ font-size:13px; font-weight:700; color:var(--a-text); }
.a-tbe-ht span{ font:400 11px/1.45 var(--a-body); color:var(--a-dim); }
/* "Available"/"Not available" is spelled out — the word is the state, colour only repeats it
   (§18). */
.a-tbe-badge{
  flex:0 0 auto; padding:2px 7px; border-radius:var(--a-r-pill);
  font:700 10px/1.7 var(--a-body); letter-spacing:.07em; text-transform:uppercase;
  background:var(--a-green-soft); color:var(--a-green);
}
.a-tbe-badge.off{ background:var(--a-bg); color:var(--a-dim); border:1px solid var(--a-line); }

.a-tbe-addr{
  display:flex; align-items:center; gap:6px; padding:4px;
  border:1px solid var(--a-line); border-radius:var(--a-r-sm); background:var(--a-float);
}
/* `.a-menu-item` without its `width:100%`, which would make two flex items each claim the whole
   row. 34px is that primitive's own minimum, kept rather than the 30px first tried. */
.a-tbe-addr .a-menu-item{ width:auto; min-height:34px; padding:6px 8px; font-size:12px; }
/* Wraps rather than ellipsing — half an email address is not an email address, so the text sits
   directly in the anchor rather than in a truncating `.lb`. */
.a-tbe-mail{
  flex:1 1 auto; min-width:0; font-weight:600; color:var(--a-blue-text);
  white-space:normal; overflow-wrap:anywhere; line-height:1.35;
}
.a-tbe-copy{ flex:0 0 auto; font-weight:600; border:1px solid var(--a-line); }
.a-tbe-copy .lb{ flex:0 0 auto; overflow:visible; }
.a-tbe-copy.done{ color:var(--a-green); border-color:var(--a-green); }

.a-tbe-p{ margin:0; font:400 11px/1.5 var(--a-body); color:var(--a-dim); }

.a-tbe-stats{ grid-template-columns:repeat(2, minmax(0, 1fr)); }
.a-tbe-stats .a-stat{ cursor:default; }
.a-tbe-cap,
.a-tbe-stamp{ margin:0; font:400 11px/1.45 var(--a-body); color:var(--a-faint); }

@media (max-width:479px){
  .a-tbe-h{ flex-wrap:wrap; }
  .a-tbe-badge{ margin-left:35px; }
  .a-tbe-stats{ grid-template-columns:1fr; }
}

/* ---------- the two AI popover action rows: Settings and Capabilities ----------
   PRODUCT OWNER, from a screenshot: these two rows were the AI redesign missed, still bare
   single-line items while every other row had an icon, subtitle and chevron. Both stay plain
   `.a-menu-item` buttons underneath, wearing one modifier so the shape can't drift between the
   two files that build a row here. Icon tile and copy block both reuse `.a-tbe-ico`/`.a-tbe-ht`'s
   own shape two sections up rather than a second "feature row" language. */
.a-ai-row{
  align-items:center; gap:12px; min-height:60px; padding:9px 11px;
}
.a-ai-row-ic{
  flex:0 0 auto; width:34px; height:34px; display:flex; align-items:center; justify-content:center;
  border-radius:var(--a-r-sm); background:var(--a-blue-soft); color:var(--a-blue-text);
}
.a-ai-row-ic svg{ width:18px; height:18px; }
.a-ai-row-copy{ display:flex; flex-direction:column; gap:2px; flex:1 1 auto; min-width:0; text-align:left; }
.a-ai-row-t{ display:flex; align-items:center; gap:6px; }
.a-ai-row-t b{ font-size:14px; font-weight:700; line-height:1.25; color:var(--a-text); }
/* aiCapabilityCount() reads the routing table's own size at build time, so this pill only ever
   draws the number it is given. */
.a-ai-row-count{
  flex:0 0 auto; padding:1px 6px; border-radius:var(--a-r-pill);
  font:700 11px/1.6 var(--a-body); font-variant-numeric:tabular-nums;
  background:var(--a-blue-soft); color:var(--a-blue-text);
}
.a-ai-row-sub{ font:400 11px/1.45 var(--a-body); color:var(--a-dim); white-space:normal; }
.a-ai-row .cv{ flex:0 0 auto; display:flex; color:var(--a-faint); }
.a-ai-row .cv svg{ width:14px; height:14px; }

/* ---------- the preview ----------
   A dialog holding the canonical surface — nearly the whole viewport, because cropping a full
   application page would be worse than not offering it. */
.a-preview{
  position:fixed; inset:3vh 3vw; display:flex; flex-direction:column; overflow:hidden;
  background:var(--a-panel); border:1px solid var(--a-line-strong);
  border-radius:var(--a-r-lg); box-shadow:var(--a-shadow-lg);
}
/* ---------- A DIALOG SIZED BY ITS CONTENT, NOT BY THE SCREEN ----------
   `.a-preview-fit` dresses every framed application surface whose content caps its own width
   (the task editor's panes at 1040px, the settings pane, Profile & account, team settings) —
   1100px is that 1040px content cap plus the frame either side, so the dialog ends where the
   content ends instead of surrounding it with empty panel. `margin-inline:auto` centres it: a
   fixed box with both `left` and `right` set plus a narrower width is over-constrained, and auto
   margins are what split the leftover space evenly. */
.a-preview-fit{ max-width:1100px; margin-inline:auto; }
.a-preview-h{
  display:flex; align-items:center; gap:var(--a-2); flex:0 0 auto;
  padding:10px var(--a-3); border-bottom:1px solid var(--a-line); background:var(--a-raised);
}
.a-preview-h b{ flex:1 1 auto; min-width:0; font:600 14px/1.2 var(--a-display);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.a-preview-a{ display:flex; align-items:center; gap:6px; flex:0 0 auto; }
.a-preview-b{ flex:1 1 auto; min-height:0; position:relative; background:var(--a-bg); }
.a-preview-b iframe{ width:100%; height:100%; border:0; display:block; }
/* Over the frame, and only for the case the frame cannot report — see altPreviewOpen(). */
.a-preview-b .a-load{ position:absolute; inset:0; display:flex; align-items:center;
  justify-content:center; gap:10px; background:var(--a-bg); }
@media (max-width:820px){
  .a-preview{ inset:0; border-radius:0; border:0; }
}

/* ---------- density ---------- */
/* Both forms, because `body` is itself `.alt-app` on every suite page — `body.a-compact
   .alt-app` (a descendant combinator) matches nothing, so writing only that form silently never
   applies. alt-tokens.css's own theme rules already list both forms together for this reason. */
body.a-compact.alt-app,
body.a-compact .alt-app{ --a-row:36px; --a-ctl:28px; --a-ctl-lg:32px; }
body.a-compact .a-row{ padding-top:3px; padding-bottom:3px; align-items:center; }
body.a-compact .a-row > .a-check,body.a-compact .a-row > .a-row-due,
body.a-compact .a-row > .a-assign-badge,
body.a-compact .a-row > .a-av,body.a-compact .a-row > .a-star,
body.a-compact .a-row > .a-more{ margin-top:0; }
/* Compact is one line, and the tags are on it — which project a task belongs to is the fact a
   dense list is scanned for, so hiding the whole meta row would take the tags with it. */
body.a-compact .a-row-main{ flex-direction:row; align-items:center; gap:8px; }
body.a-compact .a-row-desc{ display:none; }
body.a-compact .a-row-meta > *{ display:none; }
body.a-compact .a-row-meta > .a-row-tags{ display:flex; }
/* The title gives way; the tags never get clipped in half — a shared `flex:0 1 auto` once let
   both shrink together, slicing a tag's own label mid-word. */
body.a-compact .a-row-meta{ flex:0 0 auto; max-width:44%; overflow:hidden; }
body.a-compact .a-row-tags{ overflow:visible; }
body.a-compact .a-row-tags .a-chip:nth-child(n+4){ display:none; }
body.a-compact .a-row-tags .a-chip{ max-width:132px; overflow:hidden; text-overflow:ellipsis;
  white-space:nowrap; }
body.a-compact .a-rail-card{ padding:8px 10px; }

/* ================= THE STRIDE SETTINGS SHELL — TAB STRIP ONLY =================
   Contract: sys_stride-settings-design.md. The token layer, shell header and section-card
   component moved to alt-settings-stride.css; redefining any of that here would be the "two
   components under one name" defect css-class-collision-check.py exists to catch, since this
   file loads BEFORE that one on every host. Only the tab strip's STRIDE "on" treatment stays —
   dialog framing belongs to each host's existing chrome per that file's own header. */
.stg-shell .a-drawer-tabs button[aria-selected="true"]{
  background:var(--stg-ribbon-soft);
  box-shadow:inset 0 -2px 0 0 var(--stg-ribbon-to);
  color:var(--stg-text);
}
.stg-shell .a-drawer-tabs button[aria-selected="true"]::after{ display:none; }

/* Content unavailable — set by altSetContentUnavailable() (alt-shell.js); see that comment for
   why search stands down while Refresh/the bell/the quick guide stay. A body class rather than
   `hidden` on the element, matching `body.ofr-board-hidden`/`body.a-tb-no-access`, since several
   things already toggle visibility inside this row. */
body.a-shell-content-unavailable #altSearchWrap{ display:none; }
/* The utilities need `margin-left:auto` here because hiding the search removes the sibling
   (`.a-top-search{flex:1 1 100%}`) that was pinning them right — without it they fall back to
   x=12 with ~1300px of empty bar past them. */
body.a-shell-content-unavailable #altTopUtils{ margin-left:auto; }
