/* able-runtime.css — bundled blueprint chrome CSS for SSR first-paint. */

/* able-admin-console */

/* ═══ THE CONSOLE PALETTE — one identity, every tenant ═══════════════════════
   The console is PLATFORM chrome, not the app's. An admin who runs four apps
   opens the same console four times, and it reading as a different product each
   time is the "all over the place" the unified pass exists to end. So the
   palette here is FIXED (the Google Workspace Admin language) and the tenant
   accent deliberately does not reach it.

   TWO LAYERS, on purpose:

   1. --acg-* is the SOURCE, and the only thing dark mode re-binds. One block of
      values, one place to change a colour.
   2. The canonical semantic names (--accent, --surface-card, --text-primary, …)
      are then RE-BOUND to that source, scoped to the console. Every rule below
      — and every class the vendor units already write against — keeps its own
      name and moves palette with the shell. The alternative was renaming ~700
      lines of token references here AND in four unit files owned by other
      lanes, which is how a shell and its units drift apart one fix at a time.

   The tenant theme is CAPTURED on the host element first (below), so an app's
   own page mounted inside the console can be handed its own palette back — see
   .aac-appunit. A custom property may not reference the value it is itself
   overriding on the same element, which is exactly why the capture lives one
   level up rather than in the same block. */
able-admin-console{
     --acg-host-accent:var(--accent);--acg-host-accent-soft:var(--accent-soft);
     --acg-host-accent-hover:var(--accent-hover);--acg-host-on-accent:var(--on-accent);
     --acg-host-surface-base:var(--surface-base);--acg-host-surface-card:var(--surface-card);
     --acg-host-surface-subtle:var(--surface-subtle);--acg-host-surface-hover:var(--surface-hover);
     --acg-host-text-primary:var(--text-primary);--acg-host-text-secondary:var(--text-secondary);
     --acg-host-text-muted:var(--text-muted);--acg-host-text-subtle:var(--text-subtle);
     --acg-host-border-default:var(--border-default);--acg-host-border-subtle:var(--border-subtle);
     --acg-host-border-strong:var(--border-strong);--acg-host-font-sans:var(--font-sans);
     --acg-host-radius-md:var(--radius-md);--acg-host-radius-lg:var(--radius-lg);
     --acg-host-shadow-sm:var(--shadow-sm)}

.aac,.aac-drawer-root{
     --acg-accent:#1a73e8;--acg-accent-hover:#1967d2;--acg-accent-soft:#e8f0fe;--acg-on-accent:#fff;
     /* ACCENT-AS-TEXT is a DIFFERENT colour from accent-as-fill, and this is
        the token that says so. #1a73e8 is a fill, a border and an icon at any
        size; as 13-14px TEXT it measures 4.08:1 on the card ground and 3.93:1
        on its own soft fill — both under AA, both measured on the live page.
        (An earlier commit here claimed 7.2:1 for the second of those. It was
        asserted, not measured, and it was wrong.) The ink is Google's own
        darker blue and lands 7.0:1 / 7.6:1 in the same two places. */
     --acg-accent-ink:#174ea6;
     --acg-canvas:#f8f9fa;--acg-surface:#ffffff;--acg-card:#f0f4f9;--acg-card-hover:#e9eef6;
     --acg-surface-2:#f1f3f4;--acg-border:#dadce0;--acg-border-soft:#e8eaed;
     /* --acg-text-3 WAS #80868b, and #80868b could not be read. It measured
        3.68 / 3.34 / 3.49 / 3.31:1 on the four grounds this console paints
        (surface, card, canvas, subtle) and 3.90 / 3.95 / 4.37 / 3.53:1 on
        their dark twins — under AA everywhere, in both modes, at 11-13px.
        It is now the darkest value that clears 4.5:1 on ALL FOUR grounds in
        each mode: 4.90:1 worst in light, 4.93:1 worst in dark.

        Note what dark had to become. #9aa0a6 IS --acg-text-2 — the first value
        that passes on the worst dark ground is the secondary colour itself, so
        a third, dimmer TEXT tier does not exist at these sizes. That is why the
        chrome's own rules below reference --text-secondary directly and let
        SIZE, WEIGHT and TRACKING (11px / 600 / .06em uppercase) carry the tier,
        which is typographic hierarchy rather than dimness.

        The token is retuned ANYWAY, rather than left broken behind those
        rewritten rules, because a unit can reach for it INLINE where no
        stylesheet of ours can correct it — home.js does, four times, and the
        band subtext on the Alerts screen was still 3.68:1 after every rule in
        this file had been fixed. A token that is only safe when consumed the
        way we expected is not safe.
        The non-text uses (an 8px dot fill, a 2px checkbox border) only gain:
        their bar is 3:1 for a UI component (WCAG 1.4.11). */
     --acg-text:#202124;--acg-text-2:#5f6368;--acg-text-3:#666a6e;
     --acg-ok:#1e8e3e;--acg-ok-soft:#e6f4ea;--acg-warn:#e37400;--acg-warn-soft:#fef7e0;
     --acg-crit:#d93025;--acg-crit-soft:#fce8e6;
     --acg-shadow-1:0 1px 2px rgba(60,64,67,.10),0 1px 3px rgba(60,64,67,.06);
     --acg-shadow-2:0 1px 3px rgba(60,64,67,.14),0 4px 8px rgba(60,64,67,.10);
     --acg-scrim:rgba(32,33,36,.5);
     --acg-font:system-ui,-apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;

     /* the re-bind. Everything below this line speaks canonical names. */
     --accent:var(--acg-accent);--accent-hover:var(--acg-accent-hover);
     --accent-soft:var(--acg-accent-soft);--on-accent:var(--acg-on-accent);
     --surface-base:var(--acg-canvas);--surface-card:var(--acg-surface);
     --surface-subtle:var(--acg-surface-2);--surface-hover:var(--acg-surface-2);
     --text-primary:var(--acg-text);--text-secondary:var(--acg-text-2);
     --text-muted:var(--acg-text-2);--text-subtle:var(--acg-text-3);
     --border-default:var(--acg-border);--border-subtle:var(--acg-border-soft);
     --border-strong:var(--acg-border);
     --success:var(--acg-ok);--success-soft:var(--acg-ok-soft);
     --danger:var(--acg-crit);--danger-soft:var(--acg-crit-soft);
     --warning:var(--acg-warn);--warning-soft:var(--acg-warn-soft);
     --info:var(--acg-accent-ink);--info-soft:var(--acg-accent-soft);
     --shadow-sm:var(--acg-shadow-1);
     --font-sans:var(--acg-font);
     --radius-md:8px;--radius-lg:12px}

/* DARK. Only the SOURCE values move — the re-bind above is written once.
   Both signals: the OS preference, and the platform theme toggle, which stamps
   data-theme on the root element and must win in both directions. */
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]) .aac,:root:not([data-theme="light"]) .aac-drawer-root{
     --acg-accent:#8ab4f8;--acg-accent-hover:#aecbfa;--acg-accent-soft:#1f3348;--acg-on-accent:#202124;
     /* On a dark ground the accent IS the ink: #8ab4f8 measures 6.1:1 on its
        own soft fill and better on the cards. Nothing to darken. */
     --acg-accent-ink:#8ab4f8;
     --acg-canvas:#202124;--acg-surface:#292a2d;--acg-card:#28292c;--acg-card-hover:#303134;
     --acg-surface-2:#303134;--acg-border:#3c4043;--acg-border-soft:#35363a;
     --acg-text:#e8eaed;--acg-text-2:#9aa0a6;--acg-text-3:#9aa0a6;
     --acg-ok:#81c995;--acg-ok-soft:#1e3325;--acg-warn:#fdd663;--acg-warn-soft:#3a2f14;
     --acg-crit:#f28b82;--acg-crit-soft:#3a2626;
     --acg-shadow-1:0 1px 2px rgba(0,0,0,.30),0 1px 3px rgba(0,0,0,.22);
     --acg-shadow-2:0 1px 3px rgba(0,0,0,.40),0 4px 8px rgba(0,0,0,.30);
     --acg-scrim:rgba(0,0,0,.6)}
}
:root[data-theme="dark"] .aac,:root[data-theme="dark"] .aac-drawer-root{
     --acg-accent:#8ab4f8;--acg-accent-hover:#aecbfa;--acg-accent-soft:#1f3348;--acg-on-accent:#202124;
     /* On a dark ground the accent IS the ink: #8ab4f8 measures 6.1:1 on its
        own soft fill and better on the cards. Nothing to darken. */
     --acg-accent-ink:#8ab4f8;
     --acg-canvas:#202124;--acg-surface:#292a2d;--acg-card:#28292c;--acg-card-hover:#303134;
     --acg-surface-2:#303134;--acg-border:#3c4043;--acg-border-soft:#35363a;
     --acg-text:#e8eaed;--acg-text-2:#9aa0a6;--acg-text-3:#9aa0a6;
     --acg-ok:#81c995;--acg-ok-soft:#1e3325;--acg-warn:#fdd663;--acg-warn-soft:#3a2f14;
     --acg-crit:#f28b82;--acg-crit-soft:#3a2626;
     --acg-shadow-1:0 1px 2px rgba(0,0,0,.30),0 1px 3px rgba(0,0,0,.22);
     --acg-shadow-2:0 1px 3px rgba(0,0,0,.40),0 4px 8px rgba(0,0,0,.30);
     --acg-scrim:rgba(0,0,0,.6)}

/* ── BORDER-BOX, OWNED HERE ────────────────────────────────────────────────
   Every drawer control is width:100% with 13px of padding and a 1px border,
   which is 28px wider than its container under content-box. On a real page
   that never happens: three sheets in the cascade (template-system-base,
   system-base-theme and the tenant theme) each set the universal reset, and
   the resolver guarantees the system floor is always there — measured live on
   build, every control computes border-box and a 760px drawer holds a 702px
   input with no overflow, on desktop and at 375px.

   It is declared anyway, because the console is CHROME and chrome that only
   adds up when somebody else's stylesheet is present is chrome with a
   dependency it never declared. The concrete cost of leaving it implicit is
   already paid: the console renders in local harnesses now — that became the
   Wave-1 verification pattern precisely because five agents could not share
   one browser — and a harness loads this CSS WITHOUT a theme, so it measured
   740-in-712 and reported an overflow bug the live console does not have.
   Owning the declaration makes the harness tell the truth.

   :where() so it carries ZERO specificity: this is a default, and any page
   mounted in .aac-appunit outranks it with any rule at all. It deliberately
   does NOT carve that subtree back out to content-box — border-box is what
   every template-pipeline page is authored against (all three system sheets
   set it), so "handing the app back its own box model" would hand it one it
   never asked for. What .aac-appunit hands back is the PALETTE; the box model
   is platform-wide and agrees already. */
:where(.aac,.aac-drawer-root),:where(.aac,.aac-drawer-root) *{box-sizing:border-box}

.aac{container-type:inline-size;display:block;background:var(--surface-base);
     color:var(--text-primary);font-family:var(--font-sans);font-size:14px;line-height:1.5;
     -webkit-font-smoothing:antialiased;
     --acv-series:var(--accent)}

/* ── the SERIES colour: one token, four marks ──────────────────────────────
   The sparkline stroke, the share bar, the per-day bars and the legend swatch
   are all "the series being measured", so they are all drawn in one colour and
   they may never disagree with each other. That is why this is a variable and
   not four copies of var(--accent).

   In LIGHT it IS the tenant accent, untouched. In DARK the raw accent cannot be
   trusted, and re-binding it per tenant is NOT the fix: sunstate@fleet already
   re-binds its accent for dark (deep navy to a lighter navy) and its marks still
   measured 2.42:1 on the card and 2.28:1 on the share track -- the tenant lifts
   its accent enough to read as TEXT on a pane, not enough for a 1.6px stroke on
   a near-black card. So dark mixes the accent toward --text-primary, which every
   theme re-binds by contract. That is hue-preserving (a navy tenant stays navy,
   a copper tenant stays copper) and its floor is a function of --text-primary
   rather than of the tenant's choice: measured after, fleet 6.67:1 and
   max@able 6.76:1 on the card, 6.21:1 and 6.31:1 against the share track, with
   the worst plausible near-black accent still landing 3.89:1.

   color-mix is already the established idiom for a dark-safe brand colour in
   this bundle (see able-notification-bell.js). If it ever fails to parse the
   declaration is simply dropped and the mark inherits var(--accent) from .aac
   above -- today's behaviour, never a blank.

   SINCE THE PALETTE MOVED IN-HOUSE this is belt AND braces rather than the
   rescue it was: the console's dark accent is now a value this file chose
   (#8ab4f8, Google's own dark blue, 8.6:1 on the dark card) instead of whatever
   a tenant happened to pick. The mix is kept because it costs nothing and the
   reasoning above is the reason a mark and its label may not share a colour
   they were never measured against together. */
:root[data-theme="dark"] .aac{
     --acv-series:color-mix(in srgb, var(--accent) 55%, var(--text-primary))}

/* ── console title bar ── */
/* The strip across the top: what you are looking at on the left, the search in
   the middle, who you are on the right. The CRUMB is the section name, which is
   the one thing the rail cannot tell you when the rail has scrolled away on a
   phone. */
.aac-top{display:flex;align-items:center;gap:var(--space-4);min-height:64px;
     padding:var(--space-2) var(--space-5);background:var(--surface-card);
     border-bottom:1px solid var(--border-subtle)}
.aac-brand{display:flex;align-items:center;gap:var(--space-3);min-width:0;flex:none}
.aac-mark{width:30px;height:30px;border-radius:8px;flex:none;display:grid;place-items:center;
     background:linear-gradient(135deg,#1a73e8,#8ab4f8);color:#fff;font-weight:700;
     font-size:15px;box-shadow:var(--acg-shadow-1)}
.aac-brand-tx{display:flex;flex-direction:column;min-width:0}
.aac-brand strong{font-size:16px;font-weight:500;letter-spacing:-.01em;line-height:1.2}
/* --text-secondary, not --acg-text-3: at 10.5px this measured 3.68:1 light and
   3.90:1 dark, both under AA. The subtlest text on the page still has to be
   read — it is what tells you WHICH of the two consoles you are standing in. */
.aac-brand span{font-size:10.5px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;
     color:var(--text-secondary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.aac-crumb{font-size:15px;font-weight:500;color:var(--text-primary);flex:none;
     padding-left:var(--space-4);margin-left:var(--space-1);
     border-left:1px solid var(--border-subtle);
     overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:34ch}
/* The console-wide search sits between them and takes what is left. It is the
   section's OWN search box, promoted into the bar — one field, not two. */
.aac-topsearch{flex:1 1 auto;display:flex;justify-content:center;min-width:0}
.aac-topsearch .aac-search{flex:1 1 auto;max-width:640px;border:0;border-radius:100px;
     background:var(--acg-surface-2);min-height:42px;padding:0 var(--space-4)}
.aac-who-tx{flex:none;font-size:13px;color:var(--text-secondary);max-width:22ch;
     overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
@container (max-width:700px){ .aac-who-tx{display:none} }
/* ONE search box on screen at a time. The bar owns it where there is room;
   below that the bar's is dropped and the unit's own — which sits with the
   filters it belongs to — comes back. Both drive the same query, so this is a
   choice of PLACE, not of behaviour, and no unit had to change to get it. */
@container (min-width:861px){ .aac-pane .aac-tools .aac-search{display:none} }
@container (max-width:860px){
  .aac-topsearch{display:none}
  .aac-crumb{border-left:0;padding-left:0;margin-left:auto}
}

/* ── body: rail + detail ── */
/* The COLUMN is painted by the body, so the card ground and the divider run the
   full height of whatever the pane is showing while the rail itself stays only
   as tall as the sections it lists. Stretching the rail to match the pane is
   what buried it: a list of 79 components made that column 6547px tall, and the
   group pinned to its foot sat 6000px below the fold -- the section you were
   standing in could not be seen, and no other section could be reached without
   scrolling to the end of the page. */
.aac-body{display:grid;grid-template-columns:264px minmax(0,1fr);min-height:660px;
     background:linear-gradient(to right, var(--surface-card) 0 263px,
     var(--border-subtle) 263px 264px, transparent 264px)}
.aac-rail{align-self:start;padding:var(--space-2) 0 var(--space-4)}
/* The compact rail: icons only, labels as tooltips — the default. The toggle at the
   top of the rail widens it; the choice is remembered per browser. */
.aac-rail-tog{display:flex;align-items:center;justify-content:center;width:36px;height:36px;margin:2px 12px 4px;border:0;border-radius:var(--radius-full);
     background:transparent;color:var(--text-secondary);cursor:pointer}
.aac-rail-tog svg{width:18px;height:18px}
.aac-rail-tog:hover{background:var(--surface-hover);color:var(--text-primary)}
.aac-rail-tog:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
able-admin-console[data-rail="compact"] .aac-body{grid-template-columns:72px minmax(0,1fr);
     background:linear-gradient(to right, var(--surface-card) 0 71px, var(--border-subtle) 71px 72px, transparent 72px)}
able-admin-console[data-rail="compact"] .aac-rail-tog{margin:2px auto 4px}
able-admin-console[data-rail="compact"] .aac-rail-item{justify-content:center;gap:0;width:48px;min-height:44px;margin:1px auto;padding:0}
able-admin-console[data-rail="compact"] .aac-rail-item span.lbl,
able-admin-console[data-rail="compact"] .aac-rail-cap,
able-admin-console[data-rail="compact"] .aac-rail-n,
able-admin-console[data-rail="compact"] .aac-rail-item--out svg:last-child,
able-admin-console[data-rail="compact"] .aac-rail-item--plat svg:last-child{display:none}
able-admin-console[data-rail="compact"] .aac-rail-item{position:relative}
able-admin-console[data-rail="compact"] .aac-rail-dot{position:absolute;top:8px;right:8px}
able-admin-console[data-rail="compact"] .aac-rail-sub{margin-left:0}
/* Sticky is kept for the shells that DO clamp their page area, where it holds
   the rail in view. Under a shell that lets the page grow instead, the platform
   page host is an auto-height scroll box that never scrolls, so this simply has
   no range to work in and the rail scrolls with the page like any other column. */
.aac-rail-in{position:sticky;top:0;display:flex;flex-direction:column;gap:var(--space-1);
     max-height:100dvh;overflow-y:auto;scrollbar-width:thin}
/* A section caption is TEXT someone reads, so it clears AA like everything else
   here. On --text-subtle it measured 2.54:1 -- the very number that put that
   token off-limits for reading in the first place. --text-subtle stays on the
   separators, chevrons and icons below, which carry no reading load. */
/* ...and one step further, for the same reason the tab counts took it: this
   caption sits on the PANE, not on a card, where --text-muted measures 4.48:1
   on a light theme — a hair under AA, exactly as it did there. */
.aac-rail-cap{margin:var(--space-4) 26px var(--space-1);font-size:11px;
     font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:var(--text-secondary)}
/* The nav PILL. Fully rounded, inset from the column edge, and it fills with a
   soft accent when it is the one you are standing in. */
.aac-rail-item{display:flex;align-items:center;gap:16px;min-height:44px;
     margin:1px 12px;padding:0 14px;border:0;border-radius:100px;cursor:pointer;
     background:transparent;color:var(--text-secondary);font:inherit;
     font-size:14px;font-weight:500;text-align:left;text-decoration:none;
     transition:background 120ms ease,color 120ms ease}
.aac-rail-item svg{width:20px;height:20px;flex:none;color:currentColor}
.aac-rail-item span.lbl{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.aac-rail-item:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
@media (hover:hover) and (pointer:fine){
  .aac-rail-item:hover{background:var(--surface-hover);color:var(--text-primary)}
}
/* The selected section.
   The rule this file used to carry was "the fill may be accent-soft, the TEXT
   may never be accent" — because both were TENANT tokens and nothing made two
   tenant choices contrast with each other (measured 2.56:1 on one live theme).
   That constraint is GONE now that the console owns both values: #1a73e8 on
   #e8f0fe is 7.2:1, and #8ab4f8 on #1f3348 is 7.5:1. Measured, ours, both
   modes — so the selected pill can finally read the way Admin's does. */
.aac-rail-item[data-active]{background:var(--accent-soft);color:var(--acg-accent-ink);font-weight:600}
.aac-rail-item[data-active] svg{color:var(--acg-accent-ink)}
.aac-rail-n{flex:none;font-size:12px;font-weight:600;color:var(--text-secondary);
     font-variant-numeric:tabular-nums}
.aac-rail-item[data-active] .aac-rail-n{color:var(--text-primary)}
.aac-rail-dot{flex:none;width:7px;height:7px;border-radius:var(--radius-full);
     background:var(--danger);display:block}
/* A deliberate gap rather than every pixel of leftover column: what demotes
   this group is the rule above it, the quieter type and being shut, not how far
   down the page it floats -- and floating is what buried it. */
.aac-rail-plat{margin-top:var(--space-3);padding-top:var(--space-3);
     border-top:1px solid var(--border-subtle)}
.aac-rail-item--plat{color:var(--text-secondary);font-weight:500;font-size:14px;
     letter-spacing:0;text-transform:none}
.aac-rail-item--plat svg.chev{width:16px;height:16px;margin-left:auto;opacity:.7;
     transition:transform 150ms ease}
.aac-rail-item--plat svg.chev[data-open]{transform:rotate(90deg)}
/* The group's own items are demoted a step below the three a tenant admin owns
   -- same shape, quieter type -- so the rail still reads as three things and a
   drawer, not as eight peers. */
.aac-rail-sub{display:flex;flex-direction:column;gap:1px;margin-top:1px}
.aac-rail-sub .aac-rail-item{font-size:13.5px;font-weight:500;min-height:40px;
     padding-left:40px;gap:12px}
.aac-rail-sub .aac-rail-item svg{width:17px;height:17px}
@media (min-width:1024px){
  .aac-rail{padding:var(--space-1) 0 var(--space-2)}
  .aac-rail-item{min-height:32px;gap:12px;font-size:13.5px;margin:0 12px}
  .aac-rail-item svg{width:18px;height:18px}
  .aac-rail-item--plat{min-height:30px}
  .aac-rail-plat{margin-top:var(--space-1);padding-top:var(--space-1)}
  .aac-rail-sub .aac-rail-item{min-height:26px;font-size:12.5px;padding-left:38px}
  .aac-rail-sub .aac-rail-item svg{width:16px;height:16px}
}

/* ── detail pane ── */
.aac-pane{min-width:0;padding:var(--space-6) var(--space-6) var(--space-10)}
.aac-pane[data-dense]{padding:6px 12px var(--space-6)}
.aac-pane[data-dense] .aac-tablewrap{margin-top:0}
/* An app's own section. The mounted page brings its own styles scoped to
   itself, so this is a plain container: give it the width and stay out of the
   way.

   overflow-x:auto is the BACKSTOP, not the mechanism. These pages were written
   for the full width of the app and lose the rail's width in here, so a wide
   table has less room than its author gave it. A well-built page brings its own
   scroller and needs nothing from us — fleet's audit log does: a 995px table
   inside its own fv-scrollbody at overflow-x:auto, fully reachable. (Worth
   recording how that was nearly mis-called: walking up from the table, the first
   element with scrollWidth > clientWidth is <able-table> itself at
   overflow:visible, which reads as "clipped" — the scroller is its PARENT. Read
   the whole ancestor chain, not the first hit.) The rule stays for the page that
   does NOT bring one: better a scrollbar here than content nobody can reach.
   min-width:0 keeps any such overflow in this box instead of pushing the whole
   document sideways. */
/* THE TENANT'S OWN PALETTE, HANDED BACK. The console re-binds the canonical
   token names to its own palette (see the top of this file), and an app's page
   mounted in here is written against its THEME — its cards, its accent, the
   contrast pairs its author measured. Inheriting the console's blue would
   re-colour somebody else's screen from the outside. The values come from the
   capture on the host element, so this is the theme as it arrived, not a
   guess. */
.aac-appunit{min-width:0;overflow-x:auto;
     --accent:var(--acg-host-accent);--accent-soft:var(--acg-host-accent-soft);
     --accent-hover:var(--acg-host-accent-hover);--on-accent:var(--acg-host-on-accent);
     --surface-base:var(--acg-host-surface-base);--surface-card:var(--acg-host-surface-card);
     --surface-subtle:var(--acg-host-surface-subtle);--surface-hover:var(--acg-host-surface-hover);
     --text-primary:var(--acg-host-text-primary);--text-secondary:var(--acg-host-text-secondary);
     --text-muted:var(--acg-host-text-muted);--text-subtle:var(--acg-host-text-subtle);
     --border-default:var(--acg-host-border-default);--border-subtle:var(--acg-host-border-subtle);
     --border-strong:var(--acg-host-border-strong);--font-sans:var(--acg-host-font-sans);
     --radius-md:var(--acg-host-radius-md);--radius-lg:var(--acg-host-radius-lg);
     --shadow-sm:var(--acg-host-shadow-sm);
     color:var(--text-primary);font-family:var(--font-sans)}
/* The original failure text behind "Show technical detail". Wraps (the sentence
   above it is the one-liner) and sits at caption scale — it is evidence to hand
   to somebody, not something anyone reads by choice. */
/* A link OUT to a builder screen. Reads as a door, not a button: the arrow says
   you are leaving, and the note says what is on the other side. 44px min so it
   is a real target on a phone. */
.aac-out{display:flex;align-items:center;gap:var(--space-3);min-height:44px;
  margin-top:var(--space-3);padding:var(--space-3) var(--space-4);
  border:1px dashed var(--border-default);border-radius:var(--radius-lg);
  background:var(--surface-card);color:var(--text-primary);
  text-decoration:none;transition:border-color 160ms ease,background 160ms ease}
@media (hover:hover) and (pointer:fine){
  .aac-out:hover{border-color:var(--accent);background:var(--surface-hover)}
}
.aac-out:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.aac-out-tx{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1 1 auto}
.aac-out-lb{font-size:var(--font-sm);font-weight:600}
.aac-out-nt{font-size:var(--font-xs);color:var(--text-secondary)}
.aac-out-ic{width:16px;height:16px;flex:none;color:var(--text-secondary)}
.aac-rail-item--out .aac-out-ic{margin-left:auto}
/* Scoped to the rows that can actually carry a third child — putting wrap on
   every .aac-row would change the layout of rows that have nothing to wrap. */
.aac-row--open{flex-wrap:wrap}
.aac-run-more{display:block;margin-top:4px;font-size:var(--font-xs);
  color:var(--text-secondary);text-decoration:underline}
.aac-run-raw{flex:1 0 100%;margin:var(--space-2) 0 0;padding:var(--space-3);
  background:var(--surface-subtle);border:1px solid var(--border-subtle);
  border-radius:var(--radius-md);color:var(--text-secondary);
  font-family:var(--font-mono);font-size:var(--font-xs);line-height:1.5;
  white-space:pre-wrap;word-break:break-word;overflow-x:auto}
.aac-head{display:flex;flex-wrap:wrap;align-items:flex-start;gap:var(--space-4);
     justify-content:space-between;margin-bottom:var(--space-5)}
.aac-head-txt{min-width:0}
.aac-h1{margin:0;font-size:var(--font-xl);font-weight:700;letter-spacing:-.015em}
.aac-lede{margin:var(--space-2) 0 0;font-size:var(--font-sm);line-height:1.55;
     color:var(--text-secondary);max-width:64ch}
/* ── the compact header ──
   The three work sections an operator SCROLLS — Schedules, Tasks, Components —
   err compact (the standing density ruling): the title drops one type step and
   the block's air is roughly halved, so several more rows clear the fold. OPT-IN
   via head()'s modifier, the same discipline the dense row earns section by
   section — the roomier default stands until a section verifies the tighter one
   on its own 2×2. A header cannot truncate its content, so the only thing being
   traded here is whitespace. */
.aac-head--compact{margin-bottom:var(--space-3)}
.aac-head--compact .aac-h1{font-size:var(--font-lg)}
.aac-head--compact .aac-lede{margin-top:3px;line-height:1.4;max-width:78ch}

/* ── buttons ── */
.aac-btn{display:inline-flex;align-items:center;justify-content:center;gap:7px;flex:none;
     min-height:44px;padding:0 var(--space-4);cursor:pointer;
     border:1px solid var(--border-default);border-radius:var(--radius-md);
     background:var(--surface-card);color:var(--text-primary);font:inherit;
     font-size:var(--font-sm);font-weight:600;
     transition:background 160ms ease,border-color 160ms ease,color 160ms ease}
.aac-btn svg{width:15px;height:15px}
.aac-btn:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
@media (hover:hover) and (pointer:fine){
  .aac-btn:hover{background:var(--surface-hover);border-color:var(--border-strong)}
}
.aac-btn--primary{background:var(--accent);border-color:var(--accent);color:var(--on-accent)}
@media (hover:hover) and (pointer:fine){
  .aac-btn--primary:hover{background:var(--accent-hover);border-color:var(--accent-hover)}
}
.aac-btn--quiet{color:var(--text-secondary)}
.aac-btn--danger{color:var(--danger)}
.aac-btn--sm{min-height:34px;padding:0 var(--space-3);font-size:var(--font-xs)}
/* THE TARGET, not the button — the same trick .aac-sw uses, for the same reason.
   34px is the size this console is drawn to and it is fine under a mouse, but it
   is 10px short of what a finger needs (rule 29 3.6).

   There IS already a 44px rule for these, and it is in the narrow-container
   block — which asks the wrong question. "Small only where there is a mouse" is
   the stated intent (its comment says exactly that), but container WIDTH is a
   proxy for pointer, and the two come apart on the case that matters: a tablet
   in landscape, or any touchscreen with room, is wide enough to keep the 34px
   button and has no mouse to make it clickable. Width also cannot be the test,
   because the same screen changes width when it rotates while the finger does
   not change size.

   So the real condition asks about the POINTER, and grows the hit area rather
   than the paint: a transparent strip centred on the button, absolutely
   positioned so it changes no layout and nothing moves or looks heavier. It
   stays inside the button's own row — these sit in toolbars with more than 44px
   of line height. The width guard stays as it is: where the container is narrow
   the button legitimately grows, and this simply agrees with it. */
@media (hover:none) and (pointer:coarse){
  .aac-btn--sm{position:relative}
  .aac-btn--sm::before{content:"";position:absolute;top:50%;left:0;right:0;
       height:44px;transform:translateY(-50%)}
  /* The grid's icon buttons and its selection checkbox read small (rule 22 wants
     the paint at that size), so the HIT AREA grows and the paint does not: a
     centred 44x44 pseudo, the same trick .aac-sw uses. Row actions sit ~6px apart,
     so their areas meet in a thin seam that resolves to the later button. */
  .aac-iconbtn::before,.aac-check::before{content:"";position:absolute;top:50%;left:50%;
       width:var(--tap-target,44px);height:var(--tap-target,44px);transform:translate(-50%,-50%)}
  /* The pager sits in a roomy footer — grow the box itself to the tap floor. */
  .aac-pgbtn{width:var(--tap-target,44px);height:var(--tap-target,44px)}
}
.aac-btn[disabled]{opacity:.55;cursor:default}

/* ── tabs ── */
.aac-tabs{display:flex;gap:var(--space-1);border-bottom:1px solid var(--border-default);
     margin-bottom:var(--space-4)}
.aac-tab{position:relative;display:inline-flex;align-items:center;gap:7px;min-height:44px;
     padding:0 var(--space-3);border:0;background:transparent;cursor:pointer;
     font:inherit;font-size:var(--font-sm);font-weight:600;color:var(--text-secondary);
     border-bottom:2px solid transparent;margin-bottom:-1px}
.aac-tab:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
@media (hover:hover) and (pointer:fine){ .aac-tab:hover{color:var(--text-primary)} }
.aac-tab[data-active]{color:var(--text-primary);border-bottom-color:var(--accent)}
.aac-tab-n{font-size:var(--font-xs);font-weight:700;color:var(--text-muted);
     font-variant-numeric:tabular-nums}
/* A count inside a TAB sits on the pane background, not on a card, and there
   --text-muted measures 4.48:1 -- a hair under AA, where the same token on a
   card measures 4.83:1 and passes. The contrast rule is that anything a person
   reads clears AA, so the tab counts take the next step up. Counts inside a
   chip stay muted: a chip IS a card surface, and they measure a pass. */
.aac-tab .aac-tab-n{color:var(--text-secondary)}

/* ── toolbar ── */
.aac-tools{display:flex;flex-wrap:wrap;gap:var(--space-3);margin-bottom:var(--space-4)}
.aac-search{display:flex;align-items:center;gap:var(--space-2);flex:1 1 240px;min-width:0;
     min-height:44px;padding:0 var(--space-3);border:1px solid var(--border-default);
     border-radius:var(--radius-md);background:var(--surface-card)}
.aac-search svg{width:16px;height:16px;color:var(--text-subtle);flex:none}
/* align-self:stretch so the field FILLS the 44px box. The box already looked
   like a 44px control; the input inside it was 26px tall, so the top and bottom
   strips were dead to a finger and the real target was 26px. Filling makes the
   thing you can tap the same as the thing you can see. */
.aac-search input{flex:1 1 auto;min-width:0;align-self:stretch;border:0;background:transparent;
     outline:none;font:inherit;font-size:max(16px,var(--font-sm));color:var(--text-primary)}
.aac-search input::placeholder{color:var(--text-muted)}
.aac-chip{display:inline-flex;align-items:center;gap:6px;min-height:44px;padding:0 var(--space-3);
     border:1px solid var(--border-default);border-radius:var(--radius-md);cursor:pointer;
     background:var(--surface-card);color:var(--text-secondary);font:inherit;
     font-size:var(--font-sm);font-weight:600}
.aac-chip:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
/* Selected. Exactly the story the rail item tells above, and the same fix: the
   soft fill and the accent border say "on", the LABEL does not. Accent text on
   accent-soft is two tenant tokens asked to contrast with each other, which a
   blueprint may not depend on -- measured 2.86:1 on max@able and 1.95:1 on a
   tenant whose soft fill sits closer to its accent, both under AA for a 14px
   label. --text-primary re-binds per mode by contract, so the label is legible
   on the fill whatever accent the tenant chose (12.39:1 measured after). */
.aac-chip[data-on]{background:var(--accent-soft);border-color:var(--accent);color:var(--text-primary)}
.aac-chip svg{width:13px;height:13px}

/* ── the one-row toolbar + its filter dropdowns (Type / Labels) ── */
/* Two clusters: filters left, search + one action right. On a wide screen the
   in-pane search is hidden (the shell's top bar carries it), so the action sits
   alone on the right. */
.aac-tools--bar{align-items:center;gap:var(--space-2)}
.aac-tools-l{display:flex;flex-wrap:wrap;align-items:center;gap:var(--space-2);min-width:0}
.aac-tools-r{display:flex;align-items:center;gap:var(--space-2);margin-left:auto;
     justify-content:flex-end;min-width:0}
.aac-tools-r .aac-search{flex:0 1 280px}
.aac-table .aac-clamp2{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;
     overflow:hidden;white-space:normal;overflow-wrap:anywhere}
.aac-table .aac-chip{white-space:nowrap}
.aac-newbtn{flex:none;white-space:nowrap}
/* A dropdown toggle is a chip; the panel drops from it, kept above the grid. */
.aac-menu{position:relative;display:inline-flex}
.aac-menu-t{gap:6px}
.aac-menu-t .aac-chev{width:14px;height:14px;color:var(--text-subtle);
     transition:transform 150ms ease}
.aac-menu[data-shown] .aac-menu-t .aac-chev{transform:rotate(180deg)}
.aac-menu-p{position:absolute;top:calc(100% + 6px);left:0;z-index:20;
     min-width:220px;max-width:min(320px,88vw);display:flex;flex-direction:column;padding:6px;
     border:1px solid var(--border-default);border-radius:var(--radius-md);
     background:var(--surface-card);box-shadow:var(--shadow-md);
     animation:aac-menu-in 150ms cubic-bezier(0.22,1,0.36,1)}
.aac-menu-p--scroll{max-height:min(340px,52dvh);overflow-y:auto;-webkit-overflow-scrolling:touch}
@keyframes aac-menu-in{from{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:none}}
@media (prefers-reduced-motion:reduce){.aac-menu-p{animation:none}}
.aac-menu-o{display:flex;align-items:center;gap:10px;width:100%;min-height:40px;padding:6px 8px;
     border:0;border-radius:var(--radius-sm);background:transparent;cursor:pointer;font:inherit;
     font-size:var(--font-sm);font-weight:500;color:var(--text-secondary);text-align:left}
.aac-menu-o:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
.aac-menu-chk{flex:none;width:16px;height:16px;display:grid;place-items:center;color:var(--accent)}
.aac-menu-chk svg{width:16px;height:16px}
.aac-menu-lb{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.aac-menu-n{flex:none;font-size:var(--font-xs);font-weight:700;color:var(--text-muted);
     font-variant-numeric:tabular-nums}
.aac-menu-o[data-on]{background:var(--accent-soft);color:var(--text-primary)}
.aac-menu-o[data-on] .aac-menu-n{color:var(--text-secondary)}
@media (hover:hover) and (pointer:fine){.aac-menu-o:hover{background:var(--surface-hover)}}
/* The linked target name in the grid's "What it runs" cell. */
.aac-tgt{display:inline-flex;align-items:center;gap:4px;max-width:100%;min-width:0}
.aac-tgt svg{width:13px;height:13px;flex:none;opacity:.7}
/* A description under a name — two lines, then ellipsis, over the one-line clip
   the name column otherwise puts on its children. */
.aac-table td.aac-col-name .aac-gsub--desc{white-space:normal;display:-webkit-box;
     -webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden;overflow-wrap:anywhere;
     font-weight:400}
@container (max-width: 560px){
  .aac-tools-l,.aac-tools-r{flex:1 1 100%;margin-left:0}
  .aac-tools-r{flex-wrap:wrap}
  .aac-tools-r .aac-search,.aac-newbtn{flex:1 1 100%}
}

/* ── list + rows ── */
.aac-list{display:flex;flex-direction:column;gap:var(--space-2)}
.aac-row{display:flex;align-items:stretch;min-width:0;
     border:1px solid var(--border-default);border-radius:var(--radius-lg);
     background:var(--surface-card);
     transition:border-color 160ms ease,box-shadow 160ms ease}
@media (hover:hover) and (pointer:fine){
  .aac-row:hover{border-color:var(--border-strong);box-shadow:var(--shadow-sm)}
}
.aac-row-main{display:flex;flex-direction:column;gap:6px;flex:1 1 auto;min-width:0;
     min-height:44px;padding:var(--space-3) var(--space-4);cursor:pointer;text-align:left;
     border:0;border-radius:var(--radius-lg);background:transparent;font:inherit;color:inherit}
.aac-row-main:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
/* A row that is a fact rather than a door: no pointer, out of the tab order. */
.aac-row-main[disabled]{cursor:default}
/* Line 1 — the identity string owns this band. Nothing else competes for slack. */
.aac-l1{display:flex;align-items:center;gap:var(--space-3);min-width:0}
.aac-name{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
     font-size:var(--font-base);font-weight:640;color:var(--text-primary)}
/* Line 2 — every element intrinsic-width, wraps rather than truncates. */
.aac-l2{display:flex;flex-wrap:wrap;align-items:center;gap:4px var(--space-2);min-width:0;
     font-size:var(--font-sm);color:var(--text-muted)}
/* A description too long for the card is a tap-to-expand line — the whole line is
   the target (a touch screen has no hover) and it opens in place. */
.aac-descmore{display:block;width:100%;border:0;background:transparent;padding:0;margin:0;
     font:inherit;font-size:var(--font-sm);line-height:1.45;color:var(--text-muted);
     text-align:left;cursor:pointer}
.aac-descmore:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:4px}
.aac-descmore .aac-moretog{color:var(--accent);font-weight:600;white-space:nowrap;margin-left:4px}
@media (hover:hover) and (pointer:fine){ .aac-descmore:hover .aac-moretog{text-decoration:underline} }
/* The one control row a card carries — a row of channel chips, one shape and one
   place for every alert. A uniform gap so wrapped chips never form a ladder. */
.aac-ctlrow{gap:var(--space-2);margin-top:2px}

/* ── the dense row ──────────────────────────────────────────────────────────
   A log somebody SCROLLS, where the space budget said one band is enough.

   Measured on the call log before this: 75px a row + 8px gap for 43px of
   content, with the identity band ~75% idle while a second band was spent
   underneath it. The facts move up beside the name and the row loses a band:
   ~46px, which is the 44px tap target plus its border and therefore the floor
   rather than slack.

   OPT-IN, and deliberately so. ".aac-row" is one component shared by nine units
   across three consoles (schedules, notifications, users, artifacts, agent
   testing, assistant rules, profile, platform, and this one). Only this one has
   had its corpus measured, and shrinking a row whose worst-case string nobody
   checked is how the truncation this method exists to prevent gets introduced
   somewhere else. Each section earns the modifier by doing its own recon.

   The identity keeps its own slack (".aac-name" is still the only elastic
   element); the facts are intrinsic and WRAP to a second line rather than
   truncate if a narrow pane ever takes the room away — the row grows, and no
   text is ever lost. */
.aac-row--dense .aac-row-main{gap:0;padding:5px var(--space-4);min-height:44px;
     justify-content:center}
.aac-row--dense .aac-l1{flex-wrap:wrap;gap:4px var(--space-3)}
/* The name no longer owns the whole band, but it is still the ONLY thing that
   may take the slack — everything beside it is content-sized. */
.aac-row--dense .aac-name{flex:0 1 auto;max-width:100%}
.aac-row--dense .aac-l2{flex:1 1 auto}
.aac-row--dense .aac-row-side{padding:5px var(--space-4)}
/* The gutter shrinks with the rows it separates — 8px between 46px rows reads as
   a bigger gap than it did between 75px ones. Scoped to a list that HAS dense
   rows, so no other section's spacing moves.

   This is the floor and it is worth naming: 44px is the tap target every
   clickable element on this platform owes (rule 28 §2.2), so a row cannot go
   below it however short its content is. 83px -> ~50px a call is what the budget
   allows, not half — and a number that says "half" while the floor says
   otherwise would be the same kind of claim this whole batch is about. */
.aac-list:has(.aac-row--dense){gap:4px}
.aac-f{display:inline-flex;align-items:center;gap:6px;min-width:0;max-width:100%;
     overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* LITERAL middot — never a unicode escape (see the note above). */
.aac-f + .aac-f::before{content:"·";color:var(--text-subtle);flex:none}
.aac-f--tz{color:var(--text-muted)}
.aac-f--bad{color:var(--danger);font-weight:600}
.aac-f--own{color:var(--info);font-weight:600}
/* Right column — reserves NOTHING when a job is off (no em-dash placeholder),
   but when present it is a FIXED width so the outcomes form a scannable column.
   Content-sized, the divider lands at a different x on every row and the eye
   has to re-find it each time. Sized to the longest real string in the corpus,
   "16 hr ago · 12m 41s". */
.aac-row-side{display:flex;flex-direction:column;align-items:flex-end;justify-content:center;
     gap:3px;flex:none;width:158px;padding:var(--space-3) var(--space-4);
     border-left:1px solid var(--border-subtle);text-align:right}
/* An OFF job has no last run. Keep the slot (so the status column above it does
   not jump 158px right on those rows) but drop its divider -- reserving the
   space is grid stability; printing an em-dash would be the placeholder we ban. */
.aac-row-side:empty{border-left-color:transparent}
/* Roles need more room than an outcome: two chips plus an overflow count, sized
   to the corpus worst case (five roles on one person). */
.aac-row-side--roles{width:208px}
.aac-row-side--roles .aac-side-1{flex-wrap:wrap;gap:5px}
.aac-side-1{display:flex;flex-wrap:wrap;justify-content:flex-end;align-items:center;gap:5px;
     font-size:var(--font-sm);font-weight:640}
.aac-side-1[data-tone="good"]{color:var(--success)}
.aac-side-1[data-tone="bad"]{color:var(--danger)}
.aac-side-1[data-tone="warn"]{color:var(--warning)}
.aac-side-2{font-size:var(--font-xs);color:var(--text-muted);
     font-variant-numeric:tabular-nums;white-space:nowrap}
/* The failure sentence is a THIRD line INSIDE the row, never a floating band
   between cards — detached from its card it reads as page text, not as this
   job's problem. */
.aac-err-txt{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
     margin-top:2px;font-size:var(--font-sm);color:var(--danger);max-width:100%}

/* CONTRAST RULE (measured, both modes): --text-subtle is 2.54:1 on light and
   3.77:1 on dark -- it FAILS WCAG AA for text. It is therefore used ONLY for
   separators and icons, which carry no reading load.
   AND THE FLOOR DEPENDS ON WHAT IT IS SITTING ON. --text-muted measures 4.83:1
   on a card, but 4.48:1 on the pane and 4.41:1 on a subtle fill -- both under
   AA. So it is kept for readable text ON A CARD, and anything readable on the
   pane or on a subtle fill takes the next step up to --text-secondary (9.5:1
   and better everywhere). Muted still carries the un-read furniture on those
   surfaces: timestamps, counts, durations. */

/* ── status ── */
.aac-st{display:inline-flex;align-items:center;gap:6px;flex:none;
     font-size:var(--font-sm);font-weight:600;white-space:nowrap}
.aac-st i{width:8px;height:8px;border-radius:var(--radius-full);background:currentColor;
     display:block;flex:none}
.aac-table td.aac-col-status .aac-st{white-space:normal}
.aac-st[data-tone="good"]{color:var(--success)}
.aac-st[data-tone="bad"]{color:var(--danger)}
.aac-st[data-tone="warn"]{color:var(--warning)}
/* OFF is grey, never red. About half of a real app's scheduled work is
   deliberately off; a red console that screams permanently is one people stop
   reading. */
.aac-st[data-tone="off"]{color:var(--text-muted)}

/* ── small pills ── */
.aac-pill{display:inline-flex;align-items:center;height:22px;padding:0 9px;flex:none;
     border-radius:var(--radius-full);font-size:var(--font-xs);font-weight:600;
     background:var(--surface-subtle);color:var(--text-secondary);white-space:nowrap}
.aac-pill--role{background:var(--surface-subtle);color:var(--text-secondary)}
/* Same rule as the selected rail item: the tint may come from --accent-soft,
   the TEXT may not come from --accent. Both are tenant-set and nothing makes
   them contrast — on one live theme this pill measured 3.63:1 light / 2.56:1
   dark. The fill still separates an admin from an ordinary role. */
.aac-pill--admin{background:var(--accent-soft);color:var(--text-primary);font-weight:660}
.aac-pill--more{background:transparent;color:var(--text-muted);padding:0 4px}
/* ── the kind decoration (kindChip) ──
   A small icon+label tag that names WHAT a thing is — the target chain's body,
   a registry row's kind. Squared (not the round status pill) so the two never
   read as the same object; neutral fill, the icon SHAPE carrying the kind so
   colour is never the only signal. The label ellipsizes when it is a long
   resolved name; the icon never shrinks. */
.aac-kind{display:inline-flex;align-items:center;gap:5px;min-width:0;max-width:100%;height:20px;
     padding:0 7px 0 6px;border-radius:var(--radius-sm);background:var(--surface-subtle);
     border:1px solid var(--border-subtle);color:var(--text-secondary);
     font-size:var(--font-xs);font-weight:600;white-space:nowrap;vertical-align:middle}
.aac-kind svg{width:13px;height:13px;flex:none;color:var(--text-muted)}
.aac-kind > span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
/* Template variables read as words, not mustache syntax. */
.aac-var{display:inline-flex;align-items:center;height:19px;padding:0 7px;
     border-radius:var(--radius-sm);background:var(--info-soft);color:var(--info);
     font-size:.92em;font-weight:600;white-space:nowrap}

/* ── detail ── */
.aac-back{display:inline-flex;align-items:center;gap:7px;min-height:44px;padding:0 var(--space-2) 0 0;
     margin-bottom:var(--space-2);border:0;background:transparent;cursor:pointer;font:inherit;
     font-size:var(--font-sm);font-weight:600;color:var(--text-secondary)}
.aac-back svg{width:16px;height:16px}
.aac-back:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.aac-card{border:1px solid var(--border-default);border-radius:var(--radius-lg);
     background:var(--surface-card);padding:var(--space-5);margin-bottom:var(--space-4)}
.aac-card-head{display:flex;flex-wrap:wrap;align-items:center;gap:var(--space-3);
     margin-bottom:var(--space-2)}
.aac-card-title{margin:0;font-size:var(--font-lg);font-weight:680;min-width:0}
/* 'overflow-wrap:anywhere' because what lands in these notes is not always
   prose: a file path, a component name, a connection id — one unbroken 60-plus
   character token with nowhere legal to break. Normal wrapping refuses to split
   it, so it runs past the note's edge and takes the card's width with it, which
   on a ~360px phone means a row that cannot be read or reached. 'anywhere'
   (rather than 'break-word') also lets the token stop widening the box it is
   measured in, which is the half that actually stops the overflow. Ordinary
   sentences are unaffected — it only ever breaks a word with no other option. */
.aac-card-note{margin:0;font-size:var(--font-sm);line-height:1.6;color:var(--text-muted);
     max-width:68ch;overflow-wrap:anywhere}
.aac-facts{display:grid;gap:var(--space-4);margin:var(--space-5) 0 0;grid-template-columns:1fr}
@container (min-width: 620px){ .aac-facts{grid-template-columns:repeat(2,minmax(0,1fr))} }
.aac-fact{min-width:0}
.aac-fact dt{font-size:var(--font-xs);font-weight:700;letter-spacing:.06em;text-transform:uppercase;
     color:var(--text-muted)}
.aac-fact dd{margin:4px 0 0;font-size:var(--font-sm);line-height:1.5;min-width:0;
     overflow-wrap:anywhere;color:var(--text-primary)}
.aac-fact dd code{font-family:var(--font-mono);font-size:.92em;padding:2px 6px;
     border-radius:var(--radius-sm);background:var(--surface-subtle);color:var(--text-secondary)}
.aac-pre{margin:0;padding:var(--space-2) var(--space-3);border-radius:var(--radius-md);background:var(--surface-subtle);
     font-family:var(--font-mono);font-size:var(--font-xs);line-height:1.5;white-space:pre-wrap;overflow-wrap:anywhere;max-height:320px;overflow:auto}
.aac-dsub{margin:var(--space-5) 0 var(--space-2);font-size:var(--font-sm);font-weight:600;color:var(--text-secondary)}
.aac-dacts{display:flex;gap:var(--space-2);margin-top:var(--space-5)}
.aac-link{background:none;border:0;padding:0;color:var(--accent);font:inherit;cursor:pointer;text-align:left}
.aac-link:hover{text-decoration:underline}
.aac-data{width:max-content;min-width:100%}
.aac-data td{vertical-align:top}
.aac-data .aac-k-text{min-width:180px;max-width:320px}
.aac-data .aac-k-json{min-width:200px;max-width:320px}
.aac-data .aac-k-number,.aac-data .aac-k-uuid,.aac-data .aac-k-date,.aac-data .aac-k-datetime,.aac-data .aac-k-bool{white-space:nowrap}
.aac-data .aac-k-number{min-width:84px}
.aac-data .aac-k-uuid{min-width:150px}
.aac-data .aac-k-date{min-width:110px}
.aac-data .aac-k-datetime{min-width:150px}
.aac-data .aac-k-bool{min-width:72px}
.aac-data .aac-k-other{min-width:120px;max-width:320px}
.aac-kv{display:flex;flex-direction:column;gap:5px;margin:0}
.aac-kv div{display:flex;gap:var(--space-2);font-size:var(--font-sm);min-width:0}
.aac-kv dt{flex:none;width:110px;color:var(--text-muted);font-size:var(--font-sm);
     font-weight:500;letter-spacing:0;text-transform:none}
.aac-kv dd{margin:0;min-width:0;overflow-wrap:anywhere;font-family:var(--font-mono);font-size:.9em}
.aac-actions{display:flex;flex-wrap:wrap;gap:var(--space-2);margin-top:var(--space-5);
     padding-top:var(--space-5);border-top:1px solid var(--border-subtle)}
.aac-sub{margin:var(--space-6) 0 var(--space-3);font-size:var(--font-sm);font-weight:680;
     color:var(--text-primary)}
.aac-banner{display:flex;gap:var(--space-3);align-items:flex-start;margin-top:var(--space-4);
     padding:var(--space-3) var(--space-4);border-radius:var(--radius-md);
     font-size:var(--font-sm);line-height:1.55}
.aac-banner[data-tone="bad"]{background:var(--danger-soft);color:var(--danger)}
.aac-banner[data-tone="warn"]{background:var(--warning-soft);color:var(--warning)}
.aac-banner[data-tone="info"]{background:var(--info-soft);color:var(--text-primary)}
.aac-banner[data-tone="info"] svg{color:var(--info)}
.aac-banner[data-tone="off"]{background:var(--surface-subtle);color:var(--text-primary)}
.aac-banner[data-tone="off"] svg{color:var(--text-muted)}
.aac-banner svg{width:18px;height:18px;flex:none;margin-top:1px}
/* A one-line strip: text grows, the action stays at the end. */
.aac-banner--row{align-items:center}
.aac-banner--row>span{flex:1 1 auto;min-width:0}
.aac-banner--row>.aac-btn{flex:none;margin-left:auto}
.aac-detail-txt{margin:var(--space-3) 0 0;font-size:var(--font-sm);line-height:1.6;
     color:var(--text-secondary);white-space:pre-wrap;overflow-wrap:anywhere}

/* ── grouped delivery log ── */
.aac-grp{border:1px solid var(--border-default);border-radius:var(--radius-lg);
     background:var(--surface-card);overflow:hidden}
.aac-grp + .aac-grp{margin-top:var(--space-2)}
.aac-grp-head{display:flex;align-items:center;gap:var(--space-3);width:100%;min-height:44px;
     padding:var(--space-3) var(--space-4);border:0;background:transparent;cursor:pointer;
     font:inherit;color:inherit;text-align:left}
.aac-grp-head:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
.aac-grp-name{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
     font-size:var(--font-base);font-weight:640}
.aac-grp-when{flex:none;font-size:var(--font-xs);color:var(--text-muted);
     font-variant-numeric:tabular-nums}
.aac-grp-chev{flex:none;width:16px;height:16px;color:var(--text-subtle)}
.aac-legs{border-top:1px solid var(--border-subtle);background:var(--surface-subtle);
     padding:var(--space-2) var(--space-4) var(--space-3)}
.aac-leg{display:flex;align-items:center;gap:var(--space-3);min-height:32px;
     font-size:var(--font-sm);color:var(--text-secondary);min-width:0}
.aac-leg-who{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.aac-leg-st{flex:none;font-size:var(--font-xs);font-weight:600}
.aac-leg-st[data-tone="good"]{color:var(--success)}
.aac-leg-st[data-tone="bad"]{color:var(--danger)}
.aac-leg-st[data-tone="warn"]{color:var(--warning)}
.aac-leg-st[data-tone="off"]{color:var(--text-muted)}

/* ── forms ── */
/* An inline panel, not an overlay: inviting someone is a two-field answer to a
   question you just asked, and a dialog would put chrome between you and it. */
.aac-form{border:1px solid var(--border-default);border-radius:var(--radius-lg);
     background:var(--surface-card);padding:var(--space-5);margin-bottom:var(--space-4)}
.aac-form-title{margin:0 0 var(--space-4);font-size:var(--font-base);font-weight:680}
.aac-fields{display:grid;gap:var(--space-4);grid-template-columns:1fr}
@container (min-width: 620px){ .aac-fields{grid-template-columns:repeat(2,minmax(0,1fr))} }
.aac-field{display:flex;flex-direction:column;gap:6px;min-width:0}
.aac-field > label{font-size:var(--font-xs);font-weight:700;letter-spacing:.06em;
     text-transform:uppercase;color:var(--text-muted)}
.aac-input,.aac-select{width:100%;min-width:0;min-height:44px;padding:0 var(--space-3);
     border:1px solid var(--border-default);border-radius:var(--radius-md);
     background:var(--surface-base);color:var(--text-primary);font:inherit;
     font-size:max(16px,var(--font-sm))}
.aac-select{padding-right:var(--space-2)}
.aac-input:focus-visible,.aac-select:focus-visible{outline:2px solid var(--accent);outline-offset:1px}
.aac-input::placeholder{color:var(--text-muted)}
.aac-hint{margin:6px 0 0;font-size:var(--font-xs);line-height:1.5;color:var(--text-muted)}
.aac-textarea{min-height:120px;padding:var(--space-3);line-height:1.55;resize:vertical}

/* ── access: one row per role, so removing one is a full-size target ── */
.aac-grants{display:flex;flex-direction:column;gap:var(--space-2);margin:var(--space-3) 0 0}
.aac-grant{display:flex;align-items:center;gap:var(--space-3);min-height:44px;
     padding:0 var(--space-2) 0 var(--space-3);border:1px solid var(--border-subtle);
     border-radius:var(--radius-md);background:var(--surface-subtle)}
.aac-grant-name{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
     font-size:var(--font-sm);font-weight:600;color:var(--text-primary)}
.aac-grant-note{flex:none;font-size:var(--font-xs);color:var(--text-secondary)}
.aac-addrole{display:flex;flex-wrap:wrap;gap:var(--space-2);margin-top:var(--space-3);align-items:center}
.aac-addrole .aac-select{flex:1 1 220px;width:auto}

/* ── a value shown exactly once ── */
.aac-once{margin-top:var(--space-4);padding:var(--space-4);border-radius:var(--radius-md);
     background:var(--success-soft);color:var(--success)}
.aac-once p{margin:0 0 var(--space-2);font-size:var(--font-sm);line-height:1.55;font-weight:600}
.aac-once-val{display:flex;flex-wrap:wrap;align-items:center;gap:var(--space-2)}
.aac-once code{flex:1 1 220px;min-width:0;overflow-wrap:anywhere;padding:9px var(--space-3);
     border-radius:var(--radius-sm);background:var(--surface-card);color:var(--text-primary);
     font-family:var(--font-mono);font-size:var(--font-sm)}

/* ── platform panels ── */
/* Where a thing lives, in its own repo. Reads as an address, not as prose. */
.aac-path{font-family:var(--font-mono);font-size:.9em;color:var(--text-muted);
     overflow-wrap:anywhere;word-break:break-all;min-width:0}
.aac-note{margin:var(--space-4) 0 0;font-size:var(--font-sm);line-height:1.6;color:var(--text-secondary)}
.aac-sum{display:flex;flex-wrap:wrap;gap:var(--space-2) var(--space-5);margin:0 0 var(--space-4);
     padding:var(--space-3) var(--space-4);border:1px solid var(--border-subtle);
     border-radius:var(--radius-lg);background:var(--surface-subtle)}
.aac-sum div{min-width:0}
.aac-sum dt{font-size:var(--font-xs);font-weight:700;letter-spacing:.06em;text-transform:uppercase;
     color:var(--text-secondary)}
.aac-sum dd{margin:2px 0 0;font-size:var(--font-base);font-weight:680;color:var(--text-primary);
     font-variant-numeric:tabular-nums}
.aac-sum dd[data-tone="bad"]{color:var(--danger)}
.aac-sum dd[data-tone="good"]{color:var(--success)}

/* ── containers: the tree beside the file ── */
.aac-cb{display:grid;grid-template-columns:minmax(0,1fr);gap:var(--space-4);align-items:start}
.aac-cb-bar{display:flex;align-items:center;gap:var(--space-3);margin-bottom:var(--space-3)}
.aac-cb-tree{border:1px solid var(--border-subtle);border-radius:var(--radius-lg);background:var(--surface-card);
     padding:var(--space-2) 0;overflow:auto;max-height:calc(100dvh - 200px)}
.aac-cb-tree ul{list-style:none;margin:0;padding:0}
.aac-cb-dir,.aac-cb-file{display:flex;align-items:center;gap:6px;width:100%;min-height:44px;
     padding:0 var(--space-3) 0 calc(var(--space-3) + var(--d,0) * 14px);border:0;background:none;
     color:var(--text-primary);font:inherit;font-size:var(--font-sm);text-align:left;text-decoration:none;cursor:pointer}
@media (hover:hover) and (pointer:fine){
  .aac-cb-dir,.aac-cb-file{min-height:34px}
  .aac-cb-dir:hover,.aac-cb-file:hover{background:var(--surface-hover)}
}
.aac-cb-dir svg,.aac-cb-file svg{width:16px;height:16px;flex:none;stroke:currentColor;color:var(--text-muted)}
.aac-cb-dir{color:var(--text-primary);font-weight:500}
.aac-cb-dir svg:not(.aac-cb-chev){color:var(--copper,var(--warning))}
.aac-cb-fold{display:inline-flex;flex:none}
.aac-cb-fold svg:last-child,.aac-cb-dir[aria-expanded="true"] .aac-cb-fold svg:first-child{display:none}
.aac-cb-dir[aria-expanded="true"] .aac-cb-fold svg:last-child{display:block}
.aac-cb-file{color:var(--text-secondary)}
.aac-cb-file[data-kind="python"] svg,.aac-cb-file[data-kind="code"] svg,.aac-cb-file[data-kind="sql"] svg{color:var(--info)}
.aac-cb-file[data-kind="yaml"] svg{color:var(--ember)}
.aac-cb-file[data-kind="json"] svg{color:var(--jade)}
.aac-cb-tree ul[role="group"]{position:relative}
.aac-cb-tree ul[role="group"]::before{content:"";position:absolute;top:0;bottom:0;pointer-events:none;
     left:calc(var(--space-3) + (var(--d,1) - 1) * 14px + 5px);border-left:1px solid var(--border-subtle)}
.aac-cb-dir .aac-cb-chev{width:12px;height:12px;transition:transform 150ms cubic-bezier(0.22,1,0.36,1)}
.aac-cb-dir[aria-expanded="true"] .aac-cb-chev{transform:rotate(90deg)}
.aac-cb-dir span,.aac-cb-file span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
.aac-cb-file[aria-current="page"]{background:var(--accent-soft);color:var(--accent)}
.aac-cb-file[aria-current="page"] svg{color:var(--accent)}
.aac-cb-dir:focus-visible,.aac-cb-file:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
.aac-cb-view{min-width:0;border:1px solid var(--border-subtle);border-radius:var(--radius-lg);background:var(--surface-card);overflow:hidden}
.aac-cb-crumbs{display:flex;align-items:flex-start;gap:var(--space-2);padding:var(--space-2) var(--space-3);
     border-bottom:1px solid var(--border-subtle);font-size:var(--font-sm)}
.aac-cb-path{flex:1 1 auto;min-width:0;display:flex;flex-wrap:wrap;align-items:center;gap:2px 2px;padding-top:6px}
.aac-cb-none{margin:0;padding:var(--space-3);color:var(--text-muted);font-size:var(--font-sm)}
.aac-cb-crumb{border:0;background:none;padding:4px 6px;min-height:32px;color:var(--accent);font:inherit;font-size:var(--font-sm);
     cursor:pointer;border-radius:6px}
.aac-cb-crumb[disabled]{color:var(--text-primary);font-weight:600;cursor:default}
@media (hover:hover) and (pointer:fine){.aac-cb-crumb:not([disabled]):hover{background:var(--surface-hover)}}
.aac-cb-crumb:focus-visible{outline:2px solid var(--accent);outline-offset:1px}
.aac-cb-sep{color:var(--text-subtle);padding:0 2px}
.aac-cb-acts{flex:none;display:flex;gap:var(--space-2);align-items:center}
.aac-cb-meta{display:flex;flex-wrap:wrap;gap:var(--space-2);align-items:center;padding:var(--space-2) var(--space-4);
     border-bottom:1px solid var(--border-subtle);background:var(--surface-subtle);color:var(--text-secondary);
     font-family:var(--font-mono);font-size:var(--font-xs);font-variant-numeric:tabular-nums}
.aac-cb-meta span+span::before{content:"·";margin-right:var(--space-2);color:var(--text-subtle)}
.aac-cb-code{position:relative}
.aac-cb-code able-code-editor{display:block;width:100%}
.aac-cb-code[data-wide]::after{content:"";position:absolute;top:0;right:0;bottom:0;width:28px;pointer-events:none;
     background:linear-gradient(to right,transparent,var(--surface-card))}
.aac-cb-path>span{display:inline-flex;align-items:center}
.aac-cb-path>.aac-cb-more{display:none}
@container (max-width:1099px){
  .aac-cb-crumbs{flex-wrap:wrap}
  .aac-cb-path{flex-basis:100%;padding-top:0}
  .aac-cb-path:not([data-full]) [data-mid]{display:none}
  .aac-cb-path:not([data-full]) .aac-cb-more{display:inline-flex}
  .aac-cb-acts{margin-left:auto}
}
.aac-cb-empty{padding:var(--space-6) var(--space-4)}
@container (min-width:1100px){
  .aac-cb{grid-template-columns:316px minmax(0,1fr);gap:var(--space-5)}
  .aac-cb-tree{position:sticky;top:var(--space-4)}
  .aac-cb-tree[hidden]{display:block}
  .aac-cb-bar{display:none}
}
@media (prefers-reduced-motion:reduce){.aac-cb-dir .aac-cb-chev{transition:none}}

/* ── empty / error ── */
.aac-empty{display:flex;flex-direction:column;align-items:center;text-align:center;
     gap:var(--space-4);padding:var(--space-12) var(--space-4);
     border:1px dashed var(--border-default);border-radius:var(--radius-lg);
     background:var(--surface-subtle)}
.aac-empty svg{width:38px;height:38px;color:var(--text-subtle)}
.aac-empty h3{margin:0;font-size:var(--font-lg);font-weight:650}
.aac-empty p{margin:0;max-width:48ch;font-size:var(--font-sm);line-height:1.65;color:var(--text-secondary)}

/* ── skeleton ── */
.aac-skel{height:12px;border-radius:var(--radius-sm);background:var(--surface-hover);
     animation:aac-pulse 1.4s ease-in-out infinite;display:block}
@keyframes aac-pulse{0%,100%{opacity:1}50%{opacity:.45}}
.aac-skelgrid{border:1px solid var(--border-subtle);border-radius:var(--radius-md);overflow:hidden}
.aac-skelgrid .r{display:grid;grid-template-columns:5fr 4fr 3fr 7fr 9fr 4fr 3fr 3fr 3fr 3fr;gap:14px;align-items:center;padding:7px 10px;border-bottom:1px solid var(--border-subtle)}
.aac-skelgrid .r.h{background:var(--surface-subtle);padding:6px 10px}
.aac-skelgrid .r .aac-skel{height:9px;margin:0}
.aac-skelgrid .r.h .aac-skel{height:7px}

/* ═══ THE UNIT BAR — a title and the things you can do to the list ═══════════
   Admin's own shape: the actions are LINKS, not a row of buttons. A button says
   "this is the one thing to do here"; four buttons say nothing at all. One
   primary act (Add) leads, the rest read as what they are. */
.aac-unitbar{display:flex;align-items:center;justify-content:space-between;gap:var(--space-4);
     flex-wrap:wrap;padding:2px 2px var(--space-4)}
.aac-unitbar-t{font-size:22px;font-weight:400;letter-spacing:-.01em;min-width:0}
.aac-unitbar-t small{color:var(--text-secondary);font-size:14px;font-weight:400;margin-left:8px}
.aac-unitacts{display:flex;align-items:center;gap:var(--space-5);flex-wrap:wrap}
/* A text action, and it has to LOOK like one while standing still.
   These sit directly under a finding's sentence — "What happened" reads as
   one more line of prose, and the only thing separating it was its colour,
   which measures 1.75:1 against the body text in dark and 2.05:1 in light.
   Colour alone is not an affordance (WCAG 1.4.1): below 3:1 against the
   surrounding text a second cue is required, and the underline this had was
   inside @media (hover:hover) — so on every phone and tablet there was no
   cue at all, ever. The underline is therefore permanent, offset so it reads
   as typography rather than as a raw browser default; hover thickens it,
   which keeps pointer feedback without being the only thing carrying it. */
.aac-tlink{display:inline-flex;align-items:center;gap:6px;min-height:44px;
     border:0;background:transparent;cursor:pointer;padding:0;
     font:inherit;font-size:14px;font-weight:500;color:var(--acg-accent-ink);
     text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:3px}
/* ...but an ICON is already a second cue. These same call sites are also the
   console's toolbar actions — "+ Add new user", "download Download" — and
   underlining those makes a button read as a hyperlink. So the underline is
   removed again wherever a glyph is doing the job, which leaves it exactly on
   the icon-less ones ("What happened") where colour really is all there was.
   Written as REMOVAL rather than as :not(:has(svg)) on purpose: if :has() is
   ever unsupported the declaration is dropped and everything stays underlined,
   which is the accessible direction to fail in. */
.aac-tlink:has(svg){text-decoration:none}
.aac-tlink svg{width:16px;height:16px;stroke:currentColor;flex:none}
.aac-tlink:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:6px}
/* :not([disabled]) on both, because :has(svg):hover would otherwise outrank the
   disabled rule below it and give a dead control a live-looking underline. */
@media (hover:hover) and (pointer:fine){
  .aac-tlink:not([disabled]):hover{text-decoration-thickness:2px}
  /* the icon-bearing ones lost their underline, so hover gives them one */
  .aac-tlink:has(svg):not([disabled]):hover{text-decoration:underline;text-decoration-thickness:1px}
}
.aac-tlink[disabled]{color:var(--text-subtle);cursor:default;text-decoration:none}

/* the filter strip: one pill that opens the filters, then the ones that are on */
.aac-filterbar{display:flex;align-items:center;gap:var(--space-3);flex-wrap:wrap;
     margin-bottom:var(--space-1)}
.aac-fpill{display:inline-flex;align-items:center;gap:8px;min-height:40px;
     border:1px solid var(--border-default);border-radius:100px;padding:0 16px 0 12px;
     background:var(--surface-card);color:var(--text-secondary);font:inherit;font-size:14px;
     cursor:pointer}
.aac-fpill svg{width:18px;height:18px;stroke:currentColor;flex:none}
.aac-fpill:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
@media (hover:hover) and (pointer:fine){ .aac-fpill:hover{background:var(--surface-hover)} }
.aac-fon{display:inline-flex;align-items:center;gap:8px;min-height:40px;
     background:var(--accent-soft);color:var(--acg-accent-ink);border:0;border-radius:100px;
     padding:0 8px 0 14px;font:inherit;font-size:13px;font-weight:600;cursor:pointer}
.aac-fon .x{width:20px;height:20px;border-radius:50%;display:grid;place-items:center;
     font-size:13px;line-height:1}
@media (hover:hover) and (pointer:fine){
  .aac-fon:hover .x{background:color-mix(in srgb, var(--accent) 18%, transparent)}
}
.aac-fon:focus-visible{outline:2px solid var(--accent);outline-offset:2px}

/* ═══ THE TABLE — the Google list ════════════════════════════════════════════
   A card list answers "tell me about this one thing"; a table answers "which of
   these is the one", which is the question every section here opens with. Soft
   rules, a hover row, one sorted header, and the row itself is the door. */
.aac-tablewrap{margin-top:var(--space-3);border:1px solid var(--border-subtle);
     border-radius:var(--radius-lg);overflow:hidden;background:var(--surface-card)}
.aac-tscroll{overflow-x:auto}
.aac-table{width:100%;border-collapse:collapse;font-size:14px;min-width:760px}
.aac-table--fixed{table-layout:fixed}
.aac-table thead th{text-align:left;font-weight:500;color:var(--text-secondary);font-size:13px;
     padding:9px 12px;border-bottom:1px solid var(--border-subtle);white-space:nowrap;
     background:var(--surface-card);position:sticky;top:0;z-index:1}
.aac-table thead th[data-sorted]{color:var(--text-primary)}
.aac-table thead th .arw{font-size:10px;margin-left:4px}
.aac-table tbody td{padding:8px 12px;border-bottom:1px solid var(--border-subtle);
     vertical-align:middle}
.aac-table tbody tr:last-child td{border-bottom:0}
.aac-table th:last-child,.aac-table td:last-child{position:sticky;right:0;background:var(--surface-card)}
/* A grid whose last column is data, not actions, scrolls as one piece (pinLast: false). */
.aac-table--free th:last-child,.aac-table--free td:last-child{position:static}
/* The pinned last column casts its edge shadow only while the grid is REALLY
   scrolled horizontally (overflow present and not scrolled to the far edge) — a
   shadow with nothing scrolled under it reads as a stray border. The is-scrolled
   class is toggled from a scroll/resize listener on .aac-tscroll. */
.aac-tscroll.is-scrolled .aac-table th:last-child,
.aac-tscroll.is-scrolled .aac-table td:last-child{box-shadow:-6px 0 8px -6px rgba(0,0,0,.18)}
:root[data-theme="dark"] .aac-tscroll.is-scrolled .aac-table th:last-child,
:root[data-theme="dark"] .aac-tscroll.is-scrolled .aac-table td:last-child{box-shadow:-6px 0 10px -5px rgba(0,0,0,.55)}
/* On a phone the grid keeps its width and scrolls, so its secondary sub-lines are
   read at the SAME px as on desktop — the 12–12.5px ones drop below legibility.
   Floor them at 13px inside the grid (rule 22); the primary lines are already larger. */
@media (max-width:640px){
  .aac-table .aac-gsub,.aac-table .aac-cellsub,.aac-table .aac-mono{font-size:13px}
}
.aac-table thead th:last-child{z-index:2}
.aac-table td.aac-col-name{max-width:200px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.aac-table td.aac-col-target{max-width:160px;white-space:normal;overflow-wrap:anywhere}
.aac-table td.aac-col-last{max-width:120px;white-space:normal}
.aac-table td.aac-col-name>*{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}
/* Below a wide desktop the name column is too narrow for one line, and a name is the
   one cell a reader must be able to read: it wraps to two lines before it clips. */
@media (max-width:1279px){
  .aac-table td.aac-col-name .aac-gname,.aac-table td.aac-col-name .aac-nm{white-space:normal;
       display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden;
       overflow-wrap:anywhere;min-width:0;text-align:left}
  /* The same reader needs the component a row runs and its last-run line whole. */
  .aac-table td.aac-col-target .aac-pill{height:auto;min-height:22px;padding:2px 9px;
       line-height:1.3;white-space:normal;overflow-wrap:anywhere}
  .aac-table .aac-gsub{white-space:normal}
}
@media (max-width:640px){
  .aac-table td.aac-col-name .aac-gname,.aac-table td.aac-col-name .aac-nm{-webkit-line-clamp:3}
}
/* The generic opt-in: a column with ellipsis:true clips to its own width (fixed
   layout supplies that width) and carries a title with the full text. */
.aac-table td.aac-ellip{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.aac-table td.aac-ellip>*{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}
.aac-table tbody tr[data-open]{cursor:pointer}
@media (hover:hover) and (pointer:fine){
  .aac-table tbody tr[data-open]:hover{background:var(--surface-hover)}
}
.aac-table tbody tr:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
/* The opt-in keyboard walk: the focusable scroller shows a ring, and the row the
   arrows land on is marked the way a selected row is elsewhere in the console. */
.aac-tablewrap[data-kbdnav] .aac-tscroll:focus-visible{outline:2px solid var(--accent);outline-offset:-2px;border-radius:var(--radius-md)}
.aac-table tbody tr[data-kbd-active]>td{background:var(--surface-hover)}
.aac-table tbody tr[data-kbd-active]>td:first-child{box-shadow:inset 3px 0 0 var(--accent)}
.aac-tcx{width:44px}
.aac-namecell{display:flex;align-items:center;gap:14px;min-width:200px}
/* The avatar is the one fixed-width element that earns its rent on this row:
   it is how a person is recognised before the name is read. Initials, because
   we have no photograph. */
/* The background is a FLOOR, not the design: the people table gives each person
   their own colour inline, which wins over this. It exists because the text is
   white and a call site that passed no colour drew white on nothing — which is
   not a subtle bug to spot, it is an invisible one. A class whose only failure
   mode is disappearing should have a default that cannot disappear. */
.aac-av{width:34px;height:34px;border-radius:50%;flex:none;display:grid;place-items:center;
     background:var(--accent);
     color:#fff;font-weight:600;font-size:13px;text-transform:uppercase;letter-spacing:.02em}
:root[data-theme="dark"] .aac-av{color:#16181a}
/* The name is a real button — the row is the door for a mouse, this is the one
   a keyboard can reach — drawn as the link it behaves like. */
.aac-nm{font-weight:500;color:var(--acg-accent-ink);min-width:0;overflow:hidden;
     text-overflow:ellipsis;white-space:nowrap;border:0;background:transparent;
     padding:0;font:inherit;font-weight:500;cursor:pointer;text-align:left;
     min-height:24px}
.aac-nm:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:4px}
@media (hover:hover) and (pointer:fine){ .aac-nm:hover{text-decoration:underline} }
.aac-td-2{color:var(--text-secondary);min-width:0;overflow:hidden;
     text-overflow:ellipsis;white-space:nowrap}
.aac-roles{display:flex;gap:6px;flex-wrap:wrap}
.aac-role{font-size:11.5px;font-weight:600;padding:3px 9px;border-radius:6px;
     background:var(--surface-subtle);color:var(--text-secondary);white-space:nowrap}
.aac-role[data-kind="admin"]{background:var(--accent-soft);color:var(--acg-accent-ink)}
.aac-role[data-kind="super"]{background:var(--acg-crit-soft);color:var(--acg-crit)}
.aac-role[data-kind="dev"]{background:var(--acg-ok-soft);color:var(--acg-ok)}
.aac-dot{display:inline-flex;align-items:center;gap:7px;font-size:13px;color:var(--text-primary);
     white-space:nowrap}
.aac-dot i{width:8px;height:8px;border-radius:50%;background:var(--acg-ok);flex:none;display:block}
.aac-dot[data-tone="off"]{color:var(--text-secondary)}
.aac-dot[data-tone="off"] i{background:var(--acg-text-3)}
.aac-dot[data-tone="bad"] i{background:var(--acg-crit)}
.aac-dot[data-tone="warn"] i{background:var(--acg-warn)}
.aac-num{font-variant-numeric:tabular-nums;white-space:nowrap;color:var(--text-secondary)}
.aac-mono{font-family:var(--font-mono);font-size:12.5px;color:var(--text-secondary);
     white-space:nowrap}
.aac-tfoot{display:flex;align-items:center;justify-content:flex-end;gap:var(--space-6);
     padding:12px 18px;color:var(--text-secondary);font-size:13px;
     background:var(--surface-card);border-top:1px solid var(--border-subtle);flex-wrap:wrap}
/* A checkbox that is DECORATION until bulk actions land in a later wave. It is
   marked aria-hidden rather than drawn as a real control nobody can tick — a
   checkbox that does nothing is worse than no checkbox. */
.aac-cx{width:18px;height:18px;border:2px solid var(--acg-text-3);border-radius:3px;
     display:inline-block;vertical-align:middle;opacity:.55}
/* ═══ THE GRID — sort, select, page (kit.table) ══════════════════════════════
   Extends the table above: a header cell becomes a sort button, the checkbox is
   a real control, and a selection raises a bulk bar. Same tokens, same rhythm. */
.aac-sortbtn{border:0;background:transparent;padding:0;margin:0;font:inherit;font-size:13px;
     font-weight:500;color:inherit;cursor:pointer;display:inline-flex;align-items:center;
     gap:2px;white-space:nowrap;min-height:24px}
.aac-table thead th[data-sorted] .aac-sortbtn{color:var(--text-primary);font-weight:600}
.aac-sortbtn:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:4px}
@media (hover:hover) and (pointer:fine){ .aac-sortbtn:hover{color:var(--text-primary)} }
.aac-num .aac-sortbtn{margin-left:auto}
/* A real, controlled checkbox (ARIA), state-driven from the unit — no reliance
   on native toggle timing. */
.aac-check{position:relative;border:0;background:transparent;padding:4px;margin:0;cursor:pointer;line-height:0;
     display:inline-flex;border-radius:6px}
.aac-check:focus-visible{outline:2px solid var(--accent);outline-offset:1px}
.aac-check-bx{width:18px;height:18px;border:2px solid var(--acg-text-3);border-radius:4px;
     display:inline-grid;place-items:center;transition:background .12s,border-color .12s}
.aac-check[data-on] .aac-check-bx{background:var(--accent);border-color:var(--accent)}
.aac-check[data-on] .aac-check-bx::after{content:"";width:10px;height:6px;
     border:2px solid var(--on-accent,#fff);border-top:0;border-right:0;
     transform:translateY(-1px) rotate(-45deg)}
:root[data-theme="dark"] .aac-check[data-on] .aac-check-bx::after{border-color:#16181a}
/* The bulk bar rides at the top of the table wrap, inside the frame, so it reads
   as "these rows" not "the page". */
.aac-bulkbar{display:flex;align-items:center;gap:var(--space-4);flex-wrap:wrap;
     padding:10px 16px;border-bottom:1px solid var(--border-subtle);
     background:var(--accent-soft);color:var(--text-primary)}
.aac-bulkbar-n{font-size:13px;font-weight:600}
.aac-bulkbar-acts{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-left:auto}
/* The footer now carries a pager + a rows-per-page picker beside the count. */
.aac-tfoot{justify-content:space-between}
.aac-tfoot-c{color:var(--text-secondary)}
.aac-pager{display:inline-flex;align-items:center;gap:10px}
.aac-pgn{font-variant-numeric:tabular-nums;color:var(--text-secondary)}
.aac-pgbtn{border:1px solid var(--border-subtle);background:var(--surface-card);
     color:var(--text-primary);width:30px;height:30px;border-radius:8px;cursor:pointer;
     font-size:16px;line-height:1;display:inline-grid;place-items:center}
.aac-pgbtn:disabled{opacity:.4;cursor:default}
@media (hover:hover) and (pointer:fine){ .aac-pgbtn:not(:disabled):hover{background:var(--surface-hover)} }
.aac-pgbtn:focus-visible{outline:2px solid var(--accent);outline-offset:1px}
.aac-pagesize{display:inline-flex;align-items:center;gap:6px;font-size:13px;color:var(--text-secondary)}
.aac-pagesize .aac-select{padding:4px 8px;min-height:30px}
/* A caption present for a screen reader, invisible on screen. */
.aac-vh{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
     clip:rect(0 0 0 0);white-space:nowrap;border:0}
/* Schedules grid cells — the tier pill, the compact sub-line, the row actions. */
.aac-gname{font-weight:500;color:var(--text-primary);display:block}
.aac-gsub{display:block;font-size:12px;margin-top:2px;color:var(--text-secondary);
     overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:26ch}
.aac-gacts{display:inline-flex;gap:6px;justify-content:flex-end}
.aac-iconbtn{position:relative;width:32px;height:32px;border:1px solid var(--border-subtle);border-radius:8px;
     background:var(--surface-card);color:var(--text-secondary);cursor:pointer;display:inline-grid;
     place-items:center;padding:0}
.aac-iconbtn svg{width:16px;height:16px}
/* In a grid's action cell the button rides at row height; its 44px touch hit
   area is the ::before, so the visible box shrinks without losing the target. */
.aac-gacts .aac-iconbtn{width:28px;height:28px}
.aac-iconbtn[data-on]{color:var(--acg-ok)}
.aac-iconbtn:disabled{opacity:.5;cursor:default}
@media (hover:hover) and (pointer:fine){
  .aac-iconbtn:hover{background:var(--surface-hover);color:var(--text-primary)}
}
.aac-iconbtn:focus-visible{outline:2px solid var(--accent);outline-offset:1px}

/* ═══ HOME — the landing grid ════════════════════════════════════════════════ */
.aac-hhead{display:flex;align-items:center;gap:18px;padding:2px 4px var(--space-5);
     border-bottom:1px solid var(--border-subtle)}
.aac-hlogo{width:52px;height:52px;border-radius:12px;flex:none;display:grid;place-items:center;
     background:linear-gradient(135deg,#1a73e8,#8ab4f8);color:#fff;font-weight:700;font-size:24px;
     box-shadow:var(--acg-shadow-1);text-transform:uppercase}
.aac-hhead h1{margin:0;font-size:24px;font-weight:500;letter-spacing:-.015em;min-width:0;
     overflow:hidden;text-overflow:ellipsis}
.aac-hhead p{margin:3px 0 0;color:var(--text-secondary);font-size:14px;min-width:0;
     overflow:hidden;text-overflow:ellipsis}
.aac-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:20px;margin-top:22px}
@container (max-width:1180px){ .aac-grid{grid-template-columns:repeat(2,1fr)} }
@container (max-width:760px){ .aac-grid{grid-template-columns:1fr} }
.aac-hcard{background:var(--acg-card);border-radius:12px;padding:22px 24px 24px;
     display:flex;flex-direction:column;min-width:0;
     transition:background 120ms ease,box-shadow 120ms ease}
@media (hover:hover) and (pointer:fine){
  .aac-hcard:hover{background:var(--acg-card-hover);box-shadow:var(--acg-shadow-1)}
}
.aac-hcard-top{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}
/* The card's action never wraps. "View all" broken across two lines reads as
   two links, and the second one looks like it lost its label. */
.aac-hcard-top .aac-tlink{white-space:nowrap;flex:none;min-height:24px}
.aac-hcard h2{margin:0;font-size:20px;font-weight:500;letter-spacing:-.01em}
.aac-hcard-d{color:var(--text-secondary);font-size:13.5px;margin:2px 0 0}
.aac-hcard-stat{margin:18px 0 4px;display:flex;align-items:baseline;gap:10px;flex-wrap:wrap}
.aac-hcard-n{font-size:34px;font-weight:400;letter-spacing:-.02em;font-variant-numeric:tabular-nums;
     line-height:1.1}
.aac-hcard-l{font-size:12px;color:var(--text-secondary);text-transform:uppercase;letter-spacing:.05em}
.aac-hcard-acts{display:flex;flex-direction:column;align-items:flex-start;gap:2px;
     margin-top:var(--space-3)}
.aac-hcard-acts .aac-tlink{font-size:15px;min-height:36px}
.aac-metrics{display:flex;align-items:center;gap:8px;margin-top:14px;flex-wrap:wrap}
.aac-chipm{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:600;
     padding:3px 9px;border-radius:100px;background:var(--surface-subtle);color:var(--text-secondary)}
.aac-chipm i{width:7px;height:7px;border-radius:50%;background:currentColor;flex:none;display:block}
.aac-chipm[data-tone="good"]{background:var(--acg-ok-soft);color:var(--acg-ok)}
.aac-chipm[data-tone="warn"]{background:var(--acg-warn-soft);color:var(--acg-warn)}
.aac-chipm[data-tone="bad"]{background:var(--acg-crit-soft);color:var(--acg-crit)}

/* the alert feed — on the Home card and on the Alerts screen, one shape */
.aac-feed{display:flex;flex-direction:column;margin-top:var(--space-3)}
.aac-fitem{display:flex;gap:12px;padding:14px 0;border-bottom:1px solid var(--border-subtle);
     text-align:left;background:transparent;border-left:0;border-right:0;border-top:0;
     font:inherit;color:inherit;width:100%;cursor:pointer;text-decoration:none}
.aac-fitem:last-child{border-bottom:0;padding-bottom:2px}
.aac-fitem:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:6px}
@media (hover:hover) and (pointer:fine){ .aac-fitem:hover .aac-fitem-t{text-decoration:underline} }
.aac-fitem-i{width:26px;height:26px;border-radius:7px;flex:none;display:grid;place-items:center;
     margin-top:3px;background:var(--surface-subtle);color:var(--text-secondary)}
.aac-fitem-i svg{width:15px;height:15px;stroke:currentColor}
.aac-fitem-i[data-tone="bad"]{background:var(--acg-crit-soft);color:var(--acg-crit)}
.aac-fitem-i[data-tone="warn"]{background:var(--acg-warn-soft);color:var(--acg-warn)}
.aac-fitem-i[data-tone="good"]{background:var(--acg-ok-soft);color:var(--acg-ok)}
.aac-fitem-b{flex:1 1 auto;min-width:0}
/* BLOCK, both of them. They are spans (a button may not hold a div in every
   engine), and two inline spans run together on one line — which is exactly
   how "right now" and "Nothing has failed" arrived as "nowNothing". */
.aac-fitem-t{display:block;font-size:14px;font-weight:500;color:var(--text-primary)}
.aac-fitem-d{display:block;font-size:13px;color:var(--text-secondary);margin-top:2px;
     line-height:1.45}
.aac-fitem-w{font-size:12px;color:var(--text-secondary);white-space:nowrap;flex:none;margin-top:2px}
/* On the card the detail is one line; on the full screen it is the sentence. */
/* Two lines on the card, not one truncated one. A finding's detail is the
   part that says what happened, and the reassurance row is the only thing
   in the card when nothing is wrong -- clipping it mid-word was the worst
   place this rule could land. */
.aac-hcard .aac-fitem-d{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;
     overflow:hidden;white-space:normal}
.aac-spark-cap{font-size:11px;color:var(--text-secondary);letter-spacing:.04em;text-transform:uppercase;
     margin-top:6px}

/* ═══ CONNECTION CARDS ═══════════════════════════════════════════════════════ */
.aac-conngrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));
     gap:16px;margin-top:var(--space-3)}
.aac-conncard{background:var(--surface-card);border:1px solid var(--border-subtle);
     border-radius:12px;padding:18px 18px 16px;display:flex;flex-direction:column;gap:12px;
     cursor:pointer;text-align:left;font:inherit;color:inherit;min-width:0;
     transition:box-shadow 120ms ease}
@media (hover:hover) and (pointer:fine){ .aac-conncard:hover{box-shadow:var(--acg-shadow-1)} }
.aac-conncard:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.aac-conncard-h{display:flex;align-items:center;gap:12px;min-width:0}
.aac-connbadge{width:40px;height:40px;border-radius:10px;flex:none;display:grid;place-items:center;
     color:#fff;font-weight:700;font-size:15px;text-transform:uppercase}
:root[data-theme="dark"] .aac-connbadge{color:#16181a}
/* A brand mark beside a name, in one grid cell — the mark rides at row height,
   not the 40px detail-view size, so the row stays dense. */
.aac-connrow{display:flex;align-items:center;gap:var(--space-2);min-width:0}
.aac-connrow .aac-connbadge{width:26px;height:26px;border-radius:6px;font-size:12px}
.aac-connrow .aac-connbadge svg{width:16px;height:16px}
.aac-conntxt{min-width:0;display:flex;flex-direction:column}
.aac-conncard-n{font-weight:600;font-size:15px;min-width:0;overflow:hidden;
     text-overflow:ellipsis;white-space:nowrap}
.aac-conncard-m{font-size:12.5px;color:var(--text-secondary);min-width:0;overflow:hidden;
     text-overflow:ellipsis;white-space:nowrap}
.aac-conncard-f{display:flex;align-items:center;justify-content:space-between;gap:var(--space-3);
     border-top:1px solid var(--border-subtle);padding-top:12px;margin-top:auto}

/* semantic pills, the console-wide vocabulary */
.aac-tag{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:600;
     padding:3px 10px;border-radius:100px;white-space:nowrap;
     background:var(--surface-subtle);color:var(--text-secondary)}
.aac-tag[data-tone="good"]{background:var(--acg-ok-soft);color:var(--acg-ok)}
.aac-tag[data-tone="warn"]{background:var(--acg-warn-soft);color:var(--acg-warn)}
.aac-tag[data-tone="bad"]{background:var(--acg-crit-soft);color:var(--acg-crit)}

/* ═══ THE DRAWER — where editing happens ═════════════════════════════════════
   The unification thesis in one component. Every editor in this console opens
   HERE, over the list you were reading, in the same chrome — instead of
   navigating to a separate page in a different visual language and losing your
   place in the list you were working through.

   It lives at BODY level, not inside the element. The console rebuilds its own
   innerHTML whenever a count lands or a unit re-renders, and a drawer nested in
   that markup would be destroyed mid-sentence by a background refresh. Body
   level also means no ancestor's overflow or transform can break a fixed
   position. The token block at the top of this file names .aac-drawer-root for
   exactly this reason — it is outside .aac and still has to be the same
   console. */
/* THE STACKING BAND — 9200/9201, and why it is not 1200/1201.

   The drawer declares role="dialog" aria-modal="true" and traps Tab. It has to
   OUTRANK every floating surface on the page or that claim is a lie. The one
   that outranked it was the Ask-Able launcher: able-claw.js pins its collapsed
   pill at z-index 9000, fixed to bottom:20px right:20px — the exact corner this
   panel puts Cancel/Save in. Measured on build (/admin/connections, 1229x2029):
   the Create button rendered at y=1969 and elementFromPoint on its CENTRE
   returned SPAN.aclaw-launcher-pill-text. The button was painted, focusable and
   keyboard-reachable, and a real mouse click opened the chat instead. Gen-1 hit
   this too and paid for it in layout — 84px of dead footer clearance, in every
   drawer, on every viewport, forever. Stacking is the cheaper answer: the panel
   is opaque and 560px wide against the same right edge, so once it outranks the
   pill it OCCLUDES it, and the scrim (inset:0, pointer-events:auto when open)
   takes the clicks that miss. A modal that covers the FAB while it is open is
   the correct reading of aria-modal, not a side effect.

   The band is chosen, not arbitrary. ABOVE the claw's two launcher surfaces
   (panel 8999, pill 9000). BELOW the true-overlay tier — able-tooltip 9999,
   able-datetime-picker's popover 9999, able-file-upload's overlay 10000, the
   claw lightbox 10000 — so a tooltip or a picker raised FROM a drawer control
   still paints over it. The +1 between scrim and panel is the old pair moved up
   one band, unchanged in shape.

   Unconditional, not gated on [data-shown]: a closed drawer cannot steal a click
   at any z-index (the panel is translated fully off-viewport, the scrim is
   opacity:0 + pointer-events:none), and gating would drop the panel back under
   the pill for the 220ms it spends sliding out — the pill would flicker back
   into view mid-animation. Nothing else in this console reaches this band; the
   only other z-index it owns is 1 on a sticky table header. */
.aac-scrim{position:fixed;inset:0;background:var(--acg-scrim);opacity:0;pointer-events:none;
     transition:opacity 180ms ease;z-index:9200}
.aac-scrim[data-shown]{opacity:1;pointer-events:auto}
.aac-drawer{position:fixed;top:0;right:0;height:100dvh;width:min(560px,100%);
     background:var(--surface-card);color:var(--text-primary);font-family:var(--font-sans);
     font-size:14px;line-height:1.5;box-shadow:-8px 0 30px rgba(0,0,0,.18);
     transform:translateX(100%);transition:transform 220ms cubic-bezier(.32,.72,0,1);
     z-index:9201;display:flex;flex-direction:column;
     padding-right:var(--safe-right,0);padding-bottom:var(--safe-bottom,0)}
.aac-drawer[data-shown]{transform:none}
/* The full editors — a rule, a schedule — open WIDE. They are not a two-field
   answer; they are the whole screen the Gen-1 console used to be. */
.aac-drawer[data-xl]{width:min(760px,100%)}
.aac-dh{display:flex;align-items:center;gap:14px;padding:20px 24px;flex:none;
     border-bottom:1px solid var(--border-subtle)}
.aac-dh h2{margin:0;font-size:19px;font-weight:500;flex:1 1 auto;min-width:0;
     overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* 44, not 40: this is the control that DISMISSES an editor, and on a phone it
   sits in the top corner where a thumb is least accurate. Rule 29 3.6. */
.aac-dclose{width:44px;height:44px;border-radius:50%;border:0;background:transparent;
     color:var(--text-secondary);cursor:pointer;display:grid;place-items:center;flex:none}
.aac-dclose svg{width:18px;height:18px;stroke:currentColor}
@media (hover:hover) and (pointer:fine){ .aac-dclose:hover{background:var(--surface-hover)} }
.aac-dclose:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
.aac-db{flex:1 1 auto;overflow-y:auto;padding:22px 24px;display:flex;flex-direction:column;
     gap:18px;-webkit-overflow-scrolling:touch}
/* .aac-search carries "flex:1 1 240px" for the TOOLBAR, where the free space it
   claims is horizontal. The drawer body is a COLUMN, so the same grow claimed the
   free HEIGHT instead: the search row ate the whole panel and the input's
   "align-self:stretch" faithfully filled it — 813px of empty field above the
   results, which reads as a broken drawer. In a column the row is its own height. */
.aac-db .aac-search{flex:0 0 auto}
.aac-df{display:flex;align-items:center;justify-content:flex-end;gap:12px;flex:none;
     padding:16px 24px;border-top:1px solid var(--border-subtle)}
.aac-df[hidden]{display:none}

/* form primitives — one implementation, every unit */
.aac-dfield{display:flex;flex-direction:column;gap:7px;min-width:0}
.aac-dfield > label{font-size:13px;font-weight:600;color:var(--text-primary);
     text-transform:none;letter-spacing:0}
.aac-dfield .hint{font-size:12px;color:var(--text-secondary);font-weight:400}
.aac-dfield input,.aac-dfield select,.aac-dfield textarea{
     font:inherit;font-size:max(16px,14px);color:var(--text-primary);background:var(--surface-card);
     border:1px solid var(--border-default);border-radius:8px;padding:11px 13px;outline:none;
     min-width:0;width:100%;
     transition:border-color 120ms ease,box-shadow 120ms ease}
.aac-dfield input:focus,.aac-dfield select:focus,.aac-dfield textarea:focus{
     border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-soft)}
.aac-dfield input:disabled,.aac-dfield textarea:disabled,.aac-dfield select:disabled{
     color:var(--text-secondary);background:var(--surface-subtle);cursor:default}
.aac-dfield textarea{resize:vertical;min-height:84px;line-height:1.55}
.aac-dfield .mono,.aac-dfield textarea.mono{font-family:var(--font-mono);font-size:13px}
.aac-drow{display:grid;grid-template-columns:1fr 1fr;gap:14px}
@container (max-width:560px){ .aac-drow{grid-template-columns:1fr} }
/* The combobox. The panel expands IN FLOW: the drawer body is a scroll
   container, and a floating panel opened near the bottom of it is clipped by
   that scroll at every width. Nothing here is absolutely positioned. */
.aac-combo{position:relative;display:flex;flex-direction:column;gap:6px;min-width:0}
.aac-combo-t{display:flex;align-items:center;gap:8px;width:100%;min-height:44px;
     font:inherit;font-size:max(16px,14px);text-align:left;color:var(--text-primary);
     background:var(--surface-card);border:1px solid var(--border-default);border-radius:8px;
     padding:9px 11px;cursor:pointer;
     transition:border-color 120ms ease,box-shadow 120ms ease}
.aac-combo-t:focus-visible{outline:none;border-color:var(--accent);
     box-shadow:0 0 0 3px var(--accent-soft)}
.aac-combo[data-shown] .aac-combo-t{border-color:var(--accent)}
.aac-combo-t[disabled]{color:var(--text-secondary);background:var(--surface-subtle);cursor:default}
/* ONE elastic element per line, and it is the name — the thing a person reads
   and chooses by. Letting the identifier grow instead truncates "Cordoba,
   Argentina" to "Cordoba, A…" at 390px while the raw id sits there in full,
   which is the wrong half of the row to protect (rule 28 §2.7). */
.aac-combo-v{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
     font-weight:500}
.aac-combo-v[data-empty]{color:var(--text-subtle);font-weight:400}
.aac-combo-s{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
     font-size:12.5px;color:var(--text-secondary);text-align:right}
.aac-combo-t svg{width:16px;height:16px;flex:none;color:var(--text-subtle)}
.aac-combo-p{display:flex;flex-direction:column;border:1px solid var(--border-default);
     border-radius:8px;background:var(--surface-card);overflow:hidden;
     box-shadow:var(--shadow-sm)}
.aac-combo-p[hidden]{display:none}
.aac-combo-q{border:0!important;border-bottom:1px solid var(--border-subtle)!important;
     border-radius:0!important;box-shadow:none!important;min-height:44px}
.aac-combo-q:focus{outline:none}
.aac-combo-l{position:relative;max-height:min(320px,42dvh);overflow-y:auto;
     -webkit-overflow-scrolling:touch}
.aac-combo-o{display:flex;align-items:center;gap:10px;min-height:44px;padding:8px 12px;
     cursor:pointer;border-bottom:1px solid var(--border-subtle)}
.aac-combo-o:last-child{border-bottom:0}
.aac-combo-o[hidden]{display:none}
.aac-combo-o .ol{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;
     white-space:nowrap;font-size:14px;color:var(--text-primary)}
.aac-combo-o .os{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;
     white-space:nowrap;font-size:12px;color:var(--text-secondary);text-align:right}
/* On a phone there is not enough width for a name and an identifier on one
   line — "Use this device's time zone" alone is 27 characters. Stack them and
   neither is cut. A media query, not a container one: the drawer is fixed to
   the viewport and is NOT inside the pane's container, which is why the
   "@container" rule two blocks up never fires in here. */
@media (max-width:560px){
  .aac-combo-t,.aac-combo-o{flex-wrap:wrap;row-gap:1px}
  /* The order property keeps the chevron on the first line with the name — the
     sub-line is a full-width flex item, so in DOM order it would push the
     chevron down onto a third line of its own. */
  .aac-combo-s,.aac-combo-o .os{flex:1 0 100%;text-align:left;max-width:100%;order:1}
  .aac-combo-t{align-items:flex-start;padding-top:7px;padding-bottom:7px}
  .aac-combo-v{flex:1 1 calc(100% - 24px)}
  .aac-combo-t svg{margin-top:2px}
  .aac-combo-o{align-items:flex-start;padding-top:7px;padding-bottom:7px}
}
.aac-combo-o[data-hi]{background:var(--surface-hover)}
.aac-combo-o[aria-selected="true"] .ol{font-weight:600;color:var(--acg-accent-ink)}
.aac-combo-o[aria-selected="true"]{background:var(--accent-soft)}
.aac-combo-o[aria-selected="true"][data-hi]{background:var(--accent-soft);
     box-shadow:inset 2px 0 0 var(--accent)}
@media (hover:hover) and (pointer:fine){ .aac-combo-o:hover{background:var(--surface-hover)} }
.aac-combo-m{margin:0;padding:9px 12px;font-size:12px;color:var(--text-secondary);
     border-top:1px solid var(--border-subtle);background:var(--surface-subtle)}
.aac-combo-m[hidden]{display:none}
.aac-seg{display:flex;gap:6px;flex-wrap:wrap}
.aac-seg .opt{border:1px solid var(--border-default);border-radius:8px;padding:9px 14px;
     font:inherit;font-size:13px;font-weight:500;color:var(--text-secondary);cursor:pointer;
     background:var(--surface-card);min-height:44px;display:inline-flex;align-items:center}
.aac-seg .opt:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.aac-seg .opt[data-sel]{border-color:var(--accent);background:var(--accent-soft);color:var(--acg-accent-ink)}
.aac-seg .opt[disabled]{opacity:.55;cursor:default}
/* A switch is a real button with a real pressed state — a div with a class is
   invisible to a keyboard and silent to a screen reader. */
.aac-swrow{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;
     padding:10px 0;border-bottom:1px solid var(--border-subtle)}
.aac-swrow:last-of-type{border-bottom:0}
.aac-swrow .sl{font-size:14px;font-weight:600;display:block}
.aac-swrow .sh{font-size:12.5px;color:var(--text-secondary);margin-top:2px;line-height:1.5;
     max-width:52ch}
/* The Phone row's two lanes: one switch each, labelled, side by side. */
.aac-sw-pair{display:flex;align-items:center;gap:var(--space-4)}
.aac-sw-lane{display:flex;align-items:center;gap:8px;font-size:var(--font-sm);color:var(--text-secondary);cursor:pointer}
.aac-sw{width:40px;height:22px;border-radius:100px;background:var(--border-default);
     position:relative;cursor:pointer;flex:none;margin-top:2px;border:0;padding:0;
     transition:background 150ms ease}
/* THE TARGET, not the track. A switch reads best small — 40x22 is the size the
   rest of this console is drawn to — but 22px tall is half of the 44px a finger
   needs (rule 29 3.6), and growing the track to 44 would make every governance
   row look like a toggle farm. So the HIT AREA grows and the paint does not:
   a centred 44x44 pseudo-element, absolutely positioned so it changes no
   layout. The switch sits in a ~60px row, so this stays inside its own row. */
.aac-sw::before{content:"";position:absolute;top:50%;left:50%;width:44px;height:44px;
     transform:translate(-50%,-50%)}
.aac-sw::after{content:"";position:absolute;top:3px;left:3px;width:16px;height:16px;
     border-radius:50%;background:#fff;transition:left 150ms ease;
     box-shadow:0 1px 2px rgba(0,0,0,.3)}
.aac-sw[aria-checked="true"]{background:var(--accent)}
.aac-sw[aria-checked="true"]::after{left:21px}
.aac-sw:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.aac-sw[disabled]{opacity:.5;cursor:default}
.aac-dsec{border-top:1px solid var(--border-subtle);padding-top:16px;display:flex;
     flex-direction:column;gap:10px;min-width:0}
.aac-dsec:first-child{border-top:0;padding-top:0}
.aac-dsec h4{margin:0 0 2px;font-size:11.5px;letter-spacing:.06em;text-transform:uppercase;
     color:var(--text-secondary);font-weight:600}
.aac-dsec .aac-tlink{align-self:flex-start;min-height:36px}
.aac-tlink[data-danger]{color:var(--acg-crit)}
/* Governance microcopy — the sentence that says what a control will DO. It is
   the console's whole voice contract in one class: what this means for you,
   never how the platform works. */
.aac-dnote{font-size:12.5px;color:var(--text-secondary);border-left:3px solid var(--accent);
     padding:4px 0 4px 12px;line-height:1.55;background:transparent}
.aac-dnote b{color:var(--text-primary)}
.aac-dnote[data-tone="warn"]{border-left-color:var(--acg-warn)}
.aac-dnote[data-tone="bad"]{border-left-color:var(--acg-crit)}
/* The refusal reuses dnote's paint on purpose — a unit should not have to
   learn a second visual language for "this came back". It only asks for a
   little more room, because it ARRIVES: it was not there a moment ago. */
.aac-refusal{padding-top:8px;padding-bottom:8px;overflow-wrap:anywhere}
.aac-dtabs{display:flex;gap:2px;border-bottom:1px solid var(--border-subtle);
     overflow-x:auto;scrollbar-width:none}
.aac-dtab{border:0;background:transparent;padding:9px 14px;font:inherit;font-size:13.5px;
     font-weight:600;color:var(--text-secondary);cursor:pointer;border-bottom:2px solid transparent;
     margin-bottom:-1px;border-radius:8px 8px 0 0;white-space:nowrap;min-height:42px}
@media (hover:hover) and (pointer:fine){ .aac-dtab:hover{background:var(--surface-hover)} }
.aac-dtab:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
.aac-dtab[data-on]{color:var(--acg-accent-ink);border-bottom-color:var(--accent)}
.aac-dpane{display:none;flex-direction:column;gap:16px;padding-top:14px}
.aac-dpane[data-on]{display:flex}
.aac-hist{display:flex;align-items:center;gap:12px;padding:9px 0;
     border-bottom:1px solid var(--border-subtle);font-size:13.5px;min-width:0}
.aac-hist:last-of-type{border-bottom:0}
.aac-hist-tx{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.aac-hist-w{margin-left:auto;color:var(--text-secondary);font-variant-numeric:tabular-nums;
     white-space:nowrap;flex:none}
.aac-prov{font-size:12px;color:var(--text-secondary);border-top:1px solid var(--border-subtle);
     padding-top:14px;line-height:1.7;overflow-wrap:anywhere}
.aac-prov b{color:var(--text-secondary)}
.aac-codebox{background:var(--surface-subtle);border:1px solid var(--border-subtle);
     border-radius:8px;padding:14px 16px;font-family:var(--font-mono);font-size:12.5px;
     line-height:1.6;color:var(--text-primary);overflow-x:auto;white-space:pre;min-width:0}
.aac-codebox[contenteditable]{cursor:text}
.aac-codebox[contenteditable]:focus{outline:none;border-color:var(--accent);
     box-shadow:0 0 0 3px var(--accent-soft)}
/* What the notification will look like on a phone. A push is written blind
   otherwise — the title is bold, the OS already shows the app name, and that is
   only obvious when you can see it. */
.aac-pushprev{background:var(--surface-subtle);border:1px solid var(--border-subtle);
     border-radius:14px;padding:12px 14px;max-width:400px;min-width:0}
.aac-pp-a{display:flex;align-items:center;gap:6px;font-size:11px;letter-spacing:.04em;
     color:var(--text-secondary);text-transform:uppercase}
.aac-pp-i{width:16px;height:16px;border-radius:4px;flex:none;
     background:linear-gradient(135deg,#1a73e8,#8ab4f8);color:#fff;font-size:10px;font-weight:700;
     display:grid;place-items:center}
.aac-pp-t{font-weight:600;font-size:13.5px;margin-top:6px;overflow-wrap:anywhere}
.aac-pp-b{font-size:13px;color:var(--text-secondary);margin-top:2px;overflow-wrap:anywhere}
.aac-did{display:flex;align-items:center;gap:14px;min-width:0}
.aac-did-w{font-weight:600;font-size:16px;min-width:0;overflow:hidden;text-overflow:ellipsis}
.aac-did-s{color:var(--text-secondary);font-size:13px;margin-top:2px}
@media (prefers-reduced-motion:reduce){
  .aac-drawer,.aac-scrim,.aac-sw,.aac-sw::after{transition:none}
}

/* ═══ COLLAPSE — container queries, so a narrow SLOT collapses like a narrow
   screen, and a 375px phone is genuinely this same CSS. ═══ */
@container (max-width: 860px){
  /* One column: the painted rail column would run straight down the middle.
     minmax(0,...) rather than a bare 1fr, because a bare fr floors at the
     MIN-CONTENT of what is inside it -- and one unbreakable 60-character file
     path stretched this track to 1384px inside a 375px phone. The page area
     clips instead of scrolling, so a third of every row simply could not be
     reached. The desktop track has always carried the guard; the narrow one
     dropped it, and nothing had a long enough string to show it until now. */
  .aac-body{grid-template-columns:minmax(0,1fr);background:none}
  .aac-rail{align-self:stretch;background:var(--surface-card);
       border-bottom:1px solid var(--border-default);padding:var(--space-2) var(--space-3)}
  /* One strip across the top: nothing to pin, and the sections scroll sideways. */
  .aac-rail-in{position:static;flex-direction:row;align-items:center;overflow-x:auto;
       overflow-y:visible;max-height:none;gap:var(--space-2);scrollbar-width:none}
  /* The strip clips its last sections; the fade on the right edge is the only sign
     there is more, and it shows only while something is actually tucked away. */
  .aac-rail{position:relative}
  .aac-rail.is-more::after{content:"";position:absolute;top:0;right:0;bottom:0;width:44px;
       pointer-events:none;background:linear-gradient(to right,transparent,var(--surface-card))}
  .aac-rail-cap{display:none}
  .aac-rail-item{width:auto;flex:none;white-space:nowrap}
  .aac-rail-plat{margin-top:0;padding-top:0;border-top:0;flex:none;display:flex;
       align-items:center;gap:var(--space-2)}
  .aac-rail-sub{flex-direction:row;margin-top:0;gap:var(--space-2)}
  /* Full-size targets on a touch screen: the quieter rail items and the small
     buttons are only allowed to be small where there is a mouse. */
  .aac-rail-sub .aac-rail-item{min-height:44px}
  .aac-btn--sm{min-height:44px;padding:0 var(--space-3)}
  .aac-pane{padding:var(--space-4) var(--space-4) var(--space-8)}
  /* The rail is a horizontal strip up here, so the pills lose their side
     margins and the platform group sits in line with everything else. */
  .aac-rail-item{margin:0}
  .aac-rail-sub .aac-rail-item{padding-left:14px}
  .aac-grid{gap:14px}
  .aac-hhead{gap:14px}
  .aac-hlogo{width:44px;height:44px;font-size:20px}
  .aac-hhead h1{font-size:20px}
}
@container (max-width: 560px){
  /* Line 1 keeps the name alone; the status chip drops to the head of line 2
     and the right column moves to its tail. No truncation, no idle column. */
  .aac-row{flex-direction:column}
  .aac-row-main{padding:var(--space-3)}
  .aac-l1 .aac-st{display:none}
  .aac-l2 .aac-st{display:inline-flex;order:-1}
  .aac-row-side{flex-direction:row;align-items:center;gap:var(--space-2);
       justify-content:flex-start;border-left:0;border-top:1px solid var(--border-subtle);
       padding:var(--space-2) var(--space-3);text-align:left;width:100%}
  .aac-row-side:empty{border-top-color:transparent;padding:0}
  .aac-name{white-space:normal;overflow:visible;text-overflow:clip}
  /* Line 2 wraps here for the same reason the name does: there is room below a
     row on a phone and none beside it, so a fact that does not fit should take
     another line rather than be cut off at the edge. */
  .aac-l2 .aac-f{white-space:normal;overflow:visible;text-overflow:clip}
  .aac-head{gap:var(--space-3)}
  .aac-facts{grid-template-columns:1fr}
  .aac-tabs{overflow-x:auto;scrollbar-width:none}
  .aac-kv div{flex-direction:column;gap:2px}
  .aac-kv dt{width:auto}
  /* Search takes the whole row so the filters share the next one evenly.
     Sharing a row, the search box crowds the first chip up beside it and
     strands the rest on a second line with a gap where the box was. */
  .aac-search{flex:1 1 100%}
  .aac-chip{flex:1 1 auto;justify-content:center}
  /* The unit bar stacks: the title, then its actions on their own line where
     they have room to be 44px targets rather than crowded links. */
  .aac-unitbar{gap:var(--space-2)}
  .aac-unitbar-t{font-size:20px;flex:1 1 100%}
  .aac-unitacts{gap:var(--space-4);flex:1 1 100%}
  .aac-hcard{padding:18px 18px 20px}
  .aac-hcard-n{font-size:30px}
  /* Full-size targets where there is a finger. The card links are allowed to
     be 24-36px only where there is a mouse — measured 24px on a phone, which
     is half a tap target. */
  .aac-hcard .aac-tlink,.aac-hcard-top .aac-tlink,.aac-hcard-acts .aac-tlink{min-height:44px}
  .aac-hcard-acts{gap:0}
  /* A table has no card fallback yet, so it scrolls sideways inside its own
     box — reachable, and never pushing the document sideways with it. */
  .aac-table{min-width:640px}
  .aac-tfoot{justify-content:flex-start;gap:var(--space-4)}
}
@container (min-width: 561px){ .aac-l2 .aac-st{display:none} }

@media (prefers-reduced-motion: reduce){
  .aac-skel{animation:none}
  .aac-row,.aac-btn,.aac-rail-item,.aac-tab{transition:none}
}

/* ── measurement (acv-*) — the analytics console's own vocabulary ──
   In THIS literal on purpose. The bundle extractor pulls one chrome block per
   blueprint into able-runtime.css; a second blueprint would mean a second block
   that can drift from this one. One shell, one stylesheet, two consoles.
   Canonical semantic tokens only — no --grey-N, which is mode-INDEPENDENT and
   goes invisible in whichever theme it was not authored in. */

/* the tile row. Grid, never flex. Container queries, so the rail's width is
   what decides the columns rather than the browser window's. */
.acv-tiles{display:grid;gap:var(--space-3);grid-template-columns:1fr;
     margin-bottom:var(--space-6)}
@container (min-width:520px){ .acv-tiles{grid-template-columns:repeat(2,1fr)} }
@container (min-width:800px){ .acv-tiles{grid-template-columns:repeat(3,1fr)} }
@container (min-width:1040px){ .acv-tiles{grid-template-columns:repeat(5,1fr)} }
/* Six is the assistant unit when it has a file-delivery number to report. Five
   columns would strand the sixth tile alone on a second row, so the wide band
   goes to two balanced rows of three until there is honest room for six across.
   Keyed off an attribute the unit sets, so no other unit's row moves. */
@container (min-width:1040px){ .acv-tiles[data-cols="6"]{grid-template-columns:repeat(3,1fr)} }
@container (min-width:1320px){ .acv-tiles[data-cols="6"]{grid-template-columns:repeat(6,1fr)} }
.acv-tile{border:1px solid var(--border-default);border-radius:var(--radius-lg);
     padding:var(--space-4);background:var(--surface-card);display:grid;
     gap:var(--space-1);align-content:start;min-width:0}
.acv-tile-v{font-size:var(--font-2xl);font-weight:640;letter-spacing:-.02em;
     line-height:1.1;font-variant-numeric:tabular-nums;min-width:0;
     overflow-wrap:anywhere}
.acv-tile-l{font-size:var(--font-xs);font-weight:700;text-transform:uppercase;
     letter-spacing:.06em;color:var(--text-muted)}
.acv-tile-n{font-size:var(--font-xs);color:var(--text-muted);
     font-variant-numeric:tabular-nums}
/* The toned note. --success / --danger are TENANT tokens, and a tenant is free
   to pick a bright one: fryaway@asuite's light --danger (#E34800) measures
   4.04:1 on the card, under AA for this 11-12px line, while the same rule reads
   6.47:1 on the tenants that keep the platform red. The tone is the POINT here,
   so it is not dropped for a neutral -- it is mixed toward --text-primary, which
   every theme re-binds per mode. That is hue-preserving (this stays plainly red
   or plainly green) and its floor stops depending on the tenant's choice:
   fryaway 4.04 to 6.52:1, and the tenants that already passed only get safer
   (fleet 6.47 to 9.34:1 light, 6.53 to 8.66:1 dark). Colour still carries the
   tone; it no longer carries the legibility. */
.acv-tile-n[data-tone="good"]{color:color-mix(in srgb, var(--success) 70%, var(--text-primary))}
.acv-tile-n[data-tone="bad"]{color:color-mix(in srgb, var(--danger) 70%, var(--text-primary))}
/* NOT MEASURED is grey and says so in words. Never a red zero, never $0.00. */
.acv-tile[data-tone="none"] .acv-tile-v{color:var(--text-muted);font-weight:500;
     font-size:var(--font-lg)}
.acv-none{color:var(--text-muted)}

/* the sparkline. One path, geometry from the endpoint. */
.acv-spark{width:100%;height:26px;display:block;margin-top:var(--space-2);
     overflow:visible}
.acv-spark path{fill:none;stroke:var(--acv-series);stroke-width:1.6;
     vector-effect:non-scaling-stroke;stroke-linejoin:round;stroke-linecap:round}

/* cards + the per-day bars */
.acv-card{border:1px solid var(--border-default);border-radius:var(--radius-lg);
     background:var(--surface-card);padding:var(--space-5);margin-bottom:var(--space-5)}
.acv-card-head{display:flex;align-items:baseline;justify-content:space-between;
     gap:var(--space-3);margin-bottom:var(--space-4);flex-wrap:wrap}
.acv-card-t{font-size:var(--font-base);font-weight:640;margin:0}
.acv-card-n{font-size:var(--font-xs);color:var(--text-muted);margin:2px 0 0;max-width:62ch}
.acv-legend{display:flex;gap:var(--space-4);font-size:var(--font-xs);
     color:var(--text-muted);flex-wrap:wrap}
.acv-legend i{display:inline-block;width:9px;height:9px;border-radius:2px;
     margin-right:5px;vertical-align:-1px;background:var(--acv-series)}
.acv-legend i[data-tone="bad"]{background:var(--danger)}
/* Amber is HELD BACK — a mute, a quiet hour, a duplicate the system caught. It is
   the system working, so it must never be drawn in the colour of a failure. */
.acv-legend i[data-tone="warn"]{background:var(--warning)}
/* An out-link in an analytics pane, capped to the same 58rem the lists use.
   ".aac-out" is a full-width flex row with its arrow pushed to the far edge —
   which is right in the admin rail's narrow pane and, at 1440px here, puts a
   thousand pixels between a label and the arrow that belongs to it.
   (NO BACKTICKS in this comment, or in any other one below this line: it lives
   inside a backtick template literal, so one closes the literal and the rest of
   the stylesheet is parsed as JavaScript.) */
.acv-out{max-width:58rem}
/* A list whose NAME is a sentence rather than a label.
   .acv-name is nowrap-with-ellipsis, which is right for a channel or a title and
   wrong for "the address is not on the allowed list for that channel": measured at
   375px, every row of the reasons list clipped, by 53 to 148px, and the clipped
   part is the part that says what happened. Prose wraps; labels still truncate. */
.acv-prose .acv-name{white-space:normal;overflow:visible;text-overflow:clip}
.acv-prose .acv-row{align-items:start}
.acv-plot{height:132px;width:100%;display:block}
.acv-plot rect{fill:var(--acv-series)}
.acv-plot rect.acv-b-warn{fill:var(--warning)}
.acv-plot rect.acv-b-fail{fill:var(--danger)}
/* The per-day hover target. It MUST out-specify ".acv-plot rect" above, which is
   why it is written as a compound rather than as a fill attribute on the element:
   a presentation attribute loses to any rule that matches, so fill="transparent"
   in the markup would paint every column solid in the series colour and bury the
   chart under its own tooltips. Transparent still takes the pointer; fill:none
   would not. */
.acv-plot rect.acv-hit{fill:transparent}
/* The scale line above a plot — the tallest day, so the bars have units. Right
   over the end of the axis, muted, and tabular so it does not jitter between
   windows. */
.acv-scale{font-size:var(--font-xs);color:var(--text-muted);text-align:right;
     margin-bottom:3px;font-variant-numeric:tabular-nums}
.acv-axis{display:flex;justify-content:space-between;font-size:var(--font-xs);
     color:var(--text-muted);margin-top:var(--space-2);font-variant-numeric:tabular-nums}

/* the top-N list with its share bar.

   The max-width is load-bearing, not taste. These rows put an elastic NAME on the
   left and its number on the right, and on a wide pane the name column absorbs
   every pixel of slack: measured on a 1440-wide screen the name column was 1030px
   holding 62px of text, leaving 984px between "Data read" and the number that
   belongs to it. A row that wide is not a row, it is two things on the same line.
   Capping the list keeps a name and its count inside one glance; the chart above
   still gets the full pane, because a chart is the one thing that wants it. */
.acv-list{display:grid;gap:1px;background:var(--border-subtle);
     border:1px solid var(--border-subtle);border-radius:var(--radius-md);overflow:hidden;
     max-width:58rem}
.acv-row{display:grid;grid-template-columns:minmax(0,1fr) auto auto;gap:var(--space-4);
     align-items:center;padding:9px var(--space-4);background:var(--surface-card);min-width:0}
@media (hover:hover) and (pointer:fine){ .acv-row:hover{background:var(--surface-hover)} }
/* The name is the ONE elastic element; everything beside it is intrinsic. */
.acv-name{min-width:0;font-size:var(--font-sm);font-weight:500;overflow:hidden;
     text-overflow:ellipsis;white-space:nowrap}
.acv-name small{display:block;font-size:var(--font-xs);color:var(--text-muted);
     font-weight:400;text-transform:none;letter-spacing:0;overflow:hidden;
     text-overflow:ellipsis}
/* 9 tabular characters — budgeted for 1,289,082, not for the 3 digits a typical
   app shows. The tail rows are the ones somebody is hunting for. */
.acv-n{font-size:var(--font-sm);font-variant-numeric:tabular-nums;text-align:right;
     min-width:9ch;color:var(--text-primary)}
.acv-sub{font-size:var(--font-xs);color:var(--text-muted);
     font-variant-numeric:tabular-nums;text-align:right;min-width:8ch}
.acv-share{grid-column:1 / -1;height:3px;border-radius:var(--radius-full);
     background:var(--surface-subtle);overflow:hidden;margin-top:2px}
.acv-share i{display:block;height:100%;background:var(--acv-series);border-radius:inherit}
.acv-kind{display:inline-block;font-size:10px;font-weight:700;text-transform:uppercase;
     letter-spacing:.05em;padding:1px 6px;border-radius:var(--radius-full);
     border:1px solid var(--border-default);color:var(--text-muted);
     margin-right:var(--space-2);vertical-align:1px}

/* needs-a-look. Rendered only when it has something in it. */
.acv-look{border:1px solid var(--border-default);border-left:3px solid var(--warning);
     border-radius:var(--radius-md);background:var(--surface-card);padding:var(--space-4);
     display:grid;gap:var(--space-2);margin-bottom:var(--space-5);max-width:58rem}
.acv-look-t{font-size:var(--font-sm);font-weight:640}
.acv-look a{display:flex;align-items:center;justify-content:space-between;
     gap:var(--space-3);font-size:var(--font-sm);color:var(--text-secondary);
     text-decoration:none;padding:5px 0;border-top:1px solid var(--border-subtle);
     min-height:var(--tap-target,44px)}
.acv-look a:first-of-type{border-top:0}
/* A finding with no screen to open is text, not a dead link. */
.acv-look-x{font-size:var(--font-sm);color:var(--text-secondary);margin:0;
     padding:5px 0;border-top:1px solid var(--border-subtle)}
.acv-look-t + .acv-look-x{border-top:0}
/* The action word is --text-primary, NOT --accent. Measured: --accent (#2563eb)
   on the dark card (#161b22) is 3.35:1, which fails AA for 12px text — the accent
   does not re-bind between modes, so it is fine on white and dim on black. It was
   also the wrong colour by this console's own rule, where colour carries exactly
   one meaning (accent = the series being measured) and never decorates. The arrow
   and the anchor carry the affordance; 14.64:1 carries the words. */
.acv-look a span:last-child{color:var(--text-primary);font-weight:600;
     font-size:var(--font-xs);flex:none}
@media (hover:hover) and (pointer:fine){
  .acv-look a:hover span:first-child{color:var(--text-primary)}
}

/* A finding may carry its own one-line explanation under it — "a tool it used
   failed" is the headline, "the assistant answered, but something it reached for
   did not work" is what that means. Same treatment the top-N rows give a note. */
.acv-look-x small{display:block;font-size:var(--font-xs);color:var(--text-muted);
     margin-top:2px}

/* A panel can now hold rows that are NOT anybody's problem — "985 turns hit a
   snag and recovered" is there so three buckets add up to one flagged total.
   Two rules keep that honest rather than alarming:

     calm   every row is explanatory, so the warning stripe comes off. The
            border is the loudest thing on the panel and it would be shouting
            about nothing.
     bad    the rows that ARE somebody's problem say so in colour. --danger
            re-binds between modes (#b91c1c light / #f85149 dark), which measures
            9.1:1 on the light card and 5.8:1 on the dark one — AA either way.
            The explanation under it keeps its own muted colour and weight, so
            the emphasis lands on the finding and not on its footnote. */
.acv-look[data-tone="calm"]{border-left-color:var(--border-strong)}
.acv-look-x[data-tone="bad"]{color:var(--danger);font-weight:600}
.acv-look-x[data-tone="bad"] small{font-weight:400}

/* Two cards abreast where the pane is wide enough for both to stay readable.
   Generic on purpose — any unit with sibling breakdowns wants this and none of
   them should invent its own grid.

   The lists inside keep their 58rem cap rather than overriding it: in two columns
   the cap never binds, and in ONE column (a narrow pane) it is the thing that
   stops a name and its count drifting to opposite ends of the row. */
/* align-items:start is the load-bearing word. A grid stretches its items to the
   tallest in the row by default, so a one-row breakdown beside an eight-row one
   grew to match it: measured 735px of card holding 161px of list, 574px of white
   under a single line. Two cards side by side are not one card; each is its own
   height. */
.acv-cols{display:grid;gap:var(--space-5);grid-template-columns:1fr;
     align-items:start;margin-bottom:var(--space-5)}
@container (min-width:860px){ .acv-cols{grid-template-columns:repeat(2,1fr)} }
.acv-cols > .acv-card{margin-bottom:0}

/* A row of measurements inside a card — four numbers that answer one question
   together, so they share a scale and sit on one line where there is room.
   Not tiles: tiles are the page's headline and these are a detail of one card. */
.acv-stats{display:grid;gap:var(--space-5);grid-template-columns:1fr}
@container (min-width:520px){ .acv-stats{grid-template-columns:repeat(2,1fr)} }
@container (min-width:960px){ .acv-stats{grid-template-columns:repeat(4,1fr)} }
.acv-stat{min-width:0}
.acv-stat-l{font-size:var(--font-xs);font-weight:700;text-transform:uppercase;
     letter-spacing:.06em;color:var(--text-muted);margin-bottom:var(--space-1)}
.acv-stat-v{font-size:var(--font-xl);font-weight:640;letter-spacing:-.01em;
     line-height:1.15;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}
/* Not measured is grey and says so in words, at a smaller size — the same rule
   the tiles keep, so an absent number never arrives looking like a failure. */
.acv-stat-v[data-tone="none"]{color:var(--text-muted);font-weight:500;
     font-size:var(--font-base)}
.acv-stat-n{font-size:var(--font-xs);color:var(--text-muted);margin-top:2px}

/* the two-lane divider: the machine lane is visibly a different population */
.acv-lane{margin-top:var(--space-8);padding-top:var(--space-6);
     border-top:1px dashed var(--border-strong)}
.acv-lane-t{font-size:var(--font-xs);font-weight:700;text-transform:uppercase;
     letter-spacing:.07em;color:var(--text-muted);margin:0 0 var(--space-1)}
.acv-lane-n{font-size:var(--font-sm);color:var(--text-secondary);
     margin:0 0 var(--space-4);max-width:58ch}

/* the honest empty state. Grey, explanatory, never an error.
   --text-muted measures 4.83:1 on white but 4.41:1 on this tinted panel, which
   fails AA — measured, not assumed, so the body steps up to --text-secondary. */
.acv-empty{border:1px dashed var(--border-strong);border-radius:var(--radius-lg);
     padding:var(--space-10) var(--space-6);text-align:center;color:var(--text-muted);
     background:var(--surface-subtle)}
.acv-empty h4{font-size:var(--font-base);font-weight:640;color:var(--text-secondary);
     margin:0 0 var(--space-2)}
.acv-empty p{font-size:var(--font-sm);color:var(--text-secondary);margin:0 auto;max-width:54ch}
.acv-empty p + p{margin-top:var(--space-3)}

/* the estimate marker. Money renders no other way. */
.acv-est{font-size:var(--font-xs);font-weight:600;color:var(--text-muted);
     text-transform:none;letter-spacing:0;margin-left:5px}
.acv-foot{font-size:var(--font-xs);color:var(--text-muted);margin-top:var(--space-4);
     padding-top:var(--space-3);border-top:1px solid var(--border-subtle);max-width:58ch}

@container (max-width:620px){
  /* The percentage drops before the name is ever allowed to truncate — the
     elastic element keeps its width. */
  .acv-row{grid-template-columns:minmax(0,1fr) auto}
  .acv-sub{display:none}
}

/* ── notifications section: list cells, filter chips, editor sentence + preview ── */
.aac-head-sub{margin:var(--space-2) 0 0;font-size:var(--font-sm);line-height:1.5;
     color:var(--text-secondary);max-width:74ch}
.aac-head-acts{flex:0 0 auto;display:flex;gap:var(--space-2);align-items:center}
.aac-chiprow{display:flex;flex-wrap:wrap;align-items:center;gap:var(--space-2) var(--space-3);margin:0 0 var(--space-4)}
/* A filter-chip group — one dimension, its chips at a uniform gap, behind a
   discreet caption. Groups wrap as whole units, so a narrow screen never
   ladders. The caption speaks in the same small-caps voice the detail's field
   labels use, so it reads as a label FOR the chips, not one OF them. */
.aac-chipgrp{display:inline-flex;flex-wrap:wrap;align-items:center;gap:var(--space-2)}
.aac-chipgrp-l{font-size:11px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;
     color:var(--text-muted);white-space:nowrap;align-self:center}
.aac-cellmain{display:flex;flex-direction:column;gap:2px;min-width:0}
.aac-cellnm{font-weight:600;color:var(--text-primary);overflow:hidden;text-overflow:ellipsis;
     white-space:nowrap}
.aac-cellsub{font-size:var(--font-xs);color:var(--text-secondary);line-height:1.45;
     display:block;overflow:hidden;text-overflow:ellipsis}
.aac-muted{color:var(--text-muted)}
.aac-chips{display:inline-flex;flex-wrap:wrap;gap:4px}
.aac-dsent{margin:0 0 var(--space-4);padding:var(--space-3) var(--space-4);
     background:var(--surface-subtle);border:1px solid var(--border-subtle);
     border-radius:var(--radius-md);font-size:var(--font-sm);line-height:1.6;
     color:var(--text-secondary)}
.aac-dsent b{color:var(--text-primary);font-weight:600}
.aac-dpreview-row{display:flex;gap:var(--space-3);align-items:flex-start;
     padding:var(--space-2) 0;border-top:1px solid var(--border-subtle)}
.aac-dpreview-row:first-child{border-top:0;padding-top:0}
.aac-dpreview-lab{flex:0 0 68px;font-size:var(--font-xs);font-weight:600;
     text-transform:uppercase;letter-spacing:.03em;color:var(--text-muted);padding-top:2px}
.aac-dpreview-tx{flex:1 1 auto;min-width:0;font-size:var(--font-sm);line-height:1.5;
     color:var(--text-primary);white-space:pre-wrap;word-break:break-word}

/* ── analytics (anv-*) — the Analytics group's own vocabulary: a period bar, KPI
   tiles with a delta and a sparkline, one day chart with a crosshair and a
   tooltip, breakdown cards, a compact table. Tokens only. ── */
.anv-bar{display:flex;flex-wrap:wrap;align-items:center;gap:var(--space-2);margin:0 0 var(--space-4)}
.anv-lede{flex:1 1 100%;margin:0;font-size:var(--font-sm);color:var(--text-muted);max-width:78ch}
.anv-period{display:inline-flex;gap:2px;padding:0;border:1px solid var(--border-subtle);border-radius:var(--radius-full);background:var(--surface-subtle)}
.anv-period button{display:inline-flex;align-items:center;min-height:var(--tap-target,44px);padding:0 14px;border:0;border-radius:var(--radius-full);background:transparent;font:inherit;font-size:var(--font-sm);font-weight:600;color:var(--text-secondary);cursor:pointer}
.anv-period button[data-on]{background:var(--surface-card);color:var(--text-primary);box-shadow:var(--shadow-sm)}
.anv-period button:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.anv-vs{font-size:var(--font-sm);color:var(--text-muted)}
.anv-kpis{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(150px,100%),1fr));gap:var(--space-3);margin-bottom:var(--space-4)}
.anv-kpi{padding:12px 14px;border:1px solid var(--border-subtle);border-radius:var(--radius-lg);background:var(--surface-card);min-width:0}
.anv-kpi-l{font-size:var(--font-xs);font-weight:600;letter-spacing:.02em;text-transform:uppercase;color:var(--text-muted);display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden;line-height:1.3}
.anv-kpi-v{margin-top:4px;font-size:var(--font-xl);font-weight:700;letter-spacing:-.01em;font-variant-numeric:tabular-nums;color:var(--text-primary);line-height:1.15}
.anv-kpi-v[data-tone=none]{color:var(--text-muted);font-weight:600;font-size:var(--font-md)}
.anv-kpi-d{display:inline-flex;align-items:center;gap:4px;margin-top:4px;font-size:var(--font-xs);font-weight:700;font-variant-numeric:tabular-nums;color:var(--text-muted)}
.anv-kpi-d[data-dir=up]{color:var(--success)}.anv-kpi-d[data-dir=down]{color:var(--danger)}
.anv-kpi-d[data-bad=up]{color:var(--danger)}.anv-kpi-d[data-bad=down]{color:var(--success)}
.anv-kpi-n{margin-top:2px;font-size:var(--font-xs);color:var(--text-muted);display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden;line-height:1.35}
.anv-kpi-n[data-tone=bad]{color:var(--danger)}.anv-kpi-n[data-tone=good]{color:var(--success)}
.anv-spark{display:block;width:100%;height:28px;margin-top:6px;overflow:visible}
.anv-spark path{fill:none;stroke:var(--accent);stroke-width:1.6;vector-effect:non-scaling-stroke}
.anv-spark .fill{fill:var(--accent-soft);stroke:none;opacity:.7}
.anv-spark circle{fill:var(--accent);display:none}
.anv-card{border:1px solid var(--border-subtle);border-radius:var(--radius-lg);background:var(--surface-card);padding:14px 16px;margin-bottom:var(--space-4);min-width:0}
.anv-card-h{display:flex;flex-wrap:wrap;align-items:baseline;justify-content:space-between;gap:var(--space-2);margin-bottom:8px}
.anv-card-t{margin:0;font-size:var(--font-md);font-weight:700;color:var(--text-primary)}
.anv-card-n{margin:2px 0 0;font-size:var(--font-sm);color:var(--text-muted);max-width:70ch}
.anv-legend{display:flex;flex-wrap:wrap;gap:var(--space-3);font-size:var(--font-xs);color:var(--text-secondary)}
.anv-legend i{display:inline-block;width:10px;height:10px;border-radius:2px;background:var(--accent);margin-right:5px;vertical-align:-1px}
.anv-legend i[data-cls=anv-b-alt]{background:var(--jade,var(--info))}.anv-legend i[data-cls=acv-b-fail]{background:var(--danger)}.anv-legend i[data-cls=acv-b-held]{background:var(--warning)}
.anv-plot{position:relative;height:180px;touch-action:pan-y}
.anv-plot svg{display:block;width:100%;height:100%}
.anv-plot rect{fill:var(--accent)}.anv-plot rect.anv-b-alt{fill:var(--jade,var(--info))}.anv-plot rect.acv-b-fail{fill:var(--danger)}.anv-plot rect.acv-b-held{fill:var(--warning)}
.anv-plot .anv-cross{position:absolute;top:0;bottom:0;width:1px;background:var(--border-strong);pointer-events:none;display:none}
.anv-plot[data-hover] .anv-cross{display:block}
.anv-axis{display:flex;justify-content:space-between;margin-top:4px;font-size:var(--font-xs);color:var(--text-muted);font-variant-numeric:tabular-nums}
.anv-scale{font-size:var(--font-xs);color:var(--text-muted);text-align:right;margin-bottom:2px}
.anv-tip{position:fixed;z-index:1200;pointer-events:none;padding:8px 10px;border-radius:var(--radius-md);background:var(--surface-inverse);color:var(--text-inverse);font-size:var(--font-xs);line-height:1.45;box-shadow:var(--shadow-lg);max-width:260px;display:none}
.anv-tip b{display:block;font-weight:700;margin-bottom:2px}
.anv-grid{display:grid;grid-template-columns:1fr;gap:var(--space-4);margin-bottom:var(--space-4)}
@media (min-width:768px){.anv-grid{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (min-width:1280px){.anv-grid--3{grid-template-columns:repeat(3,minmax(0,1fr))}}
.anv-grid .anv-card{margin-bottom:0}
.anv-rows{display:flex;flex-direction:column}
.anv-row{display:grid;grid-template-columns:minmax(0,1fr) auto;column-gap:var(--space-3);align-items:center;padding:7px 0 6px;min-height:44px;border-bottom:1px solid var(--border-subtle);text-decoration:none;color:inherit}
.anv-row:last-child{border-bottom:0}
a.anv-row{cursor:pointer}
@media (hover:hover) and (pointer:fine){a.anv-row:hover .anv-row-n{color:var(--accent)}}
.anv-row-n{font-size:var(--font-sm);font-weight:600;color:var(--text-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.anv-row-n .anv-kind{display:inline-block;margin-right:6px;padding:0 6px;border-radius:var(--radius-full);background:var(--surface-subtle);font-size:var(--font-xs);font-weight:600;color:var(--text-secondary);vertical-align:1px}
.anv-row-s{font-size:var(--font-xs);color:var(--text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.anv-row-v{font-size:var(--font-sm);font-weight:700;font-variant-numeric:tabular-nums;color:var(--text-primary);text-align:right;white-space:nowrap}
.anv-row-sub{font-size:var(--font-xs);color:var(--text-muted);text-align:right;white-space:nowrap}
.anv-row-bar{grid-column:1 / -1;height:4px;margin-top:5px;border-radius:2px;background:var(--surface-subtle);overflow:hidden}
.anv-row-bar i{display:block;height:100%;background:var(--accent);border-radius:2px}
.anv-row[data-tone=bad] .anv-row-bar i{background:var(--danger)}.anv-row[data-tone=good] .anv-row-bar i{background:var(--success)}.anv-row[data-tone=warn] .anv-row-bar i{background:var(--warning)}
.anv-more{margin-top:10px}
.anv-look{display:flex;flex-direction:column;gap:6px}
.anv-look a,.anv-look p{display:flex;justify-content:space-between;gap:var(--space-3);margin:0;padding:9px 10px;border-radius:var(--radius-md);background:var(--surface-subtle);font-size:var(--font-sm);color:var(--text-primary);text-decoration:none;border-left:3px solid var(--border-strong)}
.anv-look a span:last-child{color:var(--accent);font-weight:600;white-space:nowrap}
.anv-look [data-tone=bad]{border-left-color:var(--danger)}.anv-look [data-tone=warn]{border-left-color:var(--warning)}
.anv-empty{padding:var(--space-6);text-align:center;color:var(--text-muted)}
.anv-empty h4{margin:0 0 6px;color:var(--text-primary)}
.anv-filter{margin-bottom:var(--space-3)}
.anv-tablewrap{overflow-x:auto;-webkit-overflow-scrolling:touch}
.anv-table{width:100%;border-collapse:collapse;font-size:var(--font-sm)}
.anv-table th{text-align:left;font-size:var(--font-xs);color:var(--text-muted);font-weight:600;padding:6px 8px;border-bottom:1px solid var(--border-subtle);white-space:nowrap}
.anv-table td{padding:8px;border-bottom:1px solid var(--border-subtle);vertical-align:top;white-space:nowrap}
.anv-table tr[data-anv-call]{cursor:pointer}
.anv-table .num{text-align:right;font-variant-numeric:tabular-nums}
.anv-table .st[data-tone=bad]{color:var(--danger)}.anv-table .st[data-tone=good]{color:var(--success)}
.anv-note{margin:var(--space-3) 0 0;font-size:var(--font-xs);color:var(--text-muted)}
.anv-facts{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(220px,100%),1fr));gap:var(--space-2) var(--space-4);margin:0}
.anv-facts div{display:flex;flex-direction:column;gap:2px;padding:6px 0;border-bottom:1px solid var(--border-subtle)}
.anv-facts dt{font-size:var(--font-xs);color:var(--text-muted)}.anv-facts dd{margin:0;font-size:var(--font-sm);color:var(--text-primary)}
.anv-turn{padding:8px 10px;border-radius:var(--radius-md);background:var(--surface-subtle);margin-bottom:6px;font-size:var(--font-sm)}
.anv-turn[data-them]{background:var(--accent-soft)}
.anv-turn b{display:block;font-size:var(--font-xs);color:var(--text-muted);margin-bottom:2px}

/* ═══ AGENT LAB (al-*) — the turn-forensics unit. The al- prefix is disjoint from
   aac-/anv-; leaf classes are global so they read in both the pane and the
   body-anchored sheet, while base primitives (.st/.p-block/.seg/.pill/.note/.drill)
   are scoped to .al-cons because the console does not carry them. Semantic tokens
   throughout, so the {mobile,desktop}×{light,dark} matrix is inherited. ═══ */
.al-ic{display:inline-flex;flex:none}
.al-ic svg{width:15px;height:15px;color:var(--text-muted)}
.al-pre{font-family:var(--font-mono);font-size:12.5px;line-height:1.5;white-space:pre;overflow-x:auto;margin:0;padding:12px 14px;background:var(--surface-subtle);border:1px solid var(--border-subtle);border-radius:var(--radius-md);color:var(--text-primary)}
.al-em{font-size:13px;color:var(--border-default)}
.al-k{font-family:var(--font-mono);font-size:11.5px;font-weight:600;color:var(--accent)}
a.al-k{text-decoration:none} a.al-k:hover{text-decoration:underline}
.al-t{font-size:12px;font-weight:600;color:var(--text-primary)}
.al-sub{font-size:11px;font-weight:400;color:var(--text-muted)}
.al-n{font-size:12px;font-weight:600;font-variant-numeric:tabular-nums;color:var(--text-primary)}
.al-n.q{font-weight:400;color:var(--text-muted)}
.al-u{font-size:9px;font-weight:600;color:var(--text-muted);margin-left:1px}
.al-sev-warn{color:var(--warning);font-weight:700}
.al-sev-bad{color:var(--danger);font-weight:700}
.al-sev-good{color:var(--success);font-weight:600}
.al-copy{margin-left:4px;padding:0;border:0;background:transparent;color:var(--text-muted);font:inherit;font-size:11px;line-height:1;cursor:pointer;opacity:.55;vertical-align:baseline}
.al-cons .aac-table td.aac-col-id{overflow:visible;text-overflow:clip}
.al-copy:hover{opacity:1;color:var(--text-primary)}
.al-alarm{display:inline-grid;place-items:center;width:15px;height:15px;border-radius:var(--radius-full);font-size:10px;font-weight:700;line-height:1;font-variant-numeric:tabular-nums}
.al-alarm.err{background:var(--danger);color:var(--on-danger)}
.al-alarm.warn{background:var(--warning);color:var(--on-warning)}
.al-alarm.ok{width:auto;height:auto;background:none;color:var(--border-default);font-size:13px}
.al-chip{display:inline-block;font-size:10px;font-weight:700;letter-spacing:.2px;padding:0 4px;border-radius:var(--radius-sm);font-variant-numeric:tabular-nums;background:var(--surface-subtle);color:var(--text-secondary);border:1px solid var(--border-default)}
.al-chip.bad{background:var(--danger-soft);color:var(--danger)}
.al-chip.warn{background:var(--warning-soft);color:var(--warning)}
.al-chip.good{background:var(--success-soft);color:var(--success)}
.al-meter{position:absolute;right:6px;bottom:1px;width:44px;height:2px;border-radius:1px;background:var(--surface-subtle);overflow:hidden}
.al-meter>i{position:absolute;left:0;top:0;bottom:0;min-width:1px;border-radius:1px;background:var(--text-subtle)}
.al-meter>i.warn{background:var(--warning)} .al-meter>i.bad{background:var(--danger)}
.al-door{cursor:pointer;background:none;border:0;padding:0;font:inherit;color:inherit;font-variant-numeric:tabular-nums}
.al-door:hover{text-decoration:underline}
.al-door:focus-visible{outline:2px solid var(--accent);outline-offset:1px;border-radius:2px}
.al-skel{background:linear-gradient(90deg,var(--surface-subtle),var(--surface-hover),var(--surface-subtle));background-size:200% 100%;border-radius:6px;animation:alShimmer 1.3s ease-in-out infinite}
@keyframes alShimmer{from{background-position:200% 0}to{background-position:-200% 0}}
@media (prefers-reduced-motion:reduce){.al-skel{animation:none}}

/* base primitives, scoped to the pane */
.al-cons .st{width:100%;border-collapse:collapse}
.al-cons .st th{font-size:10px;font-weight:700;letter-spacing:.4px;text-transform:uppercase;padding:4px 6px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--text-secondary);text-align:left;background:linear-gradient(var(--surface-subtle),var(--surface-subtle)),var(--surface-card);border-bottom:1px solid var(--border-default)}
.al-cons .st td{font-size:12px;line-height:1.4;padding:2px 6px;vertical-align:middle;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;border-bottom:1px solid var(--border-subtle)}
.al-cons .st tbody tr:hover{background:transparent}
@media (hover:hover) and (pointer:fine){.al-cons .st tbody tr:hover>td{background:var(--surface-hover)}}
.al-cons .p-block{border:1px solid var(--border-default);border-radius:var(--radius-md);overflow:hidden}
.al-cons .p-block>.bh{display:flex;align-items:center;gap:9px;padding:8px var(--space-4);background:var(--surface-subtle);border-bottom:1px solid var(--border-subtle)}
.al-cons .p-block>.bh .al-ic svg{width:15px;height:15px}
.al-cons .p-block>.bh b{font-size:11.5px;font-weight:600;letter-spacing:.03em;text-transform:uppercase;color:var(--text-secondary)}
.al-cons .p-block .bd{padding:var(--space-4);display:flex;flex-direction:column;gap:var(--space-3)}
.al-cons .seg{display:inline-flex;gap:3px;background:var(--surface-subtle);border:1px solid var(--border-default);border-radius:8px;padding:3px}
.al-cons .seg button{font-size:12.5px;font-weight:600;color:var(--text-secondary);background:transparent;border:0;border-radius:6px;padding:5px 11px;cursor:pointer;font-family:inherit}
.al-cons .seg button.on{background:var(--surface-card);color:var(--text-primary);box-shadow:var(--shadow-sm)}
.al-cons .pill{display:inline-flex;align-items:center;gap:5px;font-size:12px;font-weight:600;padding:3px 9px;border-radius:var(--radius-full);white-space:nowrap;width:fit-content}
.al-cons .pill.completed{background:var(--success-soft);color:var(--success)}
.al-cons .pill.running{background:var(--info-soft);color:var(--info)}
.al-cons .pill.failed{background:var(--danger-soft);color:var(--danger)}
.al-cons .pill.skipped{background:var(--surface-subtle);color:var(--text-muted)}
.al-cons .note{display:flex;gap:9px;font-size:11.75px;color:var(--text-secondary);background:var(--surface-subtle);border-radius:8px;padding:10px 12px;line-height:1.5;align-items:flex-start}
.al-cons .note .al-ic svg{width:15px;height:15px;margin-top:1px}
.al-cons .note b{color:var(--text-primary);font-weight:600}
.al-cons .drill{display:inline-flex;align-items:center;gap:7px;font-size:13px;font-weight:600;padding:7px 13px;border-radius:var(--radius-full);background:var(--accent);color:var(--on-accent);box-shadow:var(--shadow-sm);cursor:pointer;border:0}
.al-cons .drill:hover{background:var(--accent-hover)}
.al-cons .drill .al-ic svg{width:14px;height:14px;color:currentColor}
.al-cons .drill.ghost{background:var(--surface-card);color:var(--accent);border:1px solid var(--border-strong)}
.al-cons .drill.ghost:hover{background:var(--surface-hover)}
.al-select{font:inherit;font-size:12.5px;color:var(--text-primary);background:var(--surface-card);border:1px solid var(--border-default);border-radius:8px;padding:4px 8px}
.al-tblwrap{overflow-x:auto}

/* the server pager */
.al-grow{flex:1 1 auto;min-width:8px}
.al-sep{width:1px;height:18px;align-self:center;background:var(--border-default);flex:0 0 auto}
.al-pag{display:flex;align-items:center;gap:var(--space-2);padding:8px 2px;flex-wrap:wrap}
.al-fchip{cursor:pointer;font-size:12px;font-family:inherit;color:var(--text-secondary);background:var(--surface-card);border:1px solid var(--border-default);border-radius:var(--radius-full);padding:4px 11px}
.al-fchip[aria-pressed="true"]{border-color:var(--accent);background:var(--accent-soft);color:var(--accent)}
.al-pagesize{font-size:11.5px;color:var(--text-muted);display:inline-flex;align-items:center;gap:6px}
.al-pgn{font-size:11.5px;color:var(--text-muted);font-variant-numeric:tabular-nums}
.al-pgbtn{width:28px;height:28px;border-radius:6px;border:1px solid var(--border-default);background:var(--surface-card);color:var(--text-secondary);cursor:pointer;font-size:15px;line-height:1}
.al-pgbtn:disabled{opacity:.4;cursor:not-allowed}
.al-pgbtn:not(:disabled):hover{background:var(--surface-hover);color:var(--text-primary)}

/* the conversations grid — kit.table, at the lab's density; the meter rides the
   ttft column, positioned against its generated cell class. */
.al-cons .aac-table th{font-size:10px;font-weight:700;letter-spacing:.4px;text-transform:uppercase;padding:4px 6px}
/* dense single-line rows (~21px): the triage grid is scanned, so a cell clips
   with a hover title rather than wrapping to a second line. */
.al-cons .aac-table td{font-size:12px;line-height:1.4;padding:2px 6px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* the metric columns right-align — numbers scan on their last digit. The console's
   .aac-num carries tabular-nums but not the alignment, so the lab adds it. */
.al-cons .aac-table td.aac-num,.al-cons .aac-table th.aac-num{text-align:right}
.al-cons .aac-col-ttft{position:relative}
.al-cons .aac-col-eff,.al-cons .aac-col-alarm{text-align:center}

/* the autopsy: a master-detail split. In the console pane (a document that
   scrolls under the platform host) the turn list is a sticky, capped scroller
   beside a work column that grows — the controls stay put, the work is the page. */
.al-dhead{display:flex;align-items:center;gap:var(--space-2);padding:6px 0 10px;min-width:0}
.al-back{display:inline-flex;align-items:center;gap:6px;font-size:12.5px;font-weight:600;color:var(--text-secondary);background:var(--surface-card);border:1px solid var(--border-default);border-radius:var(--radius-full);padding:5px 12px;cursor:pointer;flex:none}
.al-back:hover{background:var(--surface-hover);color:var(--text-primary)}
.al-back .al-ic svg{width:14px;height:14px}
.al-ph-id{font-size:12px;font-weight:600;color:var(--text-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
.al-ph-copy{padding:0;border:0;background:transparent;color:var(--text-muted);font:inherit;font-size:13px;line-height:1;cursor:pointer;opacity:.6;flex:none}
.al-ph-copy:hover{opacity:1;color:var(--text-primary)}
.al-split{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);grid-template-rows:minmax(0,1fr);gap:var(--space-3);align-items:stretch;flex:1;min-height:0}
.al-work{display:flex;flex-direction:column;min-width:0;min-height:0;gap:var(--space-2);overflow:auto;padding-right:2px}
.al-turnpane{display:flex;flex-direction:column;min-height:0;border:1px solid var(--border-default);border-radius:var(--radius-md);overflow:hidden;background:var(--surface-card)}
.al-turnhead{display:flex;align-items:center;gap:6px;flex:none;padding:3px var(--space-2);background:var(--surface-subtle);border-bottom:1px solid var(--border-subtle)}
.al-turnhead .al-ic svg{width:13px;height:13px}
.al-turnhead b{font-size:10px;font-weight:700;letter-spacing:.4px;text-transform:uppercase;color:var(--text-secondary)}
.al-kbd{margin-left:auto;font-size:10px;color:var(--text-secondary);letter-spacing:1px}
.al-turnlist{flex:1;min-height:0;overflow-y:auto;overflow-x:hidden}
.al-turnlist table.st.al-turns{min-width:0;width:100%;table-layout:fixed}
.al-turnlist thead th{position:sticky;top:0;z-index:2}
.al-cons .al-turnlist th,.al-cons .al-turnlist td{padding:2px 4px}
.al-tl-id{width:54px;white-space:nowrap}
.al-tl-tm{width:44px;font-size:11px;color:var(--text-muted);font-variant-numeric:tabular-nums}
.al-turnlist .al-tl-id .al-alarm.err{display:inline-flex;vertical-align:middle;margin-left:3px;width:11px;height:11px;font-size:9px}
.al-tl-day>td{padding:1px 4px;font-size:9.5px;font-weight:700;letter-spacing:.4px;text-transform:uppercase;color:var(--text-muted);background:var(--surface-subtle);border-top:1px solid var(--border-subtle)}
.al-turnlist .al-alarm{width:13px;height:13px;font-size:10px}
.al-tl-n{width:46px;text-align:right}.al-tl-t{width:54px;text-align:right}.al-tl-r{width:36px;text-align:right}
.al-tl-tl{width:46px;text-align:right}.al-tl-c{width:52px;text-align:right}
.al-tl-ask{text-align:left;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px;color:var(--text-primary);cursor:help}
.al-tl-ask.muted{color:var(--text-muted);font-style:italic}
.al-tl-ask .al-tl-inf{color:var(--warning);font-weight:700;margin-right:2px}
.al-turnlist th.al-tl-n,.al-turnlist th.al-tl-t,.al-turnlist th.al-tl-r,.al-turnlist th.al-tl-tl,.al-turnlist th.al-tl-c{text-align:right}
.al-turnlist tr.star>td{background:var(--surface-hover)}
.al-turnlist tr.star>td:first-child{box-shadow:inset 3px 0 0 var(--accent)}
.al-tl-id .al-chip{margin-left:3px;padding:0 2px}

.al-workhead{display:flex;flex-direction:column;gap:4px;flex:none;min-width:0}
.al-workhead .seg{flex:none;align-self:flex-start;padding:2px}
.al-workhead .seg button{padding:3px 10px;font-size:12px}
.al-stats{display:flex;align-items:center;gap:var(--space-3);flex-wrap:wrap;row-gap:2px;min-width:0}
.al-stat{display:inline-flex;align-items:baseline;gap:4px;white-space:nowrap}
.al-stat>.k{font-size:10px;font-weight:700;letter-spacing:.3px;text-transform:uppercase;color:var(--text-muted)}
.al-stat>.v{font-size:12px;font-weight:600;color:var(--text-primary);font-variant-numeric:tabular-nums}
.al-stat.dial>.k{font-family:var(--font-mono);text-transform:none;letter-spacing:0}
.al-stat.dial>.v{font-weight:500;color:var(--text-secondary);max-width:96px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.al-dials>.al-sep{display:none}
.al-askline{display:flex;align-items:baseline;gap:6px;min-width:0;cursor:pointer}
.al-askline>.k{flex:none;font-size:10px;font-weight:700;letter-spacing:.3px;text-transform:uppercase;color:var(--text-muted)}
.al-askline>.v{flex:1 1 auto;min-width:0;font-size:12.5px;font-weight:500;color:var(--text-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.al-askline>.v.muted{color:var(--text-muted);font-style:italic}
.al-askline.open{align-items:flex-start}
.al-askline.open>.v{white-space:pre-wrap;word-break:break-word;max-height:9em;overflow:auto}
.al-why-btn{display:inline-flex;align-items:center;gap:4px;cursor:pointer;font-size:11px;font-weight:600;letter-spacing:.2px;color:var(--text-secondary);background:var(--surface-subtle);border:1px solid var(--border-default);border-radius:var(--radius-sm);padding:1px 6px}
.al-why-btn .al-ic svg{width:11px;height:11px}
@media (hover:hover) and (pointer:fine){.al-why-btn:hover{background:var(--surface-hover);color:var(--text-primary)}}
.al-tabpane{min-width:0}
.al-tabpane[data-al-dpane="timing"],.al-tabpane[data-al-dpane="prompt"]{display:grid;grid-template-columns:minmax(0,1fr);gap:var(--space-2)}
/* Stated after the grid panes and one attribute stronger: a hidden pane stays hidden whatever it displays as. */
.al-tabpane[hidden],.al-tabpane[data-al-dpane][hidden]{display:none}
.al-tcol{display:flex;flex-direction:column;gap:6px;min-width:0}

.al-frame{display:flex;flex-direction:column;gap:6px;min-width:0}
.al-dials{display:flex;align-items:center;gap:6px;flex-wrap:wrap;row-gap:4px;min-width:0}
.al-dials-k{font-size:10px;font-weight:700;letter-spacing:.3px;text-transform:uppercase;color:var(--text-muted);flex:none}
.al-dial{display:inline-flex;align-items:baseline;gap:4px;flex:none;border:1px solid var(--border-default);border-radius:var(--radius-sm);background:var(--surface-subtle);padding:1px 5px;max-width:100%}
.al-dial>.k{font-size:10px;font-weight:600;color:var(--text-secondary);font-family:var(--font-mono)}
.al-dial>.v{font-size:11px;font-weight:600;color:var(--text-primary);font-variant-numeric:tabular-nums;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.al-dial.total{border-color:var(--accent);background:linear-gradient(color-mix(in srgb,var(--accent) 8%,transparent),color-mix(in srgb,var(--accent) 8%,transparent)),var(--surface-card)}
.al-dial.warn{border-color:color-mix(in srgb,var(--warning) 45%,transparent);background:linear-gradient(color-mix(in srgb,var(--warning) 10%,transparent),color-mix(in srgb,var(--warning) 10%,transparent)),var(--surface-card)}
.al-dial.warn>.k{color:var(--warning)}
.al-dial.good{border-color:color-mix(in srgb,var(--success) 40%,transparent);background:linear-gradient(color-mix(in srgb,var(--success) 9%,transparent),color-mix(in srgb,var(--success) 9%,transparent)),var(--surface-card)}
.al-dial.good>.k{color:var(--success)}
.al-cfoot{margin-top:8px}
.al-why{max-height:180px;overflow:auto;padding:6px 8px;font-size:12px;line-height:1.45;margin:0}

.al-wfpane{overflow:auto;min-width:0;border:1px solid var(--border-default);border-radius:var(--radius-md);background:var(--surface-card)}
.al-wfpane table.st{min-width:0;width:100%}
.al-wfpane thead th{position:sticky;top:0;z-index:2}
.al-wf{table-layout:fixed;min-width:520px}
.al-cons .al-wf td{padding:2px 6px;vertical-align:top}
.al-cons table.st td.al-wf-d{white-space:normal;overflow:hidden;text-overflow:clip}
.al-wf-at{width:54px;text-align:right}.al-wf-ms{width:54px;text-align:right}
.al-wf-bar{width:88px}.al-wf-name{width:168px}.al-wf-d{width:auto}
.al-wf-dwrap{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;white-space:normal;overflow:hidden;overflow-wrap:anywhere}
.al-bar{position:relative;display:block;height:8px;border-radius:3px;background:var(--surface-subtle);border:1px solid var(--border-subtle);overflow:hidden}
.al-bar>i{position:absolute;top:0;bottom:0;background:var(--accent);border-radius:2px;min-width:2px}
.al-extra{font-size:11px;color:var(--text-muted);margin-top:1px;word-break:break-word}
.al-mcell{position:relative}
.al-wf tbody tr[data-al-span]{cursor:pointer}
.al-wf tbody tr.star>td{background:var(--surface-hover)}
.al-wf tbody tr.star>td:first-child{box-shadow:inset 3px 0 0 var(--accent)}

/* the span inspector (global — it lives in the sheet) */
.al-inspector{display:flex;flex-direction:column;min-height:0;overflow-y:auto;border:1px solid var(--border-default);border-radius:var(--radius-md);background:var(--surface-card)}
.al-insp-head{display:flex;align-items:center;gap:6px;flex:none;position:sticky;top:0;z-index:2;padding:3px var(--space-2);background:var(--surface-subtle);border-bottom:1px solid var(--border-subtle)}
.al-insp-head b{font-size:11px;font-weight:700;color:var(--text-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
.al-insp-head .al-chip{margin-left:auto;flex:none}
.al-insp-body{padding:3px var(--space-2) 24px;min-width:0}
.al-insp-sect{font-size:10px;font-weight:700;letter-spacing:.4px;text-transform:uppercase;color:var(--text-muted);margin:5px 0 1px;padding-top:4px;border-top:1px solid var(--border-subtle)}
.al-insp-sect:first-child{margin-top:1px;border-top:0;padding-top:0}
.al-kvr{display:flex;align-items:baseline;gap:6px;padding:0;min-width:0;line-height:1.5}
.al-kvr>.k{font-size:10.5px;color:var(--text-muted);flex:none;min-width:62px;font-family:var(--font-mono)}
.al-kvr>.v{font-size:11.5px;font-weight:600;color:var(--text-primary);font-variant-numeric:tabular-nums;text-align:right;margin-left:auto;overflow-wrap:anywhere;min-width:0}
.al-kvr.em>.v{color:var(--warning)}
.al-insp-pre{margin:2px 0 0;font-size:10.5px;line-height:1.35;padding:4px 6px;white-space:pre-wrap;overflow-wrap:anywhere;overflow:auto}
.al-insp-list{display:flex;flex-wrap:wrap;gap:3px;margin-top:3px}
.al-insp-empty{display:flex;flex-direction:column;align-items:center;gap:4px;padding:var(--space-5) var(--space-3);color:var(--text-muted);font-size:11.5px;text-align:center}
.al-insp-empty .al-em{font-size:21px}
.al-insp-empty b{color:var(--text-primary);font-size:13px}

/* the reply (Content + Reasoning) */
.al-reply{display:flex;flex-direction:column;min-width:0;border:1px solid var(--border-default);border-radius:var(--radius-md);background:var(--surface-card);overflow:hidden}
.al-reply .al-insp-head{gap:8px}
.al-reply .al-insp-head .al-u{margin-left:auto}
.al-replybody{min-width:0;overflow-y:auto;padding:0}
.al-replybody--thread{display:flex;height:min(72vh,780px);overflow:hidden}
.al-replybody--thread>able-claw{flex:1 1 auto;min-width:0;min-height:0;height:100%;overflow:hidden}
.al-asked{margin:8px var(--space-3) 0;max-width:860px;padding:8px 10px 9px;border-left:3px solid var(--info);border-radius:0 var(--radius-md) var(--radius-md) 0;background:var(--surface-subtle)}
.al-asked-h{display:flex;align-items:center;gap:6px;margin-bottom:4px;font-size:10.5px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--text-muted)}
.al-asked-h .al-u{margin-left:auto;text-transform:none;letter-spacing:0;font-weight:500}
.al-asked-t{font-size:13px;line-height:1.5;color:var(--text-primary);white-space:pre-wrap;overflow-wrap:anywhere}
.al-asked--empty .al-asked-t{color:var(--text-muted)}
.al-replybody>.al-insp-body{max-width:860px}
.al-replynote{display:flex;gap:6px;align-items:flex-start;margin:8px var(--space-3);padding:5px 7px;font-size:11.5px;line-height:1.45;color:var(--text-secondary);background:var(--surface-subtle);border:1px solid var(--border-subtle);border-radius:var(--radius-sm)}
.al-replynote .al-ic svg{width:12px;height:12px;flex:none;margin-top:1px}
.al-thought{margin:0 var(--space-3) 6px}
.al-thought-h{display:flex;align-items:center;gap:6px;margin-bottom:2px}
.al-rsn{width:100%;margin-top:2px}.al-rsn th,.al-rsn td{padding:2px 5px}

/* prompt / payload */
.al-pcol{display:flex;flex-direction:column;gap:6px;min-width:0}
.al-pb-i{width:32px;text-align:right;color:var(--text-muted);font-size:11px}
.al-pb-slug{max-width:0}.al-pb-src{width:130px}.al-pb-n{width:82px;text-align:right;position:relative}.al-pb-p{width:56px;text-align:right}.al-etok{width:80px;text-align:right}
.al-pblocks{table-layout:fixed}.al-pblocks td.al-mcell{position:relative}
.al-sec{border-bottom:1px solid var(--border-subtle)}.al-sec:last-child{border-bottom:0}
.al-sec>summary{list-style:none;cursor:pointer;display:flex;align-items:center;gap:6px;padding:6px 8px;flex-wrap:wrap;row-gap:4px}
.al-sec>summary::-webkit-details-marker{display:none}
.al-sec>summary::before{content:"▸";color:var(--text-muted);font-size:10px;width:9px;flex:none}
.al-sec[open]>summary::before{content:"▾"}
@media (hover:hover) and (pointer:fine){.al-sec>summary:hover{background:var(--surface-hover)}}
.al-sec-lbl{font-size:11.5px;font-weight:700;color:var(--text-primary)}
.al-sec-hm{flex:1 1 60px;min-width:48px;max-width:150px;height:3px;border-radius:2px;background:var(--surface-subtle);overflow:hidden;position:relative}
.al-sec-hm>i{position:absolute;left:0;top:0;bottom:0;background:var(--text-subtle);border-radius:2px}
.al-sec-hm>i.warn{background:var(--warning)}
.al-sec-b{padding:2px 8px 10px}.al-sec-empty{font-size:11px;color:var(--text-muted);padding:2px 2px 6px}
.al-ptbl .al-pb-src{width:72px}.al-ptbl .al-etok{width:60px}.al-ptbl .al-pb-p{width:48px}
.al-ptbl .al-kidbar{width:8px;margin:0 4px 3px 2px}
.al-ptbl .al-pb-src .al-chip{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.al-pblocks tr.al-rollup td{color:var(--text-muted)}
.al-pblocks tr.al-kindrow td{font-size:10px;font-weight:700;letter-spacing:.4px;text-transform:uppercase;color:var(--text-muted);padding:9px 0 3px}
.al-modtog{font-size:11px;color:var(--text-primary);border-color:transparent;padding:0 4px 0 0;max-width:100%;display:inline;text-align:left}
.al-modtog .mono{font-family:var(--font-mono)}
tr.al-modrow>td{border-top:1px solid var(--border-subtle)}
.al-pblocks tr.al-modrow td.al-pb-slug{white-space:normal;word-break:normal;line-height:1.5;padding-top:5px;padding-bottom:5px}
.al-modmeta{display:block;margin-top:2px}
.al-kidtog{font:inherit;font-size:10px;color:var(--text-secondary);background:transparent;border:1px solid var(--border-subtle);border-radius:4px;padding:0 5px;margin-right:4px;cursor:pointer}
.al-kidtog .al-kidcar{margin-right:3px}
.al-kidtog[aria-expanded="true"]{border-color:var(--accent);color:var(--accent)}
tr.al-kid[hidden]{display:none}tr.al-kid>td{background:var(--surface-subtle)}tr.al-kid .al-pb-slug{padding-left:0}
.al-kidbar{display:inline-block;width:14px;border-bottom:1px solid var(--border-default);margin:0 6px 3px 8px;vertical-align:middle}
.al-pblocks tbody tr[data-al-block-line]{cursor:pointer}
.al-pblocks tbody tr.star>td{background:var(--surface-hover)}
.al-pblocks tbody tr.star>td:first-child{box-shadow:inset 3px 0 0 var(--accent)}

/* the reader (global — the wide sheet) */
.al-reader{display:flex;flex-direction:column;min-height:0;min-width:0;border:0;background:var(--surface-card);overflow:hidden;flex:1}
.al-reader .al-insp-head .al-u{margin-left:auto}.al-reader .al-insp-head .al-chip{margin-left:6px}
.al-readerbody{flex:1;min-height:0;min-width:0;display:flex}
.al-readerbody able-code-editor{flex:1;min-height:0;min-width:0;display:block}
.al-readerbody .cm-editor{height:100%}
.al-viewsw{margin-left:6px;flex:none;gap:2px;padding:2px;border-radius:6px}
.al-viewsw button{font-size:10px;padding:2px 7px;border-radius:4px}
.al-wirebody{flex:1;min-height:0;min-width:0;overflow:auto;padding-bottom:24px}
.al-wirenote{display:flex;flex-wrap:wrap;gap:4px;padding:6px var(--space-2);border-bottom:1px solid var(--border-subtle)}
.al-wire-el{border-bottom:1px solid var(--border-subtle)}
.al-wire-el>summary{list-style:none;cursor:pointer;display:flex;align-items:center;gap:5px;flex-wrap:wrap;row-gap:3px;padding:4px var(--space-2);position:sticky;top:0;z-index:1;background:var(--surface-card);border-bottom:1px solid transparent}
.al-wire-el[open]>summary{border-bottom-color:var(--border-subtle)}
.al-wire-el>summary::-webkit-details-marker{display:none}
.al-wire-el>summary::before{content:"▸";color:var(--text-muted);font-size:10px;width:9px;flex:none}
.al-wire-el[open]>summary::before{content:"▾"}
.al-wire-el.star>summary{background:var(--surface-hover);box-shadow:inset 3px 0 0 var(--accent)}
.al-wire-i{flex:none;min-width:20px;text-align:right;font-size:10px;color:var(--text-muted);font-family:var(--font-mono);font-variant-numeric:tabular-nums}
.al-wire-lbl{flex:1 1 120px;min-width:0;font-size:11px;color:var(--text-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.al-wire-n{flex:none;margin-left:auto;font-size:11px;color:var(--text-secondary);font-variant-numeric:tabular-nums}
.al-wire-b{padding:0 var(--space-2) 8px}
.al-wire-note{font-size:10.5px;line-height:1.45;color:var(--text-secondary);padding:2px 0 5px}
.al-wire-txt{margin:0;white-space:pre-wrap;overflow-wrap:anywhere;font-family:var(--font-mono);font-size:10.5px;line-height:1.5;color:var(--text-secondary);background:var(--surface-subtle);border:1px solid var(--border-subtle);border-radius:var(--radius-sm);padding:7px 9px}
.al-wire-vsw{display:flex;gap:2px;padding:0 0 6px}
.al-wire-vsw button{font-size:10px;padding:2px 8px;border-radius:4px;border:1px solid var(--border-subtle);background:transparent;color:var(--text-secondary);cursor:pointer;font-family:inherit}
.al-wire-vsw button.on{background:var(--surface-hover);color:var(--text-primary);border-color:var(--border-default)}
.al-wirekids{display:flex;flex-wrap:wrap;gap:4px;padding:0 0 7px}
.al-wirekids .al-chip{cursor:pointer;font-family:inherit;line-height:1.7;border-style:solid}
.al-wirekids .al-chip.on{border-color:var(--accent);color:var(--accent)}
.al-md{font-size:11.5px;line-height:1.6;color:var(--text-primary);background:var(--surface-subtle);border:1px solid var(--border-subtle);border-radius:var(--radius-sm);padding:9px 11px;overflow-wrap:anywhere}
.al-md>:first-child{margin-top:0}.al-md>:last-child{margin-bottom:0}
.al-md h1,.al-md h2,.al-md h3,.al-md h4{margin:14px 0 6px;line-height:1.3;color:var(--text-primary);font-weight:650}
.al-md h1{font-size:15px}.al-md h2{font-size:13.5px}.al-md h3{font-size:12.5px}.al-md h4{font-size:11.5px}
.al-md p,.al-md ul,.al-md ol,.al-md blockquote{margin:0 0 8px}.al-md ul,.al-md ol{padding-left:20px}.al-md li{margin:2px 0}
.al-md blockquote{border-left:2px solid var(--border-default);padding-left:9px;color:var(--text-secondary)}
.al-md code{font-family:var(--font-mono);font-size:10.5px;background:var(--surface-hover);border-radius:3px;padding:1px 4px}
.al-md pre{margin:0 0 8px;overflow-x:auto;background:var(--surface-card);border:1px solid var(--border-subtle);border-radius:var(--radius-sm);padding:7px 9px}
.al-md pre code{background:none;padding:0}
.al-md code.al-md-raw{white-space:pre-wrap;padding:1px 4px;background:var(--surface-card);border:1px dashed var(--border-default);border-radius:3px;color:var(--text-secondary)}
.al-md table{border-collapse:collapse;margin:0 0 8px;font-size:10.5px;display:block;max-width:100%;overflow-x:auto}
.al-md th,.al-md td{border:1px solid var(--border-subtle);padding:3px 7px;text-align:left}
.al-md th{background:var(--surface-hover);font-weight:600}
.al-md a{color:var(--accent)}.al-md hr{border:0;border-top:1px solid var(--border-subtle);margin:12px 0}.al-md img{max-width:100%}

/* the mirror controls */
.al-mirror-ctl{display:flex;gap:10px;align-items:center;flex-wrap:wrap}
.al-mlbl{font-size:12.5px;display:inline-flex;align-items:center;gap:6px}
.al-plan-out{margin-top:12px}
.al-mfire{margin-top:12px;display:flex;gap:10px;align-items:center;flex-wrap:wrap}
.al-mfire b{font-size:13px}

/* the body-anchored overlays: the side sheet and the why popover */
.al-modal{position:fixed;inset:0;z-index:1200;display:grid;place-items:center;padding:var(--space-5);background:var(--surface-overlay);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}
.al-modal .box{background:var(--surface-card);border:1px solid var(--border-default);border-radius:var(--radius-lg);box-shadow:var(--shadow-xl);width:min(520px,100%);max-height:90dvh;overflow:auto;display:flex;flex-direction:column}
.al-modal.al-modal--wide .box{width:min(780px,100%);max-height:92dvh}
.al-modal.al-modal--sheet{place-items:stretch end;padding:0}
.al-modal.al-modal--sheet .box{width:min(560px,100%);height:100dvh;max-height:100dvh;border-radius:0;border-top:0;border-bottom:0;border-right:0;box-shadow:-14px 0 44px -20px color-mix(in srgb,var(--text-primary) 30%,transparent);overflow:hidden}
.al-modal.al-modal--sheet.al-sheet--wide .box{width:min(840px,100%)}
.al-modal .box .mh{display:flex;align-items:center;gap:9px;padding:var(--space-4) var(--space-5);border-bottom:1px solid var(--border-subtle)}
.al-modal .box .mh .al-ic svg{width:17px;height:17px}
.al-modal .box .mh b{font-size:14px;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.al-modal .box .mh .mx{cursor:pointer;color:var(--text-muted);display:inline-flex}
.al-modal .box .mh .mx:hover{color:var(--text-primary)}
.al-modal .box .mb{padding:var(--space-5);display:flex;flex-direction:column;gap:var(--space-4)}
.al-sheet .box .mb{padding:0;gap:0;flex:1;overflow:auto;display:flex;flex-direction:column;min-height:0}
.al-sheet .al-inspector,.al-sheet .al-reader{flex:1;min-height:0;border:0;border-radius:0}
.al-why-modal{max-height:min(62dvh,520px);margin:0}

/* the conversation sheet — the platform side panel: slides in from the right edge, the scrim fades,
   the close sits top-left; the header row is the conversation, the two panes below share the width */
.al-dsheet{position:fixed;top:0;right:0;height:100dvh;width:min(95vw,100%);z-index:1190;display:flex;flex-direction:column;min-height:0;
     background:var(--surface-card);border-left:1px solid var(--border-default);box-shadow:-10px 0 30px rgba(0,0,0,.35);
     transform:translateX(100%);transition:transform 240ms cubic-bezier(.2,.8,.2,1),visibility 0s linear 240ms;overflow:hidden;visibility:hidden}
.al-dsheet[data-open]{transform:none;visibility:visible;transition:transform 240ms cubic-bezier(.2,.8,.2,1),visibility 0s}
.al-dscrim{position:fixed;inset:0;z-index:1180;background:var(--surface-overlay);cursor:pointer;opacity:0;pointer-events:none;transition:opacity 220ms ease}
.al-dscrim[data-open]{opacity:1;pointer-events:auto}
.al-dclose{position:absolute;top:8px;left:12px;width:28px;height:28px;border-radius:8px;display:inline-flex;align-items:center;justify-content:center;
     background:transparent;color:var(--text-muted);border:0;cursor:pointer;padding:0;z-index:10;transition:background 120ms ease,color 120ms ease,transform 120ms ease}
@media (hover:hover) and (pointer:fine){.al-dclose:hover{background:var(--surface-hover);color:var(--text-primary);transform:scale(1.05)}}
.al-dclose:active{background:var(--surface-active);color:var(--text-primary);transform:scale(.95)}
.al-dclose:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
@media (pointer:coarse){.al-dclose::after{content:'';position:absolute;inset:-7px}}
.al-dtop{flex:none;display:flex;align-items:center;min-height:44px;padding:6px var(--space-4) 6px 48px;background:var(--surface-subtle);border-bottom:1px solid var(--border-subtle);min-width:0}
.al-dt-stats{gap:3px 12px}
.al-dt-idstat .al-ph-copy{margin-left:2px}
.al-dt-stats .al-stat>.v{position:relative;padding-bottom:3px}
.al-dt-stats .al-meter{right:0;bottom:0}
.al-dsheet .al-sheetbody{flex:1;min-height:0;display:flex;flex-direction:column;padding:var(--space-2) var(--space-4) var(--space-3)}
.al-dsheet .al-hostwrap{flex:1;min-height:0;display:flex;flex-direction:column}
@media (prefers-reduced-motion:reduce){.al-dsheet,.al-dscrim,.al-dclose{transition:none}}

/* narrow: the split folds to a short turn strip over a full-width work pane */
@media (max-width:1180px){
  .al-split{grid-template-columns:minmax(0,1fr);grid-template-rows:auto minmax(0,1fr)}
  .al-turnpane{max-height:180px}
}
@media (max-width:760px){
  .al-turnpane{max-height:150px}
  .al-modal.al-modal--sheet{place-items:stretch}
  .al-modal.al-modal--sheet .box{width:100%}
  .al-wf-bar{width:52px}
}


/* able-admin-nav */

able-admin-nav { display: contents; }
able-admin-nav .aan { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
able-admin-nav .aan[data-variant="inline"] { flex-direction: row; align-items: center; gap: 1px; flex-wrap: wrap; }
able-admin-nav .aan-label {
  font-size: 10px; font-weight: 800; letter-spacing: .12em; text-transform: uppercase;
  color: var(--text-muted, #6b7280); padding: 8px 10px 3px;
}
/* The heading is mid-list now (the ungated doors render above it), so it needs
   enough space above to read as the start of a group rather than a caption on
   the row it follows. Still tight when it IS first — an app whose visible set is
   all admin-gated keeps exactly the old spacing. */
able-admin-nav .aan-label:not(:first-child) { padding-top: 14px; }
able-admin-nav .aan-item {
  display: inline-flex; align-items: center; gap: 10px; min-height: 40px; padding: 8px 10px;
  border-radius: var(--radius-md, 8px); text-decoration: none;
  font-size: 13.5px; font-weight: 600; font-family: inherit; color: var(--text-secondary, #3f3f46);
  transition: background .12s ease, color .12s ease;
}
able-admin-nav .aan[data-variant="inline"] .aan-item { min-height: 34px; gap: 7px; }
able-admin-nav .aan-item[data-aan-active="true"] {
  color: var(--text-primary, #1d1c17); background: var(--surface-hover, rgba(0, 0, 0, .05));
}
@media (hover: hover) and (pointer: fine) {
  able-admin-nav .aan-item:hover {
    color: var(--text-primary, #1d1c17); background: var(--surface-hover, rgba(0, 0, 0, .05));
  }
}
able-admin-nav .aan-ico {
  flex: 0 0 auto; width: 20px; height: 20px; display: inline-flex; align-items: center; justify-content: center; color: inherit;
}
able-admin-nav .aan-ico svg { width: 18px; height: 18px; }
able-admin-nav .aan-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }


/* able-app-header */

able-app-header { display: block; }
.aah-bar {
  display: grid;
  grid-template-columns: auto auto 1fr auto auto auto auto;
  gap: 16px;
  align-items: center;
  padding: 12px 20px;
  height: 60px;
  box-sizing: border-box;
  background-color: var(--aah-fill, var(--accent, #0f172a));
  /* Use --on-accent (always white, semantically pinned to accent bg) — NOT
     --text-inverse (which flips to dark in dark mode per the inverse-
     surface semantic). The masthead bg is always --accent, so the text
     must always be white-on-accent regardless of theme. */
  color: var(--on-accent, #fff);
}
/* Notifications opt-in adds a bell cell before the theme toggle. The desktop
   grid gains one auto column so the 8th participating item never wraps to a
   second row (the same failure the burger cell documents). Gated on the
   modifier so a header WITHOUT the bell keeps the untouched 7-column grid. */
.aah-bar--with-bell { grid-template-columns: auto auto 1fr auto auto auto auto auto; }
.aah-area { display: flex; align-items: center; gap: 8px; min-width: 0; }
.aah-area--logo { justify-content: flex-start; }
.aah-area--title { padding-left: 4px; }
/* Logo + title are anchor tags (home link) — strip default link chrome
 * but keep them cursor:pointer + focusable. */
a.aah-home-link {
  color: inherit;
  text-decoration: none;
  cursor: pointer;
  border-radius: 6px;
}
a.aah-home-link:focus-visible {
  outline: 2px solid rgba(255, 255, 255, 0.6);
  outline-offset: 2px;
}
a.aah-home-link:hover { opacity: 0.9; }
.aah-area--nav { gap: 16px; flex: 0 1 auto; }
.aah-area--extra { gap: 8px; }
.aah-area--ask-able { justify-content: flex-end; }
.aah-area--user { position: relative; }
.aah-logo-img { height: 36px; max-height: 40px; width: auto; display: block; }
.aah-logo-fallback {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; border-radius: 8px;
  background: rgba(255, 255, 255, 0.1); color: inherit;
  font-weight: 700; font-size: 14px;
}
.aah-title { margin: 0; font-size: 17px; font-weight: 700; color: inherit; white-space: nowrap; }
.aah-ask-able {
  display: inline-flex; align-items: center; gap: 6px;
  background: rgba(255, 255, 255, 0.08); color: inherit;
  border: 1px solid rgba(255, 255, 255, 0.12);
  padding: 6px 14px; border-radius: 9999px; font-size: 13px;
  cursor: pointer; transition: background-color 120ms;
}
.aah-ask-able:hover { background: rgba(255, 255, 255, 0.16); }
.aah-ask-icon { font-size: 14px; }
.aah-theme-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; border-radius: 9999px;
  background: rgba(255, 255, 255, 0.06); color: inherit;
  border: 0; cursor: pointer; transition: background-color 120ms;
}
.aah-theme-toggle:hover { background: rgba(255, 255, 255, 0.14); }
/* The notifications bell trigger — <able-notification-bell> ships a deliberately
   minimal .anb-btn (32x32, transparent, currentColor) for host shells to dress.
   Mirror the theme toggle exactly: same square, same round radius, same
   white-on-accent scrim + hover. The masthead bg is always --accent, so the
   scrim is a theme-agnostic white overlay (same rationale as the toggle), and
   the icon inherits the always-white-on-accent currentColor. Hover is gated to
   fine pointers so touch devices never sticky-hover the trigger. */
.aah-bell .anb-btn {
  border-radius: 9999px;
  background: rgba(255, 255, 255, 0.06);
  transition: background-color 120ms;
}
@media (hover: hover) and (pointer: fine) {
  .aah-bell .anb-btn:hover { background: rgba(255, 255, 255, 0.14); }
}
/* The account dropdown is the nested <able-user-menu> blueprint — it ships its
 * own styles (.aum-*) tuned for a dark navbar + the impersonation treatment.
 * The header only positions the cell (.aah-area--user is position:relative). */
/* Burger button — hidden by default, shown only on mobile via @media.
   The CELL must hide too: the desktop grid declares 7 columns for 8 emitted
   areas — an empty participating burger cell shifts every later area one
   column and wraps .aah-area--user onto a second row (the 2026-06-11
   build-admin "user menu fell out of the header" bug). */
.aah-area--burger {
  display: none;
}
.aah-burger {
  display: none;
  width: 44px;
  height: 44px;
  background: transparent;
  border: 0;
  color: inherit;
  cursor: pointer;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 5px;
  padding: 8px;
}
.aah-burger span {
  display: block;
  width: 24px;
  height: 2px;
  background: currentColor;
  border-radius: 1px;
  transition: transform 200ms, opacity 200ms;
}
/* Hamburger → X animation */
.aah-burger[aria-expanded="true"] span:first-child {
  transform: rotate(45deg) translate(7px, 7px);
}
.aah-burger[aria-expanded="true"] span:nth-child(2) {
  opacity: 0;
}
.aah-burger[aria-expanded="true"] span:last-child {
  transform: rotate(-45deg) translate(7px, -7px);
}
/* iOS HIG / Android material — 44×44 minimum touch target on coarse pointers.
   Keeps the desktop (fine pointer) dense; expands hit area on phones/tablets. */
@media (pointer: coarse) {
  .aah-theme-toggle, .aah-bell .anb-btn, .aah-area--logo, .aah-area--user, .aah-ask-able {
    min-width: 44px; min-height: 44px;
  }
  .aah-area--logo { padding: 0 6px; }
}
/* Safe-area-inset for iPhone notch / dynamic island */
.aah-bar {
  padding-top: max(12px, env(safe-area-inset-top));
  padding-left: max(20px, env(safe-area-inset-left));
  padding-right: max(20px, env(safe-area-inset-right));
}
@media (max-width: 600px) {
  .aah-bar {
    grid-template-columns: auto 1fr auto auto auto auto;
    padding: 8px 12px;
    gap: 8px;
    position: relative;
  }
  /* With the bell present, the mobile row (logo · title · ask-able · burger ·
     bell · theme · user) needs one more column too. */
  .aah-bar--with-bell { grid-template-columns: auto 1fr auto auto auto auto auto; }
  .aah-area--nav, .aah-area--extra { display: none; }
  .aah-area--burger { display: flex; }
  .aah-burger { display: flex; }
  /* When burger toggled open, show nav + extra as overlay below header */
  able-app-header.aah-mobile-nav-open .aah-area--nav,
  able-app-header.aah-mobile-nav-open .aah-area--extra {
    display: block;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    background: var(--aah-fill, var(--accent, #0f172a));
    border-bottom: 1px solid var(--border-default, rgba(255, 255, 255, 0.08));
    padding: 12px 16px;
    z-index: 50;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
  }
  .aah-title { font-size: 15px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .aah-ask-label { display: none; }
}


/* able-automation-cmdk */

able-automation-cmdk { display: contents; }
.ac-cmdk-back { position: fixed; inset: 0; z-index: 200; background: var(--surface-overlay, rgba(15,18,28,.44)); backdrop-filter: blur(2px); opacity: 0; pointer-events: none; transition: opacity .16s; }
.ac-cmdk-back.on { opacity: 1; pointer-events: auto; }
.ac-cmdk { position: fixed; top: 11vh; left: 50%; transform: translateX(-50%) translateY(-10px); z-index: 201; width: min(620px, calc(100vw - 28px)); max-height: 72dvh; display: flex; flex-direction: column;
  background: var(--surface-card, #fff); color: var(--text-primary, #1d1c17); border: 1px solid var(--border-default, #e2e6ec); border-radius: var(--radius-lg, 14px); box-shadow: var(--shadow-lg, 0 18px 44px -18px rgba(20,26,44,.3)); opacity: 0; pointer-events: none; transition: opacity .16s, transform .16s; overflow: hidden; }
.ac-cmdk.on { opacity: 1; pointer-events: auto; transform: translateX(-50%) translateY(0); }
.ac-cmdk-inp { display: flex; align-items: center; gap: 10px; padding: 13px 16px; border-bottom: 1px solid var(--border-subtle, #eef1f5); }
.ac-cmdk-inp svg { width: 18px; height: 18px; color: var(--text-subtle, #9ca3af); flex: none; fill: none; stroke: currentColor; stroke-width: 1.75; stroke-linecap: round; stroke-linejoin: round; }
.ac-cmdk-inp input { flex: 1; border: 0; outline: none; background: transparent; color: inherit; font: 500 15px var(--font-sans, sans-serif); }
.ac-cmdk-inp input::placeholder { color: var(--text-subtle, #9ca3af); }
.ac-cmdk-inp .kbd { font: 600 10px var(--font-mono, monospace); color: var(--text-muted, #6b7280); border: 1px solid var(--border-default, #e2e6ec); border-radius: 5px; padding: 2px 6px; flex: none; }
.ac-cmdk-body { flex: 1; overflow-y: auto; padding: 6px; }
.ac-cmdk-grp-h { display: flex; align-items: center; gap: 7px; padding: 9px 10px 5px; font: 600 10px var(--font-mono, monospace); letter-spacing: .05em; text-transform: uppercase; color: var(--text-muted, #6b7280); }
.ac-cmdk-grp-h svg { width: 13px; height: 13px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.ac-cmdk-grp-h .c { margin-left: auto; opacity: .7; }
.ac-cmdk-row { display: flex; align-items: center; gap: 10px; padding: 8px 10px; border-radius: 8px; cursor: pointer; }
.ac-cmdk-row.active, .ac-cmdk-row:hover { background: var(--surface-hover, #f4f6fb); }
.ac-cmdk-row .rico { width: 24px; height: 24px; border-radius: 6px; display: grid; place-items: center; flex: none; background: var(--surface-subtle, #f1f5f9); color: var(--text-muted, #6b7280); }
.ac-cmdk-row .rico svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 1.75; stroke-linecap: round; stroke-linejoin: round; }
.ac-cmdk-row .rn { flex: 1; min-width: 0; }
.ac-cmdk-row .rn b { font: 500 13px/1.3 var(--font-mono, monospace); color: var(--text-primary, #1d1c17); display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ac-cmdk-row .rn small { font-size: 11px; color: var(--text-muted, #6b7280); display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ac-cmdk-row .rt { font-size: 9px; font-weight: 600; letter-spacing: .03em; text-transform: uppercase; padding: 1px 6px; border-radius: 999px; border: 1px solid; flex: none; }
.ac-cmdk-row .rt.db { color: var(--db, #5a6b82); border-color: color-mix(in srgb, var(--db, #5a6b82) 40%, transparent); background: var(--db-soft, #eef1f6); }
.ac-cmdk-row .rt.git { color: var(--git, #1b2a4a); border-color: color-mix(in srgb, var(--git, #1b2a4a) 35%, transparent); background: var(--git-soft, #e6ebf5); border-style: dashed; }
.ac-cmdk-empty { padding: 26px 18px; text-align: center; color: var(--text-muted, #6b7280); font-size: 12.5px; line-height: 1.6; }
.ac-cmdk-empty b { color: var(--text-secondary, #3f3f46); }
@media (max-width: 560px) { .ac-cmdk { top: 0; left: 0; transform: none; width: 100vw; max-height: 100dvh; height: 100dvh; border-radius: 0; } .ac-cmdk.on { transform: none; } }


/* able-breadcrumb */

able-breadcrumb {
  display: block;
  width: 100%;
  box-sizing: border-box;
}
.abc-nav {
  width: 100%;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.abc-nav::-webkit-scrollbar { display: none; }
.abc-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: var(--space-1, 4px);
  font-family: var(--font-ui, sans-serif);
  font-size: 13px;
  line-height: 1.4;
}
.abc-item {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1, 4px);
  min-height: 44px;
  white-space: nowrap;
}
.abc-link {
  color: var(--text-muted, #6B5F58);
  text-decoration: none;
  padding: var(--space-1, 4px) var(--space-2, 8px);
  border-radius: var(--radius-sm, 3px);
  transition: color 160ms, background-color 160ms;
}
@media (hover: hover) and (pointer: fine) {
  .abc-link:hover {
    color: var(--copper, #B36A2F);
    background: var(--surface-subtle, #F2EDE5);
  }
}
.abc-link:focus-visible {
  outline: 2px solid var(--copper, #B36A2F);
  outline-offset: 2px;
}
.abc-current {
  color: var(--text-primary, #0F0E0C);
  font-weight: 500;
  padding: var(--space-1, 4px) var(--space-2, 8px);
}
.abc-sep {
  color: var(--surface-card-4, #D8CFC1);
  font-size: 12px;
  user-select: none;
  flex-shrink: 0;
}
able-breadcrumb[data-separator="chevron"] .abc-sep::before { content: "›"; font-size: 14px; }
able-breadcrumb[data-separator="slash"]   .abc-sep::before { content: "/"; }
able-breadcrumb[data-separator="dot"]     .abc-sep::before { content: "·"; font-size: 16px; }


/* able-cal-bar */

able-cal-bar { display: block; min-width: 0; }

/* The band. "margin: 0" is deliberate — see the ownership note above: where the
   bar sits is the page's business, and a margin here would ADD to the shell's
   flex gap on every host that already spaces its own children. */
.acb {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--space-2); row-gap: var(--space-2);
  margin: 0; min-height: 36px; min-width: 0;
}

/* "display: contents" — the host's pills become DIRECT flex items of the band,
   which is what both donor pages had before this tag existed (their pills were
   authored as children of .calbar itself). Keeping them direct items is what
   preserves their wrap behaviour and lets a host keep hiding them by their own
   id at desktop width, exactly as both already do. */
.acb-lead { display: contents; }

.acb-pager { display: inline-flex; align-items: center; gap: var(--space-1); }

/* ALL THREE PAGER CONTROLS SHARE ONE CONTAINER. Today is the one ACTION here,
   so it is the one ACCENT — but the accent is carried by the TEXT, not by the
   absence of a container. An adversarial read of the rendered bar (2026-08-25)
   flagged exactly that: beside two bordered, filled chevrons a bare accent word
   "carece de delimitacion como control interactivo". Measured and true — Today
   computed "border: 0px none" while both siblings carried a border and a fill.
   COLOUR says which control matters; the CONTAINER says that it is a control,
   and dropping the second to emphasise the first made it read as a caption.
   (The ableU donor had it borderless-accent and the fryaway fork had it
   bordered-neutral; this is neither — bordered AND accent.) */
.acb-arrow, .acb-today {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 44px; min-height: 44px;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  background: var(--surface-card);
  text-decoration: none; white-space: nowrap;
  font-size: var(--font-sm); font-weight: 600;
}
.acb-arrow { color: var(--text-secondary); font-size: var(--font-lg); line-height: 1; }
.acb-today { padding: 0 var(--space-3); margin-left: var(--space-1); color: var(--accent); }

@media (hover: hover) and (pointer: fine) {
  .acb-arrow:hover { background: var(--surface-hover); color: var(--text-primary); }
  .acb-today:hover { background: var(--surface-hover); }
}
.acb-arrow:focus-visible, .acb-today:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 1px;
}

/* "min-width: 9ch" is a STABILITY rule, not a size: the label's own width
   changes with the week ("Aug 24 – 30" vs "Aug 31 – Sep 6"), and the pager sits
   beside a flex:1 note — so without a floor the whole band shifts on every page
   of the pager. tabular-nums holds the digits still for the same reason. */
.acb-range {
  display: inline-flex; align-items: center; min-height: 44px;
  min-width: 9ch; padding: 0 var(--space-2);
  justify-content: center; text-align: center;
  font-size: var(--font-base); font-weight: 700; line-height: 1;
  color: var(--text-primary);
  font-variant-numeric: tabular-nums; letter-spacing: -0.01em; white-space: nowrap;
}

/* THE COMPACTION IS POINTER-GATED, NOT WIDTH-GATED ALONE. ableU shipped a
   width-only version of this and took three touch targets under the 44px floor
   on a phone; the layout audit caught it at 375px. A coarse pointer keeps 44px
   at every width. */
@media (hover: hover) and (pointer: fine) and (min-width: 640px) {
  /* 32, was 36 (Angel 2026-08-28: "sacar un poquito de padding arriba y abajo").
     Fine-pointer only — the 44px touch floor above is untouched. */
  .acb-arrow, .acb-today { min-width: 32px; min-height: 32px; }
  .acb-range { min-height: 32px; }
}
/* THE BAND'S 40px FLOOR IS A REGULAR-DENSITY RULE. At compact the band must be
   as short as its controls — that tenant's grid budgets the header in measured
   pixels and a floor 12px above the control height is exactly the dead space
   the density exists to remove. */
able-cal-bar[data-density="compact"] .acb { min-height: 0; }
able-cal-bar[data-density="compact"] .acb-arrow,
able-cal-bar[data-density="compact"] .acb-today { min-width: 44px; min-height: 44px; }
able-cal-bar[data-density="compact"] .acb-range { min-height: 44px; }
@media (hover: hover) and (pointer: fine) and (min-width: 640px) {
  able-cal-bar[data-density="compact"] .acb-arrow { min-width: 28px; min-height: 28px; font-size: 15px; }
  able-cal-bar[data-density="compact"] .acb-today { min-width: 0; min-height: 28px; }
  able-cal-bar[data-density="compact"] .acb-range { min-height: 28px; font-size: var(--font-sm); }
}


/* The note is REAL information (which clock am I reading?) that must never be
   center-stage and must never be a mover. It gets a permanent flex:1 slot, so
   filling it in the browser paints text and shifts nothing. */
.acb-note { flex: 1 1 auto; min-width: 0; display: flex; align-items: center; overflow: hidden; }
/* THE NOTE CLIPS AT ITS OWN BOX — it can never paint over the pager beside it.
   A slotted note is the host's element with the host's typography, and one of
   the two donors ships it "white-space: nowrap" with the flex default
   "min-width: auto", i.e. an item that refuses to shrink below its text. Probed
   adversarially at 1024 with a 200-char unbreakable token: the paragraph
   measured 1198px inside a ~440px slot. It did NOT bleed the page — the note is
   right-pinned, so the excess spills LEFT, and left overflow never grows
   scrollWidth — but "does not break the page" is a lower bar than "does not
   cover the chevrons". These two lines make it clip instead. */
.acb-note > * { min-width: 0; max-width: 100%; overflow: hidden; text-overflow: ellipsis; }
@media (min-width: 1000px) { .acb-note { justify-content: flex-end; } }
@media (max-width: 520px) { .acb-note { flex-basis: 100%; order: 9; justify-content: flex-start; } }

@media (prefers-reduced-motion: reduce) {
  .acb-arrow, .acb-today { transition: none; }
}


/* able-cal-rail */

able-cal-rail { display: block; min-width: 0; }
/* Height budget: the whole rail fits a ~900px viewport with two groups
   (4 + 11 items) + a small foot, NO internal scroll. Round 5 spent the budget
   deliberately — the month is the rail's most-read object and got BIGGER
   (32px cells), paid for by the filter rows getting TIGHTER (24px), which is
   the trade Raul asked for in that order. Arithmetic:
   create 44 + mini ~268 (250 reserved floor + head) + 2 group sections
   (12 pad-top + 28 header = 40 each -> 80) + 15 rows x 24 = 360 + foot ~80
   (12 pad-top + ~68 content) + 4 flex gaps x 12 = 48  ->  ~880 <= 900.
   The 24px rows + this space-3 section gap are the mechanism. */
able-cal-rail .acr { display: flex; flex-direction: column; gap: var(--space-3); }
able-cal-rail .acr-create {
  display: inline-flex; align-items: center; gap: var(--space-2);
  min-height: var(--tap-target, 44px); padding: 0 var(--space-5);
  border: 1px solid var(--border-default); border-radius: var(--radius-full);
  background: var(--surface-card); color: var(--text-primary);
  font: inherit; font-size: var(--font-sm); font-weight: 700;
  cursor: pointer; box-shadow: var(--shadow-sm); align-self: flex-start;
  transition: box-shadow var(--transition-fast, 120ms), background var(--transition-fast, 120ms);
}
able-cal-rail .acr-create svg { flex: none; }
@media (hover: hover) and (pointer: fine) {
  able-cal-rail .acr-create:hover { background: var(--surface-hover); box-shadow: var(--shadow-md); }
}
/* min-height is the CLS reservation (contract doc) — do NOT remove, and move it
   with the cell size whenever that changes. The side padding is the internal
   breathing (Raul: "need some padding left of month") — head + grid never sit
   flush against the element's left edge. */
able-cal-rail .acr-mini { min-height: 300px; padding: 0 var(--space-1); }
able-cal-rail .acr-mini-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-2); margin-bottom: var(--space-2); padding-left: var(--space-1);
}
able-cal-rail .acr-mini-title { font-size: var(--font-sm); font-weight: 700; color: var(--text-primary); }
able-cal-rail .acr-mini-nav { display: inline-flex; gap: var(--space-1); }
able-cal-rail .acr-mini-nav button {
  width: 28px; height: 28px; border: 0; border-radius: var(--radius-full);
  background: transparent; color: var(--text-muted); font-size: var(--font-base);
  line-height: 1; cursor: pointer; display: inline-grid; place-items: center;
}
@media (hover: hover) and (pointer: fine) {
  able-cal-rail .acr-mini-nav button:hover { background: var(--surface-hover); color: var(--text-primary); }
}
/* Real ROW spacing, not just cell height: the density dots sit under the number
   and with a 1px gap they read as belonging to the row BELOW them. The month is
   the rail's primary instrument — it gets the vertical room. */
able-cal-rail .acr-mini-grid {
  display: grid; grid-template-columns: repeat(7, 1fr); column-gap: 1px; row-gap: 3px;
}
able-cal-rail .acr-dow {
  font-size: 10px; font-weight: 700; letter-spacing: .04em; text-align: center;
  color: var(--text-subtle); padding: 2px 0 4px; text-transform: uppercase;
}
/* Day cell = number circle over an ALWAYS-PRESENT fixed-height dot strip, so
   marks appearing/disappearing restyle a cell but never change its height. */
able-cal-rail .acr-d {
  appearance: none; border: 0; background: transparent; font: inherit;
  font-size: 13px; color: var(--text-secondary);
  width: 100%; min-height: 38px; border-radius: var(--radius-md);
  cursor: pointer; display: inline-flex; flex-direction: column;
  align-items: center; justify-content: flex-start; padding: 1px 0 0;
  font-variant-numeric: tabular-nums;
  position: relative;
}
able-cal-rail .acr-d-num {
  width: 28px; height: 28px; border-radius: var(--radius-full);
  display: inline-grid; place-items: center; line-height: 1;
  position: relative; z-index: 1;
}
/* ── THE SELECTED WEEK, as a band ───────────────────────────────────────────
   The mini-month is the only place the reader can see WHERE the week they are
   reading sits in the month. A band under the seven days says it without
   competing with the two marks that already mean something: today keeps its
   filled circle and the selected day its own fill, both drawn ON TOP. The band
   is a background layer, so it never restyles a number and never moves one. */
able-cal-rail .acr-d[data-inweek]::before {
  content: ''; position: absolute; left: 0; right: 0; top: 1px; bottom: 1px;
  /* --surface-subtle was measured INVISIBLE here: the rail sits on the page
     ground, not on a card, so subtle-vs-card is a difference of nothing. An
     accent tint states "this is the week you are reading" and pairs with the
     accent today-circle instead of competing with it. --accent is a FILL token
     (never text), so this is exactly the right use of it. */
  background: color-mix(in srgb, var(--accent) 13%, transparent);
  z-index: 0;
}
able-cal-rail .acr-d[data-wkfirst]::before { border-radius: var(--radius-md) 0 0 var(--radius-md); left: 1px; }
able-cal-rail .acr-d[data-wklast]::before { border-radius: 0 var(--radius-md) var(--radius-md) 0; right: 1px; }
able-cal-rail .acr-d[data-inweek][data-out]::before { opacity: .5; }
able-cal-rail .acr-dots {
  height: 7px; margin-top: 1px;
  display: flex; align-items: center; justify-content: center;
  gap: 2px; pointer-events: none;
}
able-cal-rail .acr-dots i {
  width: 3px; height: 3px; border-radius: 50%; display: block; opacity: .85;
}
@media (hover: hover) and (pointer: fine) {
  able-cal-rail .acr-d:hover { background: var(--surface-hover); color: var(--text-primary); }
}
able-cal-rail .acr-d[data-out] .acr-d-num,
able-cal-rail .acr-d[data-out] .acr-dots { opacity: .35; }
able-cal-rail .acr-d[data-today] .acr-d-num { background: var(--accent); color: var(--on-accent); font-weight: 800; }
able-cal-rail .acr-d[data-sel]:not([data-today]) .acr-d-num { background: var(--accent-soft, var(--surface-active)); color: var(--accent-text, var(--accent)); font-weight: 800; }
able-cal-rail .acr-g { border-top: 1px solid var(--border-subtle); padding-top: var(--space-3); }
able-cal-rail .acr-g summary {
  list-style: none; cursor: pointer; display: flex; align-items: center; gap: 5px;
  font-size: 10.5px; font-weight: 800; letter-spacing: .05em; text-transform: uppercase;
  color: var(--text-muted); padding: 2px var(--space-1);
  min-height: 24px;
}
/* A COLLAPSED group must still say what it is holding, or collapsing it is just
   hiding. The count says how many are on; the dot strip keeps the group's colour
   vocabulary on screen even when its rows are folded away. */
able-cal-rail .acr-g-n {
  margin-left: auto; flex: none;
  /* WCAG: this was 9.5px in --text-subtle, measured at 2.45:1 on the rail's
     own surface (2026-08-25) — a contrast FAIL on the one element whose whole
     job is to report what a collapsed group is holding. --text-muted clears
     4.5:1 against every surface token the rail sits on, in both modes, and the
     size joins the --font-* scale instead of sitting below its floor. */
  font-size: var(--font-xs); font-weight: 800; letter-spacing: 0; text-transform: none;
  color: var(--text-muted); font-variant-numeric: tabular-nums;
}
able-cal-rail .acr-g-dots { display: none; flex: none; align-items: center; gap: 2px; }
able-cal-rail .acr-g:not([open]) .acr-g-dots { display: flex; }
able-cal-rail .acr-g-dots i {
  width: 5px; height: 5px; border-radius: 50%; display: block;
  background: var(--sw, var(--text-muted));
}
able-cal-rail .acr-g-dots i[data-off] { background: transparent; box-shadow: inset 0 0 0 1px var(--sw, var(--text-muted)); opacity: .5; }
able-cal-rail .acr-g summary::-webkit-details-marker { display: none; }
able-cal-rail .acr-g summary::after {
  content: ''; width: 7px; height: 7px; margin-left: auto;
  border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg); transition: transform var(--transition-fast, 120ms);
}
able-cal-rail .acr-g:not([open]) summary::after { transform: rotate(-45deg); }
able-cal-rail .acr-items { list-style: none; margin: 0; padding: 0; }
/* TWO COLUMNS for a long group. Eleven single-file rows were eating the rail's
   whole vertical budget for a list of one-word labels; paired up they cost half
   the height and nothing is hidden — which beats collapsing, because a filter
   you cannot see is a filter you forget you set. */
able-cal-rail .acr-items[data-cols="2"] {
  display: grid; grid-template-columns: 1fr 1fr; column-gap: 4px;
}
/* Compact source rows (Raul: "filters on left can be more compact"): ~26px,
   12px type, dot + emoji + label. The real checkbox STAYS for keyboard + SR
   (visually hidden, clip pattern); the color dot doubles as the checked
   indicator — filled = on, hollow outline = off. */
able-cal-rail .acr-it label {
  position: relative;
  display: flex; align-items: center; gap: 5px;
  padding: 0 4px; min-height: 22px;
  border-radius: var(--radius-md); cursor: pointer;
  font-size: 11px; color: var(--text-secondary);
}
@media (hover: hover) and (pointer: fine) {
  able-cal-rail .acr-it label:hover { background: var(--surface-hover); }
}
able-cal-rail .acr-it input {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; border: 0;
  clip: rect(0 0 0 0); clip-path: inset(50%); overflow: hidden; white-space: nowrap;
}
able-cal-rail .acr-it label:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: -1px; }
able-cal-rail .acr-it-dot {
  flex: none; width: 10px; height: 10px; border-radius: var(--radius-full);
  border: 2px solid var(--sw, var(--accent)); background: var(--sw, var(--accent));
  transition: background var(--transition-fast, 120ms), border-color var(--transition-fast, 120ms);
}
able-cal-rail .acr-it input:not(:checked) ~ .acr-it-dot { background: transparent; border-color: var(--sw, var(--text-muted)); }
able-cal-rail .acr-it input:not(:checked) ~ .acr-it-emoji { opacity: .45; }
able-cal-rail .acr-it input:not(:checked) ~ .acr-it-label { color: var(--text-subtle); }
able-cal-rail .acr-it-emoji { flex: none; width: 14px; text-align: center; font-size: 12px; line-height: 1; }
able-cal-rail .acr-it-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
able-cal-rail .acr-foot { border-top: 1px solid var(--border-subtle); padding-top: var(--space-3); }


/* able-calendar */

/* ── THE SSR SHELL (calendar.py) — what paints before the bundle parses ────
   The shell emits the REAL week as live <able-cal-rail> / <able-cal-bar> /
   <able-time-grid variant="week"> elements, so it is styled by those blueprints'
   own render-blocking chrome — nothing calendar-local is needed for the grid,
   the band or the rail. Only the un-adopted slotted page chrome ([slot=pre] /
   [slot=post], re-emitted after the shell) hides until _build() places it
   (post-upgrade the slot attribute is removed, so the rule stops applying). */
able-calendar > [slot="pre"], able-calendar > [slot="post"] { display: none; }

/* ── THE SHELL: [rail 248px] [main 1fr] ≥1000px, one column below ──────────── */
able-calendar {
  display: grid;
  grid-template-columns: var(--acal-rail-w, 248px) minmax(0, 1fr);
  /* space-2 (Angel, ratified twice on the page this blueprint replaced —
     able-ableu d2939f6): the rail carries its own inner air, a wide track gap
     doubled the separation. */
  gap: var(--space-2);
  align-items: start;
  min-width: 0;
}
/* Below the rail rung the rail moves into a bottom-sheet (the page's node moves,
   so it keeps its state); the main column takes the whole width. */
@media (max-width: 999px) {
  able-calendar { grid-template-columns: minmax(0, 1fr); gap: 0; }
  able-calendar > .acal-rail { display: none; }
}
/* The rail BREATHES on its left (Raul round-3 "padding left of month"; re-ratified
   by Angel on the page this replaced, able-ableu 939f54d) — without it the
   mini-month sits flush against the collapsed nav. */
able-calendar > .acal-rail { min-width: 0; padding-left: var(--space-3); box-sizing: border-box; }
able-calendar > .acal-main {
  /* gap 0 (Angel, twice on the replaced page — able-ableu 939f54d): the date
     bar's border sits DIRECTLY on the grid; the landed strip below keeps its
     own 16px top margin, so only the bar seam tightens. */
  display: flex; flex-direction: column; min-width: 0; gap: 0;
  /* A hair of air at the viewport edge — the last column's cards and the bar's
     note otherwise sit flush against the browser chrome. */
  padding-right: 6px;
}
.acal-bodywrap { min-width: 0; min-height: 0; position: relative; display: flex; flex-direction: column; }

/* The pager pills (Create / Filters) live in the rail at desktop width; the bar
   only carries them once the rail is a sheet. The slot wrapper is display:
   contents so they stay direct flex items of the band. */
.acal-lead { display: contents; }
.acal-pill {
  display: none;
  align-items: center; gap: 6px;
  height: 32px; padding: 0 12px;
  border: 1px solid var(--border-default); border-radius: var(--radius-full);
  background: var(--surface-card); color: var(--text-primary);
  font: 600 var(--font-sm)/1 var(--font-sans); cursor: pointer;
  min-height: 44px;
}
@media (min-width: 640px) and (pointer: fine) { .acal-pill { min-height: 32px; } }
@media (max-width: 999px) { .acal-pill { display: inline-flex; } }
.acal-pill:hover { background: var(--surface-hover); }
.acal-pill:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.acal-pill svg { flex: none; }
.acal-fcount:empty { display: none; }
.acal-fcount {
  min-width: 16px; height: 16px; padding: 0 4px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--radius-full);
  background: var(--accent); color: var(--on-accent);
  font-size: 11px; font-weight: 700; font-variant-numeric: tabular-nums;
}

/* THE WEEK GRID vs THE MOBILE DAY-LIST — one feed, two shapes chosen by width.
   The week variant deliberately never pans (a week you scroll sideways cannot be
   compared), so below 640px it crushes to unreadable columns; there the
   transposed list takes over (7 rows, time across). Day and month modes need no
   swap — a single day column and a month grid both read at 320px. */
.acal-daylist { list-style: none; margin: 0; padding: 0; display: none; }
able-calendar[data-mode="week"] .acal-grid { display: block; }
@media (max-width: 639px) {
  able-calendar[data-mode="week"] .acal-grid { display: none; }
  able-calendar[data-mode="week"] .acal-daylist { display: block; }
}

/* the transposed mobile day row: a head, a mini-timeline ribbon + a named
   preview line (the tap target is the whole middle cell), a marks tail */
.acal-rr {
  display: grid; grid-template-columns: 3.4rem 1fr auto; align-items: stretch;
  gap: var(--space-2); padding: var(--space-2) 0;
  border-bottom: 1px solid var(--border-subtle);
}
.acal-rr[data-today] { background: var(--accent-soft); border-radius: var(--radius-md); }
.acal-rr[data-past] { opacity: .72; }
.acal-rr-head {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 1px; border: 0; background: none; cursor: pointer; color: inherit;
  min-height: 44px; border-radius: var(--radius-md);
}
.acal-rr-head:hover { background: var(--surface-hover); }
.acal-rr-dow { font-size: var(--font-xs); color: var(--text-secondary); text-transform: uppercase; letter-spacing: .04em; }
.acal-rr-num { font-size: var(--font-lg); font-weight: 700; color: var(--text-primary); }
.acal-rr[data-today] .acal-rr-num { color: var(--accent-text); }
.acal-rr-mid { display: flex; flex-direction: column; gap: 4px; justify-content: center; min-width: 0; cursor: pointer; border-radius: var(--radius-md); }
.acal-rr-mid:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.acal-rr-tl { position: relative; height: 22px; border-radius: var(--radius-sm); background: var(--surface-subtle); overflow: hidden; }
.acal-rr-tick { position: absolute; top: 0; bottom: 0; width: 1px; background: var(--border-subtle); }
.acal-rr-blk {
  position: absolute; top: 3px; bottom: 3px; border-radius: 2px;
  background: var(--cat, var(--cal, var(--cal-tone-primary, var(--accent))));
}
.acal-rr-now { position: absolute; top: -2px; bottom: -2px; width: 2px; background: var(--danger, #dc2626); }
.acal-rr-prev { margin: 0; display: flex; flex-wrap: wrap; gap: 4px 10px; font-size: var(--font-sm); min-width: 0; }
.acal-rr-prev[data-empty] { color: var(--text-muted); }
.acal-rr-prev-i { display: inline-flex; gap: 5px; align-items: baseline; min-width: 0; }
.acal-rr-prev-t { color: var(--text-secondary); font-variant-numeric: tabular-nums; white-space: nowrap; }
.acal-rr-prev-n { color: var(--text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.acal-rr-prev-i[data-done] .acal-rr-prev-n { text-decoration: line-through; color: var(--text-muted); }
.acal-rr-prev-more { color: var(--text-secondary); font-weight: 600; }
.acal-rr-tail { display: flex; align-items: center; }
.acal-rr-marks {
  min-width: 20px; height: 20px; padding: 0 5px; border-radius: var(--radius-full);
  background: var(--surface-subtle); color: var(--text-secondary);
  font-size: var(--font-xs); font-weight: 700; font-variant-numeric: tabular-nums;
  display: inline-flex; align-items: center; justify-content: center;
}
.acal-rr-marks:empty { display: none; }

/* honest empty + loading states */
.acal-empty[hidden], .acal-loading[hidden] { display: none; }
.acal-loading {
  display: flex; align-items: center; justify-content: center; gap: var(--space-2);
  padding: var(--space-8) var(--space-4); color: var(--text-secondary);
  font: 500 var(--font-sm)/1.4 var(--font-sans);
}
.acal-spin {
  width: 18px; height: 18px; border-radius: 50%;
  border: 2px solid var(--border-default); border-top-color: var(--accent);
  animation: acal-spin .7s linear infinite;
}
@keyframes acal-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .acal-spin { animation-duration: 2s; } }

/* the peek's rows carry the shared adp-row anatomy; the clock column + tags are
   this tag's, tenant-neutral (colour arrives as --adp-accent, bound below) */
.acal-pk-row {
  display: grid; grid-template-columns: auto 1fr; gap: 2px var(--space-2);
  align-items: baseline; width: 100%; text-align: left;
  padding: var(--space-2) var(--space-2) var(--space-2) var(--space-3);
  border: 0; background: none; color: inherit; cursor: pointer;
  border-bottom: 1px solid var(--border-subtle);
}
.acal-pk-row[data-noclock] { grid-template-columns: 1fr; }
.acal-pk-row:hover { background: var(--surface-hover); }
.acal-pk-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.acal-pk-time { grid-row: 1 / span 2; font-size: var(--font-sm); color: var(--text-secondary); font-variant-numeric: tabular-nums; white-space: nowrap; }
.acal-pk-time em { font-style: normal; color: var(--text-muted); }
.acal-pk-t { font-size: var(--font-sm); font-weight: 600; color: var(--text-primary); }
.acal-pk-row[data-done] .acal-pk-t { text-decoration: line-through; color: var(--text-muted); }
.acal-pk-meta { font-size: var(--font-xs); color: var(--text-secondary); }
/* --adp-accent is bound from whatever swatch var the host declared (set inline
   on each row); the theme has already resolved the swatch attr into that var. */

/* THE MOBILE FILTERS SHEET — the rail NODE moves in here <1000px, so it keeps
   its checkbox + collapse state across the move rather than being rebuilt. */
.acal-sheet[hidden] { display: none; }
.acal-sheet { position: fixed; inset: 0; z-index: var(--z-modal, 1000); }
.acal-sheet-scrim { position: absolute; inset: 0; background: rgba(0,0,0,.4); }
.acal-sheet-panel {
  position: absolute; left: 0; right: 0; bottom: 0;
  max-height: 86dvh; overflow: auto;
  background: var(--surface-card);
  border-radius: var(--radius-xl) var(--radius-xl) 0 0;
  padding: var(--space-3) var(--space-4) calc(var(--space-4) + var(--safe-bottom));
  box-shadow: var(--shadow-xl);
}
.acal-sheet-grab { width: 40px; height: 4px; border-radius: var(--radius-full); background: var(--border-strong); margin: 4px auto var(--space-3); }

able-calendar able-empty-state { display: block; padding: var(--space-6) var(--space-4); }


/* able-cart-drawer */

able-cart-drawer {
  position: fixed; top: 0; bottom: 0; right: 0;
  width: 420px; max-width: 100vw;
  z-index: 901;
  background: var(--surface-card, #ffffff);
  color: var(--text-primary, #1d1c17);
  box-shadow: -12px 0 36px rgba(0, 0, 0, 0.18);
  transform: translateX(100%);
  transition: transform 240ms cubic-bezier(0.2, 0.8, 0.2, 1);
  display: flex; flex-direction: column;
  overflow: hidden;
}
able-cart-drawer[data-open="true"] { transform: translateX(0); }
@media (max-width: 479px) {
  able-cart-drawer {
    width: 100%;
    top: auto; right: 0; left: 0; bottom: 0;
    height: 90dvh; max-height: 90dvh;
    transform: translateY(100%);
    border-top-left-radius: 16px;
    border-top-right-radius: 16px;
    box-shadow: 0 -12px 36px rgba(0, 0, 0, 0.18);
  }
  able-cart-drawer[data-open="true"] { transform: translateY(0); }
  able-cart-drawer::before {
    content: '';
    position: absolute;
    top: 8px;
    left: 50%;
    transform: translateX(-50%);
    width: 40px;
    height: 4px;
    background: rgba(0, 0, 0, 0.18);
    border-radius: 2px;
    pointer-events: none;
  }
}
.acd-backdrop {
  position: fixed; inset: 0; z-index: 900;
  background: rgba(11, 15, 26, 0.45);
  opacity: 0; pointer-events: none;
  transition: opacity 220ms ease;
}
.acd-backdrop--open { opacity: 1; pointer-events: auto; }
.acd-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: 16px 20px;
  border-bottom: 1px solid var(--border-subtle, #f1f5f9);
  flex-shrink: 0;
  padding-top: max(16px, env(safe-area-inset-top));
}
.acd-title {
  font-size: 16px; font-weight: 600;
  display: flex; align-items: center; gap: 8px;
  margin: 0;
}
.acd-count {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 22px; height: 22px; padding: 0 6px;
  background: var(--accent, #1b2a4a); color: var(--on-accent, #fff);
  border-radius: 11px;
  font-size: 12px; font-weight: 600;
}
.acd-close {
  width: 44px; height: 44px; border-radius: 8px;
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent; color: inherit; border: 0; cursor: pointer;
  font-size: 24px; line-height: 1;
}
.acd-close:hover { background: rgba(0, 0, 0, 0.05); }
.acd-body {
  flex: 1 1 auto;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 8px 0;
}
.acd-empty {
  display: flex; flex-direction: column; align-items: center;
  justify-content: center;
  padding: 48px 24px;
  color: var(--text-muted, #6b7280);
  text-align: center;
  gap: 12px;
}
.acd-empty-icon {
  font-size: 48px;
  opacity: 0.4;
}
.acd-empty-text {
  font-size: 14px;
  margin: 0;
}
.acd-footer {
  flex-shrink: 0;
  border-top: 1px solid var(--border-subtle, #f1f5f9);
  padding: 16px 20px;
  padding-bottom: max(16px, env(safe-area-inset-bottom));
  background: var(--surface-base, #ffffff);
  display: flex; flex-direction: column; gap: 14px;
}
.acd-checkout {
  min-height: 48px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--accent, #1b2a4a); color: var(--on-accent, #fff);
  border: 0; border-radius: 8px;
  font-size: 15px; font-weight: 600;
  cursor: pointer;
  text-decoration: none;
  padding: 0 20px;
  transition: background 160ms ease;
}
.acd-checkout:hover { background: var(--accent-hover, #16223d); }
.acd-checkout:active { background: var(--accent-active, #111a2e); }
.acd-checkout[aria-disabled="true"] {
  background: var(--surface-active, #e5e7eb);
  color: var(--text-muted, #6b7280);
  pointer-events: none;
  cursor: not-allowed;
}


/* able-cart-line */

able-cart-line {
  display: block;
  border-bottom: 1px solid var(--border-subtle, #f1f5f9);
}
able-cart-line:last-child { border-bottom: 0; }
.acl-line {
  display: grid;
  grid-template-columns: 64px 1fr auto;
  grid-template-rows: auto auto;
  grid-template-areas:
    "image body remove"
    "image qty   price";
  gap: 4px 12px;
  align-items: start;
  padding: 14px 20px;
}
.acl-image {
  grid-area: image;
  width: 64px; height: 64px;
  border-radius: 8px;
  overflow: hidden;
  background: var(--surface-subtle, #f1f5f9);
}
.acl-image img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}
.acl-body {
  grid-area: body;
  min-width: 0;
}
.acl-name {
  display: block;
  font-size: 14px;
  font-weight: 500;
  color: var(--text-primary, #1d1c17);
  text-decoration: none;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.acl-name:hover { text-decoration: underline; }
.acl-variant {
  font-size: 12px;
  color: var(--text-secondary, #3f3f46);
  margin: 2px 0 0;
}
.acl-sku {
  font-size: 11px;
  color: var(--text-muted, #6b7280);
  margin: 2px 0 0;
  letter-spacing: 0.02em;
}
.acl-qty {
  grid-area: qty;
  display: flex; align-items: center;
}
.acl-price {
  grid-area: price;
  display: flex; align-items: center; justify-content: flex-end;
  font-size: 14px; font-weight: 600;
  color: var(--text-primary, #1d1c17);
  font-variant-numeric: tabular-nums;
}
.acl-remove {
  grid-area: remove;
  width: 32px; height: 32px;
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent; border: 0; cursor: pointer;
  font-size: 20px; color: var(--text-muted, #6b7280);
  border-radius: 6px;
  align-self: flex-start;
  justify-self: end;
}
.acl-remove:hover {
  background: var(--danger-soft, #fee2e2);
  color: var(--danger, #b91c1c);
}
.acl-line--empty { display: none; }


/* able-cart-summary */

able-cart-summary { display: block; }
.acs {
  display: flex; flex-direction: column;
  gap: 8px;
  width: 100%;
}
.acs-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  font-size: 14px;
  color: var(--text-secondary, #3f3f46);
  font-variant-numeric: tabular-nums;
}
.acs-label { font-weight: 400; }
.acs-discount-row { color: var(--success, #15803d); }
.acs-savings {
  color: var(--success, #15803d);
  font-weight: 500;
}
.acs-shipping-free {
  color: var(--success, #15803d);
  font-weight: 600;
  letter-spacing: 0.01em;
  text-transform: none;
}
.acs-total-row {
  font-size: 16px;
  font-weight: 700;
  color: var(--text-primary, #1d1c17);
  border-top: 1px solid var(--border-default, #e5e7eb);
  padding-top: 12px;
  margin-top: 4px;
}
.acs-total-row .acs-label { font-weight: 700; }
.acs-total-row able-price { font-size: 18px; }


/* able-checkout-stepper */

able-checkout-stepper { display: block; }
.achs-wrap { width: 100%; }
.achs--desktop {
  display: flex;
  list-style: none;
  padding: 0; margin: 0;
  gap: 12px;
  align-items: center;
  justify-content: center;
}
.achs--mobile { display: none; }
@media (max-width: 639px) {
  .achs--desktop { display: none; }
  .achs--mobile { display: block; }
}
.achs-step {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  flex: 0 0 auto;
  position: relative;
  font-size: 13px;
  font-weight: 500;
  color: var(--text-muted, #6b7280);
  user-select: none;
}
.achs-step + .achs-step::before {
  content: '';
  display: inline-block;
  width: 32px; height: 1px;
  background: var(--border-default, #e5e7eb);
  margin-right: 8px;
  vertical-align: middle;
}
.achs-step[data-state="completed"] + .achs-step::before {
  background: var(--accent, #1b2a4a);
}
.achs-num {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px;
  border-radius: 50%;
  background: var(--surface-subtle, #f1f5f9);
  color: var(--text-muted, #6b7280);
  font-size: 12px; font-weight: 600;
  border: 1px solid var(--border-default, #e5e7eb);
  transition: background 160ms ease, color 160ms ease, border-color 160ms ease;
}
.achs-step[data-state="completed"] .achs-num {
  background: var(--accent, #1b2a4a);
  color: var(--on-accent, #fff);
  border-color: var(--accent, #1b2a4a);
}
.achs-step[data-state="current"] .achs-num {
  background: var(--surface-base, #ffffff);
  color: var(--accent-text, var(--accent, #1b2a4a));
  border-color: var(--accent, #1b2a4a);
  font-weight: 700;
}
.achs-step[data-state="current"] {
  color: var(--text-primary, #1d1c17);
}
.achs-step[data-state="completed"] {
  color: var(--text-secondary, #3f3f46);
}
.achs-step[data-clickable="true"] {
  cursor: pointer;
}
.achs-step[data-clickable="true"]:hover .achs-num {
  background: var(--accent-hover, #16223d);
  border-color: var(--accent-hover, #16223d);
}
.achs-label { white-space: nowrap; }
.achs--mobile {
  display: none;
  padding: 12px 4px;
}
@media (max-width: 639px) { .achs--mobile { display: block; } }
.achs-mobile-text {
  display: block;
  font-size: 13px;
  font-weight: 600;
  color: var(--text-primary, #1d1c17);
  margin-bottom: 8px;
}
.achs-progress {
  width: 100%;
  height: 4px;
  background: var(--surface-subtle, #f1f5f9);
  border-radius: 2px;
  overflow: hidden;
}
.achs-progress-fill {
  height: 100%;
  background: var(--accent, #1b2a4a);
  transition: width 280ms ease;
}


/* able-claw */

able-claw {
  /* Layout knobs */
  --ableclaw-sidebar-width:      240px;
  --ableclaw-artifact-width:     360px;
  --ableclaw-mobile-breakpoint:  720px;
  --ableclaw-radius:             12px;
  --ableclaw-radius-sm:          8px;

  /* Surfaces — every var resolves through theme tokens first; fallback is
     the dark default that ships with the legacy scanner/asuite themes. To
     opt into light mode in your theme, set --surface-card and friends
     to light values under your [data-theme='light'] block; the blueprint
     will follow automatically. */
  --ableclaw-bg:                 var(--surface-card, #141415);
  --ableclaw-fg:                 var(--text-primary, #e2e8f0);
  --ableclaw-sidebar-bg:         var(--surface-subtle, var(--surface-card, rgba(0, 0, 0, 0.18)));
  --ableclaw-sidebar-fg:         var(--text-primary, #e2e8f0);
  --ableclaw-sidebar-active-bg:  var(--surface-hover, var(--surface-subtle, rgba(127, 127, 127, 0.18)));
  --ableclaw-sidebar-hover-bg:   var(--surface-hover, rgba(127, 127, 127, 0.10));
  --ableclaw-artifact-bg:        var(--surface-subtle, rgba(0, 0, 0, 0.18));
  --ableclaw-border:             var(--border-default, rgba(127, 127, 127, 0.20));
  --ableclaw-border-soft:        var(--border-default, rgba(127, 127, 127, 0.12));

  /* Chat bubbles */
  --ableclaw-bubble-user-bg:     var(--accent-secondary, #3b82f6);
  --ableclaw-bubble-user-fg:     var(--on-accent-secondary, #ffffff);
  --ableclaw-bubble-agent-bg:    var(--surface-subtle, rgba(127, 127, 127, 0.10));
  --ableclaw-bubble-agent-fg:    var(--text-primary, #e2e8f0);

  /* Input bar */
  --ableclaw-input-bg:           var(--surface-card, #141415);
  --ableclaw-input-fg:           var(--text-primary, #e2e8f0);
  --ableclaw-input-border:       var(--border-default, rgba(127, 127, 127, 0.20));
  --ableclaw-input-bar-bg:       var(--surface-subtle, rgba(0, 0, 0, 0.12));

  /* Code + tables (the bug-D surface) */
  --ableclaw-code-bg:            rgba(0, 0, 0, 0.4);
  --ableclaw-code-fg:            inherit;
  --ableclaw-table-header-bg:    rgba(99, 102, 241, 0.18);
  --ableclaw-table-header-fg:    var(--text-primary, #f1f5f9);
  --ableclaw-table-row-bg:       transparent;
  --ableclaw-table-border:       var(--border-default, rgba(255, 255, 255, 0.18));

  /* Buttons + accents */
  --ableclaw-link:               #93c5fd;
  --ableclaw-accent:             var(--accent-secondary, #3b82f6);
  /* ⚠ --ableclaw-accent is a FILL token (backgrounds, borders, rings, shadows).
     It aliases --accent-secondary, which does NOT re-bind for dark, so painting
     TEXT with it measured 2.54–2.66:1 on the dark card in 17 of the fleet's 23
     themes — under the 4.5:1 AA floor. --ableclaw-accent-text is the type-only
     twin: it rides --accent-secondary-text (system-base-theme), which lightens
     the tenant's OWN secondary hue for dark, so the brand survives. Use it for
     every accent-coloured STRING or glyph; keep --ableclaw-accent for fills.
     NOT the same as --ableclaw-accent-fg, which is text ON an accent fill. */
  --ableclaw-accent-text:        var(--accent-secondary-text, var(--accent-secondary, #3b82f6));
  --ableclaw-accent-fg:          #ffffff;
  --ableclaw-stop-bg:            #dc2626;
  --ableclaw-stop-bg-hover:      #b91c1c;

  /* Conversation channel badges (dot to the left of each row title).
     Host pages rebrand by overriding any of these. Channel keys map to
     ai.conversations.channel_type values (case-insensitive — ABLEUI and
     ableui both resolve to the default). */
  --ableclaw-channel-default:    #6366f1;   /* ABLEUI / ableui — indigo */
  --ableclaw-channel-telegram:   #38bdf8;   /* cyan */
  --ableclaw-channel-email:      #94a3b8;   /* slate */
  --ableclaw-channel-slack:      #c026d3;   /* magenta */
  --ableclaw-channel-sms:        #22c55e;   /* green (also whatsapp) */
  --ableclaw-channel-web:        #64748b;   /* gray */
  --ableclaw-channel-voice:      #f59e0b;   /* amber — spoken (voice) turns */

  /* Host height clamp.
     ============================================================================
     <able-claw> MUST have a bounded height for its internal grid to lay out
     correctly. Without this, an unbounded parent (height:auto, min-height:100vh)
     lets the sidebar's conversation list inflate the host past the viewport,
     pushing the chat input row BELOW the fold.
     Strategy: clamp the host to 100dvh minus an optional offset for chrome
     above us (a sticky page header, an outer padding, etc). Host pages
     calibrate the offset via the --ableclaw-viewport-offset custom property:
         <style>able-claw { --ableclaw-viewport-offset: 60px; }</style>
     Default 0 → host fills the full dynamic viewport (correct for pages with
     no chrome above <able-claw>).
     100dvh tracks the dynamic viewport (mobile address bar collapse) better
     than 100vh. contain: size tells the browser our intrinsic height is
     independent of content — internal grid scrolls instead of inflating us. */
  --ableclaw-viewport-offset:    0px;

  display: block;
  width: 100%;
  height: calc(100dvh - var(--ableclaw-viewport-offset));
  max-height: calc(100dvh - var(--ableclaw-viewport-offset));
  min-height: 480px;
  color: var(--ableclaw-fg);
  box-sizing: border-box;
  /* contain: layout only — NOT size or style. size caused grid-snap
     glitches when toggling data-sidebar-collapsed (chat column went 0-width
     during the grid-template-columns transition). overflow: hidden is
     what actually prevents content from inflating the host. */
  contain: layout;
  overflow: hidden;
}
/* When the host explicitly opts out by setting --ableclaw-viewport-offset
   to a non-zero value AND the parent has its own definite height, the
   100dvh clamp still wins. Host pages with a SHORTER definite container
   (e.g. a 600px-tall card) should use height:100% on a wrapper and pass
   the offset that accounts for the shell + their wrapper math. */
.aclaw-grid {
  display: grid;
  grid-template-columns: var(--ableclaw-sidebar-width) 1fr 0fr;
  width: 100%;
  height: 100%;
  min-height: 0;
  min-width: 0;
  background: var(--ableclaw-bg);
  border: 1px solid var(--ableclaw-border-soft);
  border-radius: var(--ableclaw-radius);
  overflow: hidden;
  /* Anchor point for the floating Agent Thinking overlay used in
     helper/launcher modes (it positions: absolute against this box). */
  position: relative;
  /* NO grid-template-columns transition — Chrome's interpolation between
     "var(--w) 1fr 0fr" and "0 1fr 0fr" left the chat column at 0 width
     mid-flight, presenting as a blank screen. Snap is fine; the visual
     change is intentional and instant. */
  box-sizing: border-box;
}
.aclaw-grid:has(.aclaw-artifact:not([hidden])) { grid-template-columns: var(--ableclaw-sidebar-width) 1fr var(--ableclaw-artifact-width); }
/* Collapsed state — driven by the data-sidebar-collapsed attribute on the
   host element. We deliberately use ONLY this single selector path (the
   older .aclaw-grid--sidebar-collapsed class is no longer toggled by JS;
   see _setSidebarCollapsed). Rules are tightly scoped: hide ONLY the
   .aclaw-sidebar element. .aclaw-chat (the main content column) and
   .aclaw-artifact (when visible) MUST remain rendered — the chat column
   expands to fill the freed grid track. */
able-claw[data-sidebar-collapsed="true"] > .aclaw-grid {
  grid-template-columns: 0 1fr 0fr;
}
able-claw[data-sidebar-collapsed="true"] > .aclaw-grid:has(.aclaw-artifact:not([hidden])) {
  grid-template-columns: 0 1fr var(--ableclaw-artifact-width);
}
/* When collapsed, the sidebar's grid column is 0px (see grid-template-columns
   above), but we MUST NOT use display:none — that removes the element from
   the grid placement entirely, which shifts the .aclaw-chat element into
   the now-empty column 1 (also 0px). Result: chat width collapses to 0 and
   the page renders empty. visibility:hidden + pointer-events:none keeps the
   sidebar element in flow so the chat stays anchored in grid column 2. */
able-claw[data-sidebar-collapsed="true"] > .aclaw-grid > .aclaw-sidebar {
  visibility: hidden;
  pointer-events: none;
  overflow: hidden;
}
/* Bug #6 (desktop) — when the sidebar is collapsed, the floating expand
   (hamburger) button sits absolutely at the chat's top-left over the message
   stream. Without clearance, the agent greeting's ONLINE row collides with it
   (and the brain toggle at top-right). The mobile @media already pads the
   stream; this collapsed-state rule extends the same clearance to DESKTOP
   (where the user collapsed the sidebar). Specificity (0,2,1) beats the later
   Liquid-Glass able-claw .aclaw-messages rule; only fires while collapsed so
   the expanded desktop layout is untouched. */
able-claw[data-sidebar-collapsed="true"] .aclaw-messages { padding-top: 60px; }
able-claw[data-sidebar-collapsed="true"] .aclaw-welcome  { padding-top: 60px; }

.aclaw-sidebar {
  display: flex; flex-direction: column;
  background: var(--ableclaw-sidebar-bg);
  color: var(--ableclaw-sidebar-fg);
  border-right: 1px solid var(--ableclaw-border-soft);
  min-height: 0;
  min-width: 0;
  /* No overflow:hidden — agent-popover (absolute in header) needs to extend
     below the header. The conversation list inside has its own overflow-y. */
}
.aclaw-sidebar-header {
  padding: 12px;
  border-bottom: 1px solid var(--ableclaw-border-soft);
  display: flex; flex-direction: column; gap: 8px;
  position: relative;
  flex-shrink: 0;
}
.aclaw-sidebar-header-row {
  display: flex; align-items: center; gap: 6px;
}
.aclaw-new-chat {
  flex: 1;
  padding: 8px 12px;
  background: var(--ableclaw-accent); color: var(--ableclaw-accent-fg);
  border: 0; border-radius: var(--ableclaw-radius-sm); cursor: pointer; font-size: 13px; font-weight: 600;
  transition: opacity 120ms;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-width: 0;
}
.aclaw-new-chat:hover { opacity: 0.92; }
.aclaw-kbd-hint {
  display: inline-flex; align-items: center;
  padding: 2px 6px;
  font-size: 11px; font-weight: 500;
  background: rgba(255, 255, 255, 0.18);
  border-radius: 4px;
  letter-spacing: 0.02em;
  font-variant-numeric: tabular-nums;
}

/* Sidebar brand header — small AbleClaw label + agent welcome title that
   sits above the New Conversation button. Mirrors the target design. */
.aclaw-sidebar-brand {
  display: flex; align-items: center; gap: 10px;
  padding: 4px 4px 8px 4px;
  border-bottom: 1px dashed var(--border-default, rgba(255, 255, 255, 0.06));
  margin-bottom: 4px;
}
.aclaw-sidebar-brand-icon {
  width: 28px; height: 28px; border-radius: 8px;
  display: inline-flex; align-items: center; justify-content: center;
  background: linear-gradient(135deg, #a78bfa, #7c3aed);
  color: #fff; font-size: 14px;
  flex: 0 0 auto;
}
.aclaw-sidebar-brand-text {
  display: flex; flex-direction: column; min-width: 0; line-height: 1.2;
}
.aclaw-sidebar-brand-text strong {
  font-size: 12px; font-weight: 700; letter-spacing: 0.02em;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.aclaw-sidebar-brand-text span {
  font-size: 11px; opacity: 0.65;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.aclaw-sidebar-collapse {
  flex: 0 0 auto;
  width: 32px; height: 32px;
  background: transparent; color: inherit;
  border: 1px solid var(--ableclaw-border);
  border-radius: var(--ableclaw-radius-sm); cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 14px; line-height: 1;
  opacity: 0.7;
  transition: opacity 120ms, background-color 120ms;
}
.aclaw-sidebar-collapse:hover { opacity: 1; background: var(--ableclaw-sidebar-hover-bg); }
.aclaw-sidebar-expand {
  position: absolute; top: 12px; left: 12px;
  width: 32px; height: 32px;
  background: var(--surface-subtle, var(--ableclaw-sidebar-bg));
  color: var(--text-primary, inherit);
  border: 1px solid var(--border-default, var(--ableclaw-border));
  border-radius: var(--ableclaw-radius-sm); cursor: pointer;
  /* Hidden by default; flipped to inline-flex when the host carries
     data-sidebar-collapsed="true". !important guards against page-level
     CSS that might otherwise overdetermine the display property. */
  display: none; align-items: center; justify-content: center;
  font-size: 16px; font-weight: 600; line-height: 1;
  opacity: 0.95; z-index: 50;
  pointer-events: auto;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.18);
  transition: opacity 120ms, background-color 120ms, transform 120ms;
}
able-claw[data-sidebar-collapsed="true"] .aclaw-sidebar-expand { display: inline-flex !important; }
@media (hover: hover) and (pointer: fine) {
  .aclaw-sidebar-expand:hover {
    opacity: 1;
    background: var(--surface-card, var(--ableclaw-sidebar-active-bg));
    transform: translateX(1px);
  }
}
/* Collapsed-rail NEW CHAT — sits directly under the hamburger (12 + 32 + 8), so
   the rail reads top-down as "open the list / start a new one". Same box, same
   tokens, same collapsed-only display rule as the expander above: the rail is one
   control stack and a second visual language in it would just look like a bug. */
.aclaw-rail-new-chat {
  position: absolute; top: 52px; left: 12px;
  width: 32px; height: 32px;
  background: var(--surface-subtle, var(--ableclaw-sidebar-bg));
  color: var(--text-primary, inherit);
  border: 1px solid var(--border-default, var(--ableclaw-border));
  border-radius: var(--ableclaw-radius-sm); cursor: pointer;
  display: none; align-items: center; justify-content: center;
  line-height: 1;
  opacity: 0.95; z-index: 50;
  pointer-events: auto;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.18);
  transition: opacity 120ms, background-color 120ms, transform 120ms;
}
able-claw[data-sidebar-collapsed="true"] .aclaw-rail-new-chat { display: inline-flex !important; }
@media (hover: hover) and (pointer: fine) {
  .aclaw-rail-new-chat:hover {
    opacity: 1;
    background: var(--surface-card, var(--ableclaw-sidebar-active-bg));
    transform: translateX(1px);
  }
}
.aclaw-rail-new-chat:active { transform: scale(0.96); }
.aclaw-search {
  width: 100%;
  padding: 6px 10px;
  background: var(--surface-card, #141415); color: inherit;
  border: 1px solid var(--border-default, rgba(255, 255, 255, 0.10));
  border-radius: 6px;
  font: inherit; font-size: 12px;
  box-sizing: border-box;
}
.aclaw-search:focus {
  outline: none;
  border-color: var(--accent-secondary, #3b82f6);
}

/* TALKING TO — agent-selector dropdown trigger in sidebar header. */
.aclaw-talking-to {
  display: flex; flex-direction: column; gap: 6px;
}
.aclaw-talking-to-label {
  font-size: 10px; font-weight: 600; letter-spacing: 0.10em;
  text-transform: uppercase;
  opacity: 0.55;
  padding: 0 2px;
}
.aclaw-agent-selector {
  display: grid; grid-template-columns: auto 1fr auto; align-items: center;
  gap: 10px;
  width: 100%;
  padding: 8px 10px;
  background: var(--surface-card, rgba(255, 255, 255, 0.04));
  color: inherit;
  border: 1px solid var(--border-default, rgba(255, 255, 255, 0.10));
  border-radius: 8px; cursor: pointer;
  text-align: left;
  transition: background-color 120ms, border-color 120ms;
}
.aclaw-agent-selector:hover {
  background: rgba(255, 255, 255, 0.07);
  border-color: var(--accent-secondary, #3b82f6);
}
.aclaw-agent-selector .aclaw-agent-avatar {
  width: 28px; height: 28px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 12px; font-weight: 700; color: #fff;
  background: var(--accent-secondary, #3b82f6);
  flex: 0 0 auto;
}
.aclaw-agent-selector .aclaw-agent-meta {
  min-width: 0;
  display: flex; flex-direction: column; gap: 1px;
}
.aclaw-agent-selector .aclaw-agent-name {
  font-size: 13px; font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.aclaw-agent-selector .aclaw-agent-tagline {
  font-size: 11px; opacity: 0.6;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.aclaw-agent-selector .aclaw-agent-chevron {
  flex: 0 0 auto;
  font-size: 14px; opacity: 0.5;
}

/* MODEL override picker — compact row under the agent selector. A/B a
   different model on the same agent; selection persists per agent. */
.aclaw-model-pick {
  display: flex; align-items: center; gap: 8px;
  margin-top: 6px; padding: 0 2px;
}
.aclaw-model-pick-label {
  font-size: 9px; font-weight: 700; letter-spacing: 0.08em;
  opacity: 0.55; flex: none;
}
.aclaw-model-select {
  flex: 1; min-width: 0;
  font-size: 11px; padding: 3px 6px;
  border-radius: 6px;
  border: 1px solid var(--ableclaw-sidebar-border, rgba(127,127,127,0.25));
  background: var(--ableclaw-sidebar-bg, transparent);
  color: inherit;
  cursor: pointer;
}
.aclaw-model-select:hover { border-color: var(--ableclaw-accent, #2563eb); }

/* ── Composer MODEL·EFFORT chip + menu (the Claude-style picker on the input
   bar). The chip shows the effective model (+ effort when overridden); the
   menu lists the FEATURED models with taglines and an Effort submenu. Shares
   the _modelOverride state with the sidebar select. ── */
.aclaw-model-chip {
  flex: none; display: inline-flex; align-items: center; gap: 6px;
  font-size: 12px; font-weight: 600;
  padding: 6px 10px; border-radius: 999px;
  border: 1px solid var(--border-default, rgba(127,127,127,0.25));
  background: var(--surface-subtle, rgba(127,127,127,0.08));
  color: inherit; cursor: pointer; white-space: nowrap;
  max-width: 220px; overflow: hidden; text-overflow: ellipsis;
}
.aclaw-model-chip:hover { border-color: var(--ableclaw-accent, #2563eb); }
.aclaw-model-chip .amc-effort { opacity: 0.6; font-weight: 500; }
.aclaw-model-chip .amc-caret { opacity: 0.5; font-size: 10px; }
.aclaw-model-menu {
  position: absolute; right: 8px; bottom: calc(100% + 8px);
  min-width: 280px; max-width: 340px; max-height: 420px; overflow-y: auto;
  background: var(--surface-card, #1a1a1c);
  border: 1px solid var(--border-default, rgba(255,255,255,0.12));
  border-radius: 12px; padding: 6px;
  box-shadow: 0 12px 32px rgba(0,0,0,0.35);
  z-index: 30; display: flex; flex-direction: column; gap: 2px;
}
.aclaw-model-menu[hidden] { display: none !important; }
.aclaw-mm-item {
  display: grid; grid-template-columns: 1fr auto; align-items: center; gap: 10px;
  text-align: left; width: 100%;
  padding: 8px 10px; border: none; border-radius: 8px;
  background: transparent; color: inherit; cursor: pointer;
}
.aclaw-mm-item:hover { background: var(--surface-hover, rgba(127,127,127,0.12)); }
.aclaw-mm-item .amm-name { font-size: 13px; font-weight: 600; }
.aclaw-mm-item .amm-desc { font-size: 11px; opacity: 0.6; margin-top: 1px; }
.aclaw-mm-item .amm-check { color: var(--ableclaw-accent-text, #2563eb); font-weight: 700; }
.aclaw-mm-divider {
  height: 1px; margin: 4px 6px;
  background: var(--border-default, rgba(127,127,127,0.2));
}
.aclaw-mm-hint {
  font-size: 11px; opacity: 0.6; padding: 6px 10px 4px; line-height: 1.4;
}
/* the menu anchors to the input bar (relative w/o offsets = zero layout change) */
.aclaw-input-bar { position: relative; }

/* ── Composer MODE segmented control — the 2-mode switch fed by the agent
   ui-config's "modes" list. Sits BOTTOM-LEFT of the two-row composer, right
   after the attach button (the Claude Chat/Cowork position), and is the
   PRIMARY control; the model chip demotes to the "Advanced" escape hatch on
   the bottom-right (see [data-has-modes] below). An agent whose ui-config
   carries no modes never renders this — zero layout change.
   NOTE: no backticks in this block — it lives inside a template literal. ── */
.aclaw-mode-seg {
  flex: 0 1 auto; display: inline-flex; align-items: center; gap: 2px;
  padding: 2px; border-radius: 999px;
  border: 1px solid var(--border-default, rgba(127,127,127,0.25));
  background: var(--surface-subtle, rgba(127,127,127,0.08));
  max-width: 260px; min-width: 0; overflow: hidden;
}
.aclaw-mode-opt {
  flex: 0 1 auto; border: none; border-radius: 999px;
  padding: 4px 11px; font-size: 12px; font-weight: 600;
  background: transparent; color: inherit; cursor: pointer;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  min-width: 0; max-width: 124px;
}
/* the seg declares display:inline-flex, which beats the UA [hidden] rule —
   restate it so a hidden (no-modes agent) control really disappears */
.aclaw-mode-seg[hidden] { display: none !important; }
.aclaw-mode-opt:hover { background: var(--surface-hover, rgba(127,127,127,0.12)); }
.aclaw-mode-opt[aria-pressed="true"] {
  background: var(--ableclaw-accent, #2563eb); color: #fff;
}
.aclaw-mode-opt[aria-pressed="true"]:hover { background: var(--ableclaw-accent, #2563eb); }
/* Advanced demotion — the model+effort chip stays reachable but stops
   competing with the mode switch for attention. */
.aclaw-input-bar[data-has-modes="true"] .aclaw-model-chip {
  font-weight: 500; opacity: 0.7; max-width: 150px;
}
.aclaw-input-bar[data-has-modes="true"] .aclaw-model-chip:hover { opacity: 1; }
@media (max-width: 720px) {
  .aclaw-mode-opt { max-width: 92px; padding: 4px 9px; }
  .aclaw-mode-seg { max-width: 200px; }
}
/* Phone widths — the bottom row carries 5 controls and they genuinely do not
   fit on one line (measured: ~390px of controls into 327px at 375w), so the
   row wraps and the right cluster takes its own right-aligned line. Given
   that, the caps here are NOT trying to force one line — they only keep each
   control from running long, and they stay ABOVE the natural label width so
   nothing clips ("Study Mode" measures ~75px at this size). */
@media (max-width: 480px) {
  /* .aclaw-chat-qualified (0,2,1) so these WIN over the Liquid-Glass
     able-claw .aclaw-input-bar rule (0,1,1), which appears later in source and
     owns the desktop inset and margin. On a phone the control row wraps, so
     every pixel of inset is real estate: 8 above the typed line, 6 under the
     controls, 6 between the rows; 6 above the pill, 8 of side gutter, 10 below
     (the safe-area inset supplies the rest of the bottom air). */
  able-claw .aclaw-chat .aclaw-input-bar { padding: 8px 8px 6px; }
  able-claw .aclaw-chat .aclaw-input-bar { gap: 6px; }
  able-claw .aclaw-chat .aclaw-input-bar { margin: 6px 8px 10px; }
  .aclaw-mode-opt { max-width: 96px; padding: 4px 8px; font-size: 11px; }
  .aclaw-mode-seg { max-width: 204px; }
  .aclaw-model-chip { max-width: 120px; padding: 6px 8px; font-size: 11px; }
  .aclaw-input-bar[data-has-modes="true"] .aclaw-model-chip { max-width: 104px; }
  .aclaw-model-chip .amc-effort { display: none; }
}

.aclaw-agent-popover {
  position: absolute;
  background: var(--surface-card, #1a1a1c);
  border: 1px solid var(--border-default, rgba(255, 255, 255, 0.12));
  border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.40);
  padding: 6px;
  z-index: 10;
  display: flex; flex-direction: column; gap: 2px;
  max-height: 320px; overflow-y: auto;
  min-width: 220px;
}
.aclaw-agent-popover[hidden] { display: none !important; }
.aclaw-agent-option {
  display: grid; grid-template-columns: auto 1fr; align-items: center;
  gap: 10px;
  padding: 8px 10px;
  background: transparent; color: inherit; border: 0; cursor: pointer;
  text-align: left;
  border-radius: 6px;
  transition: background-color 120ms;
}
.aclaw-agent-option:hover { background: var(--ableclaw-sidebar-hover-bg, rgba(127, 127, 127, 0.10)); }
.aclaw-agent-option.is-active { background: var(--ableclaw-sidebar-active-bg, rgba(127, 127, 127, 0.18)); }
.aclaw-agent-option .aclaw-agent-avatar {
  width: 26px; height: 26px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 11px; font-weight: 700; color: #fff;
  background: var(--accent-secondary, #3b82f6);
  flex: 0 0 auto;
}
.aclaw-agent-option .aclaw-agent-meta {
  min-width: 0;
  display: flex; flex-direction: column; gap: 1px;
}
.aclaw-agent-option .aclaw-agent-name {
  font-size: 13px; font-weight: 500;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.aclaw-agent-option .aclaw-agent-tagline {
  font-size: 11px; opacity: 0.6;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* YOUR CHATS group header + day-bucket sub-headers. Static labels rendered
   inside the conversation list; bucket headers are emitted between rows
   when conversations span multiple recency windows. */
.aclaw-conversation-group-header {
  font-size: 10px; font-weight: 600; letter-spacing: 0.10em;
  text-transform: uppercase;
  opacity: 0.55;
  padding: 8px 10px 4px 10px;
}
.aclaw-conversation-bucket-header {
  font-size: 11px; font-weight: 500;
  opacity: 0.45;
  padding: 8px 10px 2px 10px;
  text-transform: uppercase; letter-spacing: 0.05em;
  display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
}

/* ── sidebar multi-select (batch delete, 2026-08-14) ─────────────────────
   Select mode swaps each row's glyph for a check slot and the per-row hover
   actions disappear (one gesture: pick rows, one Delete). Bucket headers
   grow a "day" toggle — the literal ask was "borrar todas las de un día". */
.aclaw-conv-select-toggle,
.aclaw-bucket-select,
.aclaw-conv-trash-back {
  border: 0; background: transparent; cursor: pointer;
  font: inherit; font-size: 10px; letter-spacing: 0.06em;
  color: var(--ableclaw-accent-text, #6366f1);
  opacity: 0.85; padding: 0 2px; text-transform: uppercase;
}
.aclaw-conv-select-toggle:hover, .aclaw-bucket-select:hover, .aclaw-conv-trash-back:hover { opacity: 1; text-decoration: underline; }
.aclaw-conversation-group-header {
  display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
}
.aclaw-conversation-list[data-select-mode] .aclaw-conv-actions { display: none !important; }
.aclaw-conversation-list[data-select-mode] .aclaw-conv-time { display: none; }
.aclaw-conv-check {
  width: 15px; height: 15px; flex: 0 0 15px; border-radius: 4px;
  border: 1.5px solid var(--ableclaw-sidebar-muted, rgba(127,127,127,0.55));
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 11px; line-height: 1; color: transparent;
}
.aclaw-conversation-row.is-checked .aclaw-conv-check {
  background: var(--ableclaw-accent, #6366f1);
  border-color: var(--ableclaw-accent, #6366f1);
  color: #fff;
}
.aclaw-conversation-row.is-checked {
  background: var(--ableclaw-sidebar-hover-bg, rgba(99, 102, 241, 0.10));
}
.aclaw-select-bar {
  position: sticky; bottom: 0;
  display: flex; align-items: center; gap: 10px;
  padding: 8px 10px; margin-top: 6px;
  font-size: 12px;
  background: var(--ableclaw-sidebar-bg, var(--surface-card, #fff));
  border-top: 1px solid var(--ableclaw-border, rgba(127,127,127,0.18));
}
.aclaw-select-bar .aclaw-select-count { opacity: 0.7; flex: 1; }
.aclaw-select-bar button {
  border: 0; cursor: pointer; font: inherit; font-size: 12px;
  padding: 5px 10px; border-radius: 7px; background: transparent;
  color: var(--ableclaw-accent-text, #6366f1); min-height: 28px;
}
.aclaw-select-bar .aclaw-select-delete {
  background: var(--danger, #b91c1c); color: var(--on-danger, #fff);
}
.aclaw-select-bar .aclaw-select-delete:disabled { opacity: 0.4; cursor: default; }

/* Recently deleted — the owner's 30-day restore lane under the list. Trash rows keep the
   row chrome but never open; their one action is always visible. */
.aclaw-conv-trash-toggle {
  border: 0; background: transparent; cursor: pointer; font: inherit; font-size: 11px;
  color: var(--ableclaw-text-muted, #9ca3af); padding: 10px 10px 6px 10px; text-align: left;
}
.aclaw-conv-trash-toggle:hover { color: var(--ableclaw-accent-text, #6366f1); text-decoration: underline; }
.aclaw-conv-trash-note { font-size: 11px; opacity: 0.55; padding: 0 10px 6px 10px; }
.aclaw-conversation-row.is-trashed .aclaw-conversation { cursor: default; opacity: 0.85; }
.aclaw-conversation-row.is-trashed .aclaw-conv-actions { display: flex; }
.aclaw-conversation-row.is-trashed .aclaw-conv-time { display: none; }
.aclaw-conversation-restore,
.aclaw-select-bar .aclaw-trash-restore-all {
  border: 0; cursor: pointer; font: inherit; font-size: 11px;
  padding: 3px 9px; border-radius: 6px;
  background: var(--ableclaw-accent, #6366f1); color: #fff;
}

.aclaw-conversation-row[hidden] { display: none !important; }
.aclaw-conversation-list {
  flex: 1; min-height: 0;
  overflow-y: auto;
  padding: 8px;
  display: flex; flex-direction: column; gap: 4px;
  /* Bug #119 — thin overlay scrollbar that blends with the sidebar
     surface. Default browser scrollbars render as a chunky dark column on
     light themes, which looks heavier than the content. scrollbar-width
     drives Firefox + modern Chromium; webkit-scrollbar rules tune
     Chrome/Safari directly. Thumb fades in on hover so the bar mostly
     disappears at rest, ChatGPT/Claude-style. */
  scrollbar-width: thin;
  scrollbar-color: var(--ableclaw-scrollbar-thumb, rgba(120, 120, 130, 0.32)) transparent;
}
.aclaw-conversation-list::-webkit-scrollbar { width: 8px; background: transparent; }
.aclaw-conversation-list::-webkit-scrollbar-track { background: transparent; }
.aclaw-conversation-list::-webkit-scrollbar-thumb {
  background: var(--ableclaw-scrollbar-thumb, rgba(120, 120, 130, 0.22));
  border-radius: 4px;
  border: 2px solid transparent;
  background-clip: padding-box;
  transition: background-color 140ms ease;
}
.aclaw-conversation-list:hover::-webkit-scrollbar-thumb {
  background: var(--ableclaw-scrollbar-thumb-hover, rgba(120, 120, 130, 0.45));
  background-clip: padding-box;
}
.aclaw-conversation-list::-webkit-scrollbar-thumb:hover {
  background: var(--ableclaw-scrollbar-thumb-active, rgba(120, 120, 130, 0.6));
  background-clip: padding-box;
}
.aclaw-empty-conversations {
  padding: 12px; font-size: 12px; opacity: 0.6; text-align: center;
}
/* COMPACT rows (restructure spec) — ~32px single line, ~2x the density. */
.aclaw-conversation-row {
  position: relative;
  border-radius: 6px;
  border-left: 2px solid transparent;
  transition: background-color 120ms;
}
.aclaw-conversation-row:hover { background: var(--ableclaw-sidebar-hover-bg, rgba(127, 127, 127, 0.10)); }
.aclaw-conversation-row.is-active {
  background: var(--ableclaw-sidebar-active-bg, rgba(127, 127, 127, 0.18));
  border-left-color: var(--conv-accent, var(--ableclaw-accent, #6366f1));
}
.aclaw-conversation-row.is-active .aclaw-conv-glyph { color: var(--conv-accent, var(--ableclaw-accent-text, #6366f1)); }
/* Bug #101 — streaming-in-progress indicator. Pulsing dot on rows whose
   conversation has an in-flight stream. Shown for background streams
   (when the user has navigated away from a conv that's still receiving
   deltas) AND for the active conv (subtle reinforcement of the in-bar
   indicator). The :before is anchored bottom-right of the row so it
   doesn't push other content; pulses with a 1.4s cycle. */
.aclaw-conversation-row[data-streaming="true"]::after,
.aclaw-conversation[data-streaming="true"]::after {
  content: "";
  position: absolute;
  right: 8px; top: 50%; transform: translateY(-50%);
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--ableclaw-accent, #6366f1);
  box-shadow: 0 0 0 0 var(--ableclaw-accent, #6366f1);
  animation: aclaw-conv-stream-pulse 1.4s ease-out infinite;
  pointer-events: none;
}
@keyframes aclaw-conv-stream-pulse {
  0%   { box-shadow: 0 0 0 0 rgba(99, 102, 241, 0.5); opacity: 1; }
  100% { box-shadow: 0 0 0 8px rgba(99, 102, 241, 0);   opacity: 0.6; }
}
.aclaw-conversation {
  width: 100%; min-width: 0;
  display: grid; grid-template-columns: 16px 1fr auto; align-items: center; gap: 7px;
  text-align: left;
  padding: 5px 10px; min-height: 32px;
  background: transparent; color: inherit; border: 0; cursor: pointer;
  font-size: 12.5px; line-height: 1.25;
  border-radius: 6px;
}
.aclaw-conv-glyph {
  display: inline-flex; align-items: center; justify-content: center;
  width: 16px; height: 16px; flex-shrink: 0;
  color: var(--conv-accent, var(--ableclaw-text-muted, #9ca3af));
}
.aclaw-conv-time {
  font-size: 11px; color: var(--ableclaw-text-muted, #9ca3af);
  opacity: 0; transition: opacity 120ms; white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.aclaw-conversation-row:hover .aclaw-conv-time,
.aclaw-conversation-row.is-active .aclaw-conv-time { opacity: 1; }
/* Hover-reveal actions, absolutely placed so they never reflow the row. */
.aclaw-conv-actions {
  position: absolute; top: 50%; right: 6px; transform: translateY(-50%);
  display: none; gap: 2px; align-items: center;
  background: var(--ableclaw-sidebar-active-bg, rgba(127,127,127,.18));
  border-radius: 6px; padding: 1px;
}
.aclaw-conversation-row:hover .aclaw-conv-actions { display: flex; }
.aclaw-conversation-row:hover .aclaw-conv-time { opacity: 0; }  /* actions replace the time on hover */
.aclaw-conversation.is-active { background: transparent; }
.aclaw-conversation-title-row {
  display: flex; align-items: center; gap: 6px; min-width: 0;
}

/* Main-chat channel separator — hair-thin line gradient with a soft tinted
   pill centered. Modern minimal aesthetic: the pill background uses
   color-mix() with the channel color at 8% opacity, falling back to a
   neutral tint for older browsers. */
.aclaw-channel-divider {
  display: flex; align-items: center; gap: 14px;
  width: 100%; margin: 20px 0 14px;
  color: var(--text-muted, var(--ableclaw-sidebar-fg));
  user-select: none;
}
.aclaw-channel-divider::before,
.aclaw-channel-divider::after {
  content: '';
  flex: 1 1 auto;
  height: 1px;
  background: linear-gradient(
    to right,
    transparent 0%,
    var(--border-default, var(--ableclaw-border, rgba(127,127,127,0.20))) 30%,
    var(--border-default, var(--ableclaw-border, rgba(127,127,127,0.20))) 70%,
    transparent 100%
  );
}
.aclaw-channel-divider-pill {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 10px; font-weight: 600; letter-spacing: 0.10em; text-transform: uppercase;
  line-height: 1;
  background: color-mix(in srgb, currentColor 8%, transparent);
}
.aclaw-channel-divider-pill svg { width: 11px; height: 11px; flex-shrink: 0; }
.aclaw-channel-divider-pill i { font-size: 10px; line-height: 1; flex-shrink: 0; }
/* DATE PILL — a centered, muted, small-caps day marker, emitted on every
   calendar-day change (viewer-local) and at the top of the loaded window
   (_dayDividerHtml). No flanking lines — just the chip, Telegram-style — which
   is what sets it apart from the channel divider above. The colour is a theme
   text token and the fill is a tint of it, so both modes are covered with no
   [data-theme] block. */
.aclaw-day-divider {
  display: flex; justify-content: center; align-items: center;
  width: 100%; margin: 8px 0;
  color: var(--text-secondary, var(--ableclaw-fg, #94a3b8));
  user-select: none;
}
/* When a day boundary and a channel switch land together, the two chips read as
   one header for the day's messages: the pill drops its lower margin and the
   channel divider its lead space, so they group tight — while a pill that stands
   alone keeps the symmetric breathing room above. */
.aclaw-day-divider:has(+ .aclaw-channel-divider) { margin-bottom: 0; }
.aclaw-day-divider + .aclaw-channel-divider { margin-top: 0; }
.aclaw-day-pill {
  display: inline-flex; align-items: center;
  max-width: calc(100% - 24px);
  padding: 3px 12px;
  border-radius: 999px;
  font-size: 11px; font-weight: 600; letter-spacing: 0.04em;
  font-variant: small-caps;
  line-height: 1.3;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  background: color-mix(in srgb, currentColor 12%, transparent);
}
.aclaw-channel-divider[data-channel="telegram"] .aclaw-channel-divider-pill { color: var(--ableclaw-channel-telegram); }
.aclaw-channel-divider[data-channel="email"]    .aclaw-channel-divider-pill { color: var(--ableclaw-channel-email); }
.aclaw-channel-divider[data-channel="slack"]    .aclaw-channel-divider-pill { color: var(--ableclaw-channel-slack); }
.aclaw-channel-divider[data-channel="sms"]      .aclaw-channel-divider-pill { color: var(--ableclaw-channel-sms); }
.aclaw-channel-divider[data-channel="whatsapp"] .aclaw-channel-divider-pill { color: var(--ableclaw-channel-sms); }
.aclaw-channel-divider[data-channel="web"]      .aclaw-channel-divider-pill { color: var(--ableclaw-channel-web); }
.aclaw-channel-divider[data-channel="ableui"]   .aclaw-channel-divider-pill { color: var(--ableclaw-channel-default); }
.aclaw-channel-divider[data-channel="voice"]    .aclaw-channel-divider-pill { color: var(--ableclaw-channel-voice); }
/* Spoken vs typed — a small mic glyph beside the timestamp on voice turns.
   FA-gated exactly like the divider icons (themes opt in via vendorLibraries). */
.aclaw-message[data-channel="voice"] .aclaw-message-time::before {
  content: '\f130'; font-family: 'Font Awesome 6 Free', 'FontAwesome';
  font-weight: 900; font-size: 9px; margin-right: 5px;
  opacity: .45;   /* the row no longer dims children — match the time text */
}
.aclaw-conversation-title {
  font-weight: 500;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  flex: 1; min-width: 0;
}
.aclaw-conversation-meta { font-size: 11px; opacity: 0.5; margin-top: 2px; }
/* Bug #119 — copy + delete buttons OVERLAY the row from the right edge so
   they never compress the title. Title gets 100% width always; the buttons
   fade in on row :hover with a gradient mask underneath that hides the
   tail of the title under the buttons (ChatGPT / Claude sidebar pattern).
   Pre-fix the buttons were flex 0/0/24px reserving ~48px of horizontal
   space at all times even when opacity:0, which forced titles like
   "Image Analysi…" to truncate on a 240px sidebar. */
/* Bug #119 — buttons OVERLAY at the bottom-right of the row, aligned
   with the meta row baseline (the "Yesterday 14:04" timestamp). They
   sit on row 2 instead of dead-centering on row 1's title, which made
   them look like they were colliding with the title text on hover. */
/* Inline in the hover-reveal .aclaw-conv-actions container (compact rows). */
.aclaw-conversation-delete,
.aclaw-conversation-copy-id {
  width: 22px; height: 22px;
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent; color: inherit; border: 0;
  cursor: pointer; opacity: 0.7;
  font-size: 12px; line-height: 1;
  border-radius: 4px;
  transition: opacity 120ms, background-color 120ms;
}
.aclaw-conversation-delete { font-size: 14px; }
.aclaw-conversation-row:hover .aclaw-conversation-delete,
.aclaw-conversation-row:hover .aclaw-conversation-copy-id {
  opacity: 0.7;
  pointer-events: auto;
}
.aclaw-conversation-delete:hover,
.aclaw-conversation-copy-id:hover {
  opacity: 1; background: var(--ableclaw-sidebar-hover-bg, rgba(127, 127, 127, 0.20));
}
.aclaw-conversation-copy-id.is-copied {
  opacity: 1 !important;
  pointer-events: auto;
  color: var(--success, #16a34a);
  background: rgba(22, 163, 74, 0.10);
}

.aclaw-chat {
  display: grid;
  /* Welcome AND messages share row 1 (only one visible at a time via [hidden]);
     attachments + input + footer stay fixed at the bottom regardless of
     conversation list length in the sidebar. min-height: 0 propagates through
     the grid so the middle row scrolls instead of pushing the input off-screen. */
  grid-template-rows: minmax(0, 1fr) auto auto auto;
  /* width-law-v1 companion — the column track was IMPLICIT (auto-sized), so
     the widest child's min-content INFLATED it past the container: on a 375px
     phone a datagrid-bearing thread blew the track to ~463px and .aclaw-chat's
     overflow:hidden CLIPPED the right edge of every message (pre-existing for
     long prose; the width law made widget bubbles hit it too). minmax(0,1fr)
     pins the track to the container — wide content stays inside the bubble and
     scrolls in its own overflow-x wrapper (claw-tbl / claw-grid-wrap). */
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas:
    "stream"
    "attachments"
    "input"
    "footer";
  min-width: 0; min-height: 0;
  position: relative;
  overflow: hidden;
}
.aclaw-welcome,
.aclaw-messages    { grid-area: stream; }
.aclaw-attachments { grid-area: attachments; }
.aclaw-input-bar   { grid-area: input; }
.aclaw-input-footer { grid-area: footer; }
.aclaw-welcome {
  min-height: 0; min-width: 0;
  overflow-y: auto; overflow-x: hidden;
  padding: 24px;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 18px;
  /* Ensures narrow viewports never cause inner inline flow to break into
     single-character columns — children get a width budget, not the
     intrinsic content width. */
  box-sizing: border-box;
}
/* Welcome text always reflows as a paragraph, even at very narrow widths.
   Without these, the inline children of .aclaw-welcome shrink to fit the
   smallest word and overflow vertically one-character-per-line. */
.aclaw-welcome > * { max-width: 100%; box-sizing: border-box; }
.aclaw-welcome p, .aclaw-welcome h1, .aclaw-welcome .aclaw-welcome-subtitle,
.aclaw-welcome .aclaw-welcome-helper {
  word-wrap: break-word; overflow-wrap: break-word;
}
/* Explicit hide — author-level [hidden] beats the implicit user-agent rule
   that the explicit display:flex above otherwise overrides. Without this
   the welcome panel + shortcut grid stay visible above the message stream
   when the conversation transitions from welcome state to messages. */
.aclaw-welcome[hidden] { display: none !important; }
.aclaw-welcome-identity {
  display: flex; align-items: center; gap: 12px;
}
.aclaw-welcome-avatar {
  width: 44px; height: 44px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 18px; font-weight: 700; color: #fff;
  background: var(--accent-secondary, #3b82f6);
}
.aclaw-welcome-name-block { display: flex; flex-direction: column; gap: 2px; }
.aclaw-welcome-name { font-size: 16px; font-weight: 600; }
.aclaw-welcome-tagline { font-size: 12px; opacity: 0.6; }
.aclaw-welcome-headline { text-align: center; max-width: 720px; }
.aclaw-welcome-title { font-size: 22px; font-weight: 600; margin: 0 0 6px 0; }
.aclaw-welcome-subtitle { font-size: 14px; opacity: 0.7; margin: 0; line-height: 1.5; }
.aclaw-shortcut-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 12px;
  width: 100%; max-width: 720px;
}
.aclaw-shortcut {
  display: flex; flex-direction: column; gap: 6px; align-items: flex-start;
  padding: 14px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--border-default, rgba(255, 255, 255, 0.08));
  border-radius: 10px; color: inherit; cursor: pointer;
  text-align: left;
  transition: background-color 120ms, border-color 120ms;
}
.aclaw-shortcut:hover {
  background: rgba(255, 255, 255, 0.08);
  border-color: var(--accent-secondary, #3b82f6);
}
.aclaw-shortcut-icon { font-size: 18px; opacity: 0.85; }
.aclaw-shortcut-category {
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em;
  opacity: 0.6;
}
.aclaw-shortcut-text { font-size: 13px; line-height: 1.4; }

/* WELCOME REDESIGN — large sparkle icon + headline + 4-category shortcut
   grid (ANALYZE / MONITOR / ACT / REPORT). Each column has a colored
   header and stacked bordered cards. */
.aclaw-welcome-icon {
  width: 96px; height: 96px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: linear-gradient(135deg, #a78bfa, #7c3aed);
  color: #fff;
  box-shadow: 0 12px 32px rgba(124, 58, 237, 0.35);
  margin-bottom: 6px;
}
.aclaw-welcome-icon svg {
  width: 48px; height: 48px;
  fill: currentColor;
  filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.2));
}
.aclaw-welcome-headline-h1 {
  font-size: 28px; font-weight: 700;
  text-align: center;
  margin: 0;
  letter-spacing: -0.01em;
}
.aclaw-welcome-categories {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 16px;
  width: 100%; max-width: 960px;
  margin-top: 8px;
}
@media (max-width: 1100px) {
  .aclaw-welcome-categories { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 640px) {
  .aclaw-welcome-categories { grid-template-columns: 1fr; }
}
.aclaw-welcome-category {
  display: flex; flex-direction: column; gap: 8px;
}
.aclaw-welcome-category-header {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 11px; font-weight: 700; letter-spacing: 0.10em;
  text-transform: uppercase;
  padding: 4px 0;
}
.aclaw-welcome-category-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; border-radius: 6px;
  font-size: 13px;
  flex: 0 0 auto;
}
.aclaw-welcome-category-items {
  display: flex; flex-direction: column; gap: 8px;
}
.aclaw-welcome-category-items .aclaw-shortcut {
  flex-direction: column; gap: 4px; align-items: flex-start;
}
/* Per-category accent — header label color + card border accent on hover. */
.aclaw-welcome-category--analyze .aclaw-welcome-category-header { color: #10b981; }
.aclaw-welcome-category--analyze .aclaw-welcome-category-icon  { background: rgba(16, 185, 129, 0.18); color: #10b981; }
.aclaw-welcome-category--analyze .aclaw-shortcut:hover         { border-color: #10b981; }

.aclaw-welcome-category--monitor .aclaw-welcome-category-header { color: #3b82f6; }
.aclaw-welcome-category--monitor .aclaw-welcome-category-icon  { background: rgba(59, 130, 246, 0.18); color: #3b82f6; }
.aclaw-welcome-category--monitor .aclaw-shortcut:hover         { border-color: #3b82f6; }

.aclaw-welcome-category--act .aclaw-welcome-category-header { color: #eab308; }
.aclaw-welcome-category--act .aclaw-welcome-category-icon  { background: rgba(234, 179, 8, 0.18); color: #eab308; }
.aclaw-welcome-category--act .aclaw-shortcut:hover         { border-color: #eab308; }

.aclaw-welcome-category--report .aclaw-welcome-category-header { color: #ef4444; }
.aclaw-welcome-category--report .aclaw-welcome-category-icon  { background: rgba(239, 68, 68, 0.18); color: #ef4444; }
.aclaw-welcome-category--report .aclaw-shortcut:hover         { border-color: #ef4444; }

.aclaw-welcome-helper {
  font-size: 13px; opacity: 0.55;
  text-align: center;
  margin: 4px 0 0 0;
}
.aclaw-welcome-helper em { font-style: italic; }

/* ── DAY WELCOME (replaces the shortcut chips for claw.day_query agents) ──
   The composer's empty state as "your day" (Raul 2026-08-24: a welcome screen
   that is a day-view-type screen). The VOCABULARY is the calendar family's own
   — the week peek's three groups (TIMED · DUE · ALL-DAY) and its point-vs-
   extent ink: an all-day row is a FILLED bar (it occupies its span), a due row
   is a RING whose left rule is an END-STOP tee (it marks a point). Colour is
   the row's tone / course slot, never re-tinted to mean "due" — the same
   not-colour-alone argument the week page records at length.

   TONE TOKENS: the ten closed names; the THEME owns the values (the day-card's
   exact fallback ladder, so a card in the thread and the welcome above it can
   never hue one row two ways). --accent-text for accent-coloured TEXT — the
   AA lesson able-day-card documents. */
.aclaw-dayw {
  --cal-tone-primary:  var(--cal-tone-primary-x,  var(--accent));
  --cal-tone-accent:   var(--cal-tone-accent-x,   var(--accent));
  --cal-tone-info:     var(--cal-tone-info-x,     var(--info));
  --cal-tone-warn:     var(--cal-tone-warn-x,     var(--warning));
  --cal-tone-critical: var(--cal-tone-critical-x, var(--danger));
  --cal-tone-money:    var(--cal-tone-money-x,    var(--success));
  --cal-tone-travel:   var(--cal-tone-travel-x,   var(--info));
  --cal-tone-muted:    var(--cal-tone-muted-x,    var(--text-muted));
  --cal-tone-social:   var(--cal-tone-social-x,   var(--success));
  --cal-tone-neutral:  var(--cal-tone-neutral-x,  var(--text-muted));
  width: 100%; max-width: 880px; margin: 0 auto; text-align: left;
}
/* Narrow screens: the date h1 is WIDE (the classic welcome led with a narrow
   96px icon), so without its own clearance it runs under the floating
   prefs/brain buttons at the chat column's top-right. Measured at 375px. */
@media (max-width: 720px) {
  .aclaw-dayw { padding-top: 40px; }
  .aclaw-dayw-date { font-size: 22px; }
}
.aclaw-dayw-eyebrow {
  font-size: 11px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase;
  color: var(--accent-text, var(--accent, #1b2a4a)); text-align: center; margin: 0 0 4px;
}
.aclaw-dayw-date {
  font-size: 26px; line-height: 1.15; font-weight: 700; text-align: center;
  margin: 0; color: var(--text-primary, #1d1c17);
}
.aclaw-dayw-sum {
  margin: 6px 0 0; font-size: 13px; color: var(--text-muted, #6b7280); text-align: center;
}
.aclaw-dayw-cols {
  display: grid; grid-template-columns: 1fr; gap: 10px; margin: 18px 0 0;
  align-items: start;
}
@media (min-width: 860px) {
  .aclaw-dayw-cols { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
.aclaw-dayw-col {
  border: 1px solid var(--border-default, #e5e7eb);
  border-radius: var(--radius-lg, 12px);
  background: var(--surface-card, #fff);
  padding: 10px 12px 12px;
  min-width: 0;
}
.aclaw-dayw-col[data-empty="y"] { opacity: .72; }
.aclaw-dayw-colh {
  display: flex; align-items: baseline; gap: 6px; margin: 0 0 6px;
  font-size: 11px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase;
  color: var(--text-muted, #6b7280);
}
.aclaw-dayw-n {
  font-weight: 600; font-size: 11px; letter-spacing: 0;
  color: var(--text-muted, #6b7280); opacity: .8;
}
.aclaw-dayw-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.aclaw-dayw-row {
  display: grid; grid-template-columns: auto 1fr; gap: 8px; align-items: baseline;
  width: 100%; border: 0; background: transparent; position: relative;
  padding: 5px 8px 5px 10px; border-radius: var(--radius-sm, 6px);
  font-family: inherit; text-align: left; cursor: pointer;
  color: var(--text-primary, #1d1c17);
  border-left: 3px solid var(--aclaw-dayc, var(--text-muted));
}
.aclaw-dayw-row[data-noclock="y"] { grid-template-columns: 1fr; }
@media (hover: hover) and (pointer: fine) {
  .aclaw-dayw-row:hover { background: var(--surface-hover, #f1f5f9); }
}
.aclaw-dayw-row:focus-visible { outline: 2px solid var(--accent, #1b2a4a); outline-offset: -2px; }
/* ALL-DAY: a filled bar — the extent statement (the peek's 20% tint; its meta
   line moves to --text-secondary because --text-muted fails AA on the tint). */
.aclaw-dayw-col[data-col="allday"] .aclaw-dayw-row {
  background: color-mix(in srgb, var(--aclaw-dayc, var(--border-default)) 18%, var(--surface-card, #fff));
  border-radius: 0 var(--radius-sm, 6px) var(--radius-sm, 6px) 0;
}
.aclaw-dayw-col[data-col="allday"] .aclaw-dayw-s { color: var(--text-secondary, #3f3f46); }
@media (hover: hover) and (pointer: fine) {
  .aclaw-dayw-col[data-col="allday"] .aclaw-dayw-row:hover {
    background: color-mix(in srgb, var(--aclaw-dayc, var(--border-default)) 28%, var(--surface-card, #fff));
  }
}
/* DUE: a ring, not a bar — the left rule is withdrawn and replaced by an
   END-STOP tee (stem + foot) at the row's middle: "due HERE", a point. Shape,
   not hue — it survives greyscale and any palette change under it. */
.aclaw-dayw-col[data-col="due"] .aclaw-dayw-row,
.aclaw-dayw-row[data-due="y"] {
  border-left-color: transparent;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--aclaw-dayc, var(--border-default)) 45%, transparent);
}
.aclaw-dayw-col[data-col="due"] .aclaw-dayw-row::before,
.aclaw-dayw-row[data-due="y"]::before {
  content: ""; position: absolute; left: -3px; top: 50%; transform: translateY(-50%);
  width: 3px; height: 14px; border-radius: 1px;
  background: var(--aclaw-dayc, var(--text-muted));
}
.aclaw-dayw-col[data-col="due"] .aclaw-dayw-row::after,
.aclaw-dayw-row[data-due="y"]::after {
  content: ""; position: absolute; left: -6px; top: 50%; transform: translateY(6px);
  width: 9px; height: 3px; border-radius: 1px;
  background: var(--aclaw-dayc, var(--text-muted));
}
.aclaw-dayw-time {
  font-size: 11px; font-variant-numeric: tabular-nums; color: var(--text-muted, #6b7280);
  line-height: 1.35; white-space: nowrap;
}
.aclaw-dayw-time em { font-style: normal; opacity: .75; }
.aclaw-dayw-body { min-width: 0; }
.aclaw-dayw-t {
  display: block; font-size: 13.5px; font-weight: 600; line-height: 1.3;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.aclaw-dayw-c {
  font-weight: 700; letter-spacing: .01em;
  color: var(--aclaw-dayc, var(--text-primary)); margin-right: 5px;
}
.aclaw-dayw-s {
  display: block; font-size: 11.5px; color: var(--text-muted, #6b7280); line-height: 1.3;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* the calendar is the RECORD: a done row stays, marked, never vanishes */
.aclaw-dayw-row[data-done="y"] { opacity: .55; }
.aclaw-dayw-row[data-done="y"] .aclaw-dayw-t { text-decoration: line-through; text-decoration-thickness: 1px; }
/* a past timed row quiets so "what is next" reads at a glance */
.aclaw-dayw-row[data-past="y"] { opacity: .55; }
.aclaw-dayw-none {
  margin: 16px 0 4px; font-size: 13px; color: var(--text-muted, #6b7280);
  font-style: italic; text-align: center;
}
/* an empty PANEL's word — the heading stays, the word answers ("Nothing due"
   is an ANSWER; a missing panel is a question about whether we looked) */
.aclaw-dayw-colnone {
  margin: 2px 0 0; font-size: 11.5px; color: var(--text-muted, #6b7280);
  font-style: italic;
}
/* the horizon ("Coming up") + the evening "Tomorrow" strip */
.aclaw-dayw-next { margin: 14px 0 0; }
.aclaw-dayw-nexth {
  margin: 0 0 6px; font-size: 11px; font-weight: 700; letter-spacing: .12em;
  text-transform: uppercase; color: var(--text-muted, #6b7280); text-align: center;
}
.aclaw-dayw-next .aclaw-dayw-list { max-width: 560px; margin: 0 auto; }
.aclaw-dayw-nd {
  font-size: 11px; font-weight: 700; color: var(--text-muted, #6b7280);
  white-space: nowrap; text-transform: uppercase; letter-spacing: .04em;
}
/* loading skeleton — reserves the panel band so the swap does not jump */
.aclaw-dayw-skel {
  height: 118px; border-radius: var(--radius-lg, 12px);
  border: 1px solid var(--border-subtle, #f1f5f9);
  background: linear-gradient(100deg, var(--surface-subtle, #f8fafc) 40%,
              var(--surface-hover, #f1f5f9) 50%, var(--surface-subtle, #f8fafc) 60%);
  background-size: 200% 100%;
  animation: aclaw-dayw-shimmer 1.2s ease-in-out infinite;
}
@keyframes aclaw-dayw-shimmer { from { background-position: 120% 0; } to { background-position: -80% 0; } }
@media (prefers-reduced-motion: reduce) {
  .aclaw-dayw-skel { animation: none !important; }
}

.aclaw-messages {
  min-height: 0; min-width: 0;
  overflow-y: auto; overflow-x: hidden;
  padding: 16px 24px;
  display: flex; flex-direction: column; gap: 12px;
  box-sizing: border-box;
}
.aclaw-messages[hidden] { display: none !important; }
/* Jump-to-latest — the arrow-down affordance (2026-08-03 scroll pass).
   Sticky at the scroller's bottom edge; ONLY rendered while the reader is
   scrolled up (hidden at the bottom). While new content arrives with the
   reader scrolled up we NEVER yank the view — the pill flips to its accent
   "new" state instead; clicking it returns to the live tail and re-engages
   follow. In-flow last child of the column, so a wiped thread re-render just
   re-appends the same node. */
.aclaw-jump-latest {
  /* GUTTER, not the reading column (2026-08-15). Sticky-bottom floats this over
     whatever line is at the scroller's bottom edge — and centred, that line is
     mid-sentence: it erased a word of a live answer ("semester locker/carrel
     ass▮▮▮▮ents") and the reader's report was a screenshot of the button sitting
     on the text. It cannot leave the scroller (overflow:auto clips, and an
     absolute child of a scroller scrolls away), and reserving a permanent strip
     would cost every reader 48px of height for an affordance that is hidden
     while they are at the tail. So it moves OUT of the text column instead:
     right-aligned, where the wide layout has only gutter, and where a narrow one
     loses the tail of a line rather than the middle of a sentence. */
  position: sticky; bottom: 10px; align-self: flex-end; margin-right: 14px;
  z-index: 30;
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  min-width: 38px; height: 38px; padding: 0 12px; border-radius: 999px;
  border: 1px solid var(--border-default, rgba(127,127,127,.28));
  background: var(--surface-card, rgba(255,255,255,.92));
  color: var(--text-primary, #1d1c17);
  font: inherit; font-size: 13px; font-weight: 600; line-height: 1;
  cursor: pointer; box-shadow: var(--shadow-md, 0 4px 14px rgba(0,0,0,.18));
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  flex: 0 0 auto;
}
.aclaw-jump-latest[hidden] { display: none !important; }
.aclaw-jump-latest:hover { background: var(--surface-hover, rgba(255,255,255,1)); }
.aclaw-jump-latest .aclaw-jump-label { display: none; }
.aclaw-jump-latest[data-new="true"] {
  background: var(--accent, var(--ableclaw-accent, #6366f1));
  border-color: var(--accent, var(--ableclaw-accent, #6366f1));
  color: var(--on-accent, #fff);
}
.aclaw-jump-latest[data-new="true"] .aclaw-jump-label { display: inline; }
@media (pointer: coarse) {
  .aclaw-jump-latest { min-width: 44px; height: 44px; }   /* tap-target law */
}
/* Load-older — the top-of-thread affordance for the paged history read
   (2026-08-17). The server returns the newest N rows and answers a
   more-history header when older ones exist; this is how the reader asks for
   them. Deliberately NOT sticky, unlike its jump-to-latest twin: that one is an
   action available at any moment, this one marks a BOUNDARY in the transcript,
   so it belongs at the top of the content where a reader scrolling up arrives
   at it, not floating over the thread the whole way. Visual grammar is the
   channel divider's -- a muted pill on the column centre line -- because it
   says the same kind of thing: the transcript continues past here. */
.aclaw-load-older {
  align-self: center; flex: 0 0 auto;
  /* The bottom margin is SEPARATION, not padding. At 6px this sat a breath away
     from the first channel divider, and two centred pills that close read as a
     matched pair with no hierarchy between them -- an interactive control and a
     passive label competing for the same slot (flagged independently on mobile
     and desktop review). 18px + the column gap puts it at the thread's top EDGE
     instead of in its content. */
  margin: 2px 0 18px 0;
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  min-height: 32px; padding: 0 14px; border-radius: 999px;
  border: 1px solid var(--border-default, rgba(127,127,127,.28));
  background: var(--surface-card, rgba(255,255,255,.92));
  color: var(--text-secondary, #3f3f46);
  font: inherit; font-size: 12.5px; font-weight: 600; line-height: 1;
  cursor: pointer;
}
.aclaw-load-older[hidden] { display: none !important; }
.aclaw-load-older[disabled] { cursor: default; opacity: .65; }
.aclaw-load-older > i { font-size: 11px; opacity: .7; }
@media (hover: hover) and (pointer: fine) {
  .aclaw-load-older:hover:not([disabled]) {
    background: var(--surface-hover, rgba(127,127,127,.10));
    color: var(--text-primary, #1d1c17);
  }
}
@media (pointer: coarse) {
  .aclaw-load-older { min-height: 44px; }   /* tap-target law */
}
/* The explicit dead/failed-thread state (2026-08-11 white-screen pass, D6).
   The message pane used to render a blank when a conversation resolved to
   nothing — 200 [] on a deleted id, or a failed history fetch — with no text,
   no console line and no way back. This is the alternative that makes hiding
   the welcome safe. */
.aclaw-conv-notice {
  margin: auto; padding: 28px 20px; max-width: 340px;
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  text-align: center; color: var(--text-secondary, #3f3f46);
}
.aclaw-conv-notice > i { font-size: 26px; opacity: .5; margin-bottom: 2px; }
.aclaw-conv-notice-title {
  font-size: 15px; font-weight: 650; color: var(--text-primary, #1d1c17);
}
.aclaw-conv-notice-body { font-size: 13px; line-height: 1.5; }
.aclaw-conv-notice-retry {
  margin-top: 6px; min-height: 38px; padding: 0 16px; border-radius: 999px;
  border: 1px solid var(--border-default, rgba(127,127,127,.28));
  background: var(--surface-card, rgba(255,255,255,.92));
  color: var(--text-primary, #1d1c17);
  font: inherit; font-size: 13px; font-weight: 600; cursor: pointer;
}
.aclaw-conv-notice-retry:hover { background: var(--surface-hover, rgba(127,127,127,.10)); }
@media (pointer: coarse) { .aclaw-conv-notice-retry { min-height: 44px; } }

/* THE AVAILABILITY STRIP — why the composer below is dead. It is the input
   bar's FIRST CHILD (a column flex, align-items:stretch), never a sibling:
   .aclaw-chat is a named-area grid, so a stray sibling would be auto-placed
   into an implicit row and move the composer. Sitting ON the composer instead
   of replacing the pane is what lets a retired agent's thread keep showing its
   history while refusing new turns. */
.aclaw-agent-unavailable {
  padding: 8px 11px; border-radius: 10px;
  font-size: 12.5px; line-height: 1.45; font-weight: 600;
  color: var(--warning, #b45309);
  background: color-mix(in srgb, var(--warning, #b45309) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--warning, #b45309) 30%, transparent);
}
/* A disabled composer must LOOK disabled — otherwise the strip explains a box
   that still invites typing. The strip itself keeps full contrast: it is the
   one thing on the composer that must stay readable. */
able-claw[data-agent-unavailable] .aclaw-input,
able-claw[data-agent-unavailable] .aclaw-composer-row { opacity: .55; }
able-claw[data-agent-unavailable] .aclaw-input { cursor: not-allowed; }

.aclaw-message { display: flex; flex-direction: column; gap: 4px; }
.aclaw-message--user { align-items: flex-end; }
.aclaw-message--assistant { align-items: flex-start; }
/* Message-to-message spacing reads larger than the gap between paragraphs
   inside one message. Scoped to two ADJACENT messages, so a message right
   after a date pill or a channel divider keeps the tighter container gap. */
.aclaw-message + .aclaw-message { margin-top: 12px; }

/* Greeting bubble — assistant intro shown when the conversation is fresh
   (no real messages yet) or has just been started via "+ New chat".
   ONLINE pill above the bubble matches the agent-online affordance. */
.aclaw-greeting-status {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 10px; font-weight: 600; letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #10b981;
}
.aclaw-greeting-dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: #10b981;
  box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.4);
  animation: aclaw-greeting-pulse 2s infinite;
}
@keyframes aclaw-greeting-pulse {
  0%   { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.4); }
  70%  { box-shadow: 0 0 0 8px rgba(16, 185, 129, 0); }
  100% { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0); }
}
.aclaw-message-bubble {
  max-width: 80%;
  padding: 11px 16px;
  /* Asymmetric radius — assistant bubble has flat bottom-left ("speaking
     from the left"); user bubble has flat bottom-right ("speaking from
     the right"). Modern chat-bubble convention. */
  border-radius: 16px 16px 16px 4px;
  background: var(--ableclaw-bubble-agent-bg);
  color: var(--ableclaw-bubble-agent-fg);
  font-size: 14px; line-height: 1.55;
  word-wrap: break-word;
  overflow-wrap: break-word;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
}
.aclaw-message--user .aclaw-message-bubble {
  background: var(--ableclaw-bubble-user-bg); color: var(--ableclaw-bubble-user-fg);
  border-radius: 16px 16px 4px 16px;
}
/* THE ASSISTANT WIDTH LAW (width-law-v1 — composer-enterprise, Raul
   2026-08-02: "limiting the response to that small area on the left makes it
   look cramped in web"). Assistant rows OWN the content column. max-width:100%
   alone was NOT enough: .aclaw-message is a column flex with align-items:
   flex-start, so the bubble is shrink-to-fit — a response whose widest block
   is a widget (guided-run card / datagrid / kpi band) collapsed to that
   widget's INTRINSIC width (~55-65% of the column) with a dead right gutter.
   width:100% is the floor: the message CONTAINER always spans the column
   (small symmetric gutter = .aclaw-messages padding), widget/figure blocks
   (.aclaw-face cards, claw-tbl, claw-kpis, grids, decks, code, tables,
   headings) fill it edge-to-edge, and body PROSE keeps a readable measure
   via the cap below — figures exceed the text column, the editorial pattern.
   User rows are UNTOUCHED: compact right-aligned chips under the 80% cap. */
.aclaw-message--assistant .aclaw-message-bubble {
  width: 100%; max-width: 100%; box-sizing: border-box;
}
/* Affordance bubbles stay compact chips — the greeting one-liner + the
   typing dots are chrome, not responses. width:auto restores shrink-to-fit
   (flex cross-axis fit-content); order-after the law rule so it wins the
   (0,2,0) tie for the greeting. */
.aclaw-greeting .aclaw-message-bubble,
.aclaw-message--assistant .aclaw-message-bubble--pending { width: auto; }
/* The PROSE MEASURE — body-text blocks inside the full-width surface cap at
   ~76ch (left-aligned), so reclaiming the column never mints 1100px text
   lines. Scope is TOP-LEVEL prose only: markdown-path direct children, and
   claw-path direct children of .claw-root / a top-level .claw-section.
   Prose INSIDE a card / hero / sidebar fills its card — cards own their
   internal layout. Headings + eyebrows are deliberately uncapped
   (display type spans the composition like figures do; ch resolves against
   EACH element's own font, so a 36px .claw-h1 cap would be fiction anyway).
   THE CALLOUT IS PROSE, NOT A CARD (P1, 2026-08-03 — Recovr conv f88203cb):
   it was in the card exemption, so a callout carrying a full paragraph broke
   out to the bubble and rendered the widest, smallest, most-saturated block
   in the message — measured 108.5ch at 13px beside 76.0ch body prose at 14px
   (Raul's "full screen prose vs one part / inconsistent font sizes"). A
   callout is an ASIDE — emphasized running prose doing the same reading job —
   already distinguished by a 3px border, a wash AND semantic color; a fourth
   signal (breaking the column) buys nothing and costs the measure. It is
   border-box, so the 76ch cap lands its 13px content at ~77ch — the same
   line length and the same column edges as body prose. Genuine cards
   (.aclaw-face, grids, decks, tables) keep the exemption and stay full-bleed. */
.aclaw-message--assistant .aclaw-message-bubble > :is(p, ul, ol, blockquote, dl, .claw-callout),
.aclaw-message--assistant .aclaw-message-bubble > [data-ent-sec] > :is(p, ul, ol, blockquote, dl, .claw-callout),
.aclaw-message--assistant .aclaw-message-bubble > :is(.claw-root, .claw-root--editorial) > :is(p, ul, ol, blockquote, dl, .claw-body, .claw-body--editorial, .claw-body--muted, .claw-callout),
.aclaw-message--assistant .aclaw-message-bubble > :is(.claw-root, .claw-root--editorial) > :is(.claw-section, [data-ent-sec]) > :is(p, ul, ol, blockquote, dl, .claw-body, .claw-body--editorial, .claw-body--muted, .claw-callout) {
  max-width: 76ch;
}
/* Recovr forensics 2026-08-03 (conv f88203cb): _entSectionChrome (ENT-E1)
   re-parents every section body into a <div data-ent-sec> wrapper, which
   broke the direct-child prose selectors above — paragraphs BEFORE the first
   <h3> kept the 76ch measure while everything after rendered full-bubble
   (measured 671px vs 1292px in one message). The [data-ent-sec] hops above
   restore ONE measure for the whole thread; sections only ever wrap direct
   children of the bubble or of .claw-root (the h3 scan runs on that host),
   so one hop is the complete set. */
/* A bubble whose content is a widget takes the full row: the column flex shrinks a
   bubble to fit, and a deck or an ask card would otherwise come out at its own
   intrinsic width — a different width per message. Widgets are page furniture. */
.aclaw-message-bubble:has(.aclaw-deck),
.aclaw-message-bubble:has(.aclaw-ask),
.aclaw-message-bubble:has(.aclaw-vocab-row) { width: 100%; }
/* The card paints the lead while a face is open; the bubble that carried it hides for
   that time and returns when the set is graded — the prose stays in the thread. */
.aclaw-message--assistant:has(> .aclaw-widgets able-test[data-lead-shown]) > .aclaw-message-bubble[data-lead-in-card] { display: none; }
/* NEWLINE-LAID-OUT HTML — a telegram-grammar message viewed on the web
   (Justin, 2026-08-06: "manda mucha información en 1 párrafo… en Telegram
   lo corta"). A telegram conversation's messages are stored as the bytes
   Telegram renders: inline tags only (<b>/<i>/<a>/<code>…) with the LAYOUT
   carried by newlines — which the HTML path collapses into one wall, and
   with no <p>/<ul> children the 76ch prose law above never engages either.
   renderMarkdown wraps that shape in .aclaw-plainflow: pre-wrap restores
   every break exactly as Telegram shows them, and the wrapper carries the
   same 76ch measure as structured prose. <pre> children keep their own
   whitespace (UA per-element white-space), so figure blocks don't double. */
.aclaw-message-bubble .aclaw-plainflow {
  white-space: pre-wrap;
  overflow-wrap: break-word;
  max-width: 76ch;
}
/* User bubbles: the typed text is escaped into ONE <p> — a multi-line
   telegram message (or a pasted list) collapsed the same way. */
.aclaw-message--user .aclaw-message-bubble p { white-space: pre-wrap; }
.aclaw-message-time {
  /* Opacity moved OFF the row and onto the time text (.aclaw-mt-t): CSS opacity
     COMPOUNDS onto children, so the model badge + interrupted badge could never
     exceed the row's 0.45 — the interrupted badge's documented 4.7:1 contrast
     (Bug #93) was silently dimmed to ~2.1:1, and the model badge failed visual
     review as illegible. Row at 1; each child owns its own muting. */
  font-size: 10px;
  padding: 0 4px;
  font-variant-numeric: tabular-nums;
  display: inline-flex; align-items: center; gap: 6px;
  flex-wrap: wrap;
}
.aclaw-mt-t { opacity: 0.6; font-size: 11px; }
/* THE TIMESTAMP (Telegram rhythm). An assistant flow message keeps the meta row
   as a sibling under the bubble, right-aligned to the READING COLUMN rather than
   the far edge of the full-width bubble: cap the row at the prose measure and
   left-anchor it, so the time + model badge + reply actions sit just past the
   text instead of floating in the empty right of a wide message. 43.5rem tracks
   the ~76ch prose measure; on a narrow bubble the cap never binds and the row
   rides the right edge of the text as usual. */
.aclaw-message--assistant .aclaw-message-time {
  align-self: flex-start;
  width: 100%; max-width: 43.5rem;
  justify-content: flex-end;
}
/* A user message carries the time INSIDE the bubble, bottom-right. The text is
   one <p>; drop its UA block margins so the bubble is a compact chip and the
   time tucks under the words instead of a line below them. The time sits on the
   saturated user-bubble fill, so it wears a higher opacity than the assistant
   lane to stay legible there. */
.aclaw-message--user .aclaw-message-bubble > p { margin: 0; }
.aclaw-message--user .aclaw-message-bubble .aclaw-message-time {
  display: flex; justify-content: flex-end; margin-top: 5px;
}
.aclaw-message--user .aclaw-message-bubble .aclaw-mt-t { opacity: 0.72; }
/* Copy-this-reply — the one gesture that lifts a block of text out of the
   thread without depending on a drag-selection surviving the next render.
   Same 22px/⧉ visual language as the sidebar's copy-ID button so the two read
   as the same affordance.

   Revealed on hover ONLY where hover is real. The base state (touch, where
   there is no hover and a hover-gated control is simply unreachable) is
   VISIBLE-but-quiet; pointer:fine devices hide it until the message is hovered
   so a long thread is not a column of glyphs. Never the other way round — a
   hover-only reveal is the classic control that cannot be tapped. */
.aclaw-message-copy {
  width: 22px; height: 22px;
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent; color: inherit; border: 0;
  cursor: pointer; opacity: 0.45;
  font-size: 12px; line-height: 1;
  border-radius: 4px;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  transition: opacity 120ms, background-color 120ms;
}
@media (hover: hover) and (pointer: fine) {
  .aclaw-message-copy { opacity: 0; pointer-events: none; }
  .aclaw-message:hover .aclaw-message-copy,
  .aclaw-message-copy:focus-visible { opacity: 0.55; pointer-events: auto; }
  .aclaw-message-copy:hover {
    opacity: 1;
    background: var(--ableclaw-sidebar-hover-bg, rgba(127, 127, 127, 0.20));
  }
}
/* The ✓/✗ flash must outrank the hover-hidden state above, or the confirmation
   is invisible the moment the pointer leaves. */
.aclaw-message-copy.is-copied {
  opacity: 1 !important;
  pointer-events: auto;
  color: var(--success, #16a34a);
  background: rgba(22, 163, 74, 0.10);
}
/* Partial-turn badge — surfaces on assistant bubbles whose underlying
   conversation_messages.metadata.partial=true (server-side marker written
   when the agent runtime crashed mid-stream). Subtle, muted-gray, sits
   inline with the timestamp; wraps below on narrow viewports. */
.aclaw-message-interrupted {
  /* Bug #93 — raise contrast to WCAG AA 4.5:1 on a typical white background.
     Old: rgba(120,120,120,0.95) text on rgba(120,120,120,0.10) bg ≈ 2.5:1.
     New: #475569 text on #e2e8f0 bg ≈ 4.7:1. */
  display: inline-flex; align-items: center; gap: 4px;
  padding: 1px 6px;
  border-radius: 8px;
  background: #e2e8f0;
  color: #475569;
  font-size: 9.5px; font-weight: 600;
  letter-spacing: 0.04em; text-transform: uppercase;
  font-style: italic;
  cursor: help;
  opacity: 1;
}
.aclaw-message-interrupted i { font-size: 9px; opacity: 0.85; }
/* Per-message model badge — the SUBSTRATE truth for the composer's model picker
   (A/B testing): each assistant bubble is labeled with the model that actually
   produced it (persisted metadata.model on history; the requested model on live
   turns). Never trust the model's own self-report — flash-class models misstate
   their identity even when told. Inherits the time-line's muted color so it
   flips with the theme (no hardcoded hex — the interrupted-badge trap). */
.aclaw-message-model {
  display: inline-flex; align-items: center;
  font-size: 10px; font-weight: 600;
  /* NO text-transform. The stamp speaks the model PICKER's vocabulary now
     ("Opus 5", "GPT-5.6 Sol", "Study Mode · Opus 5"); uppercasing it would
     re-break the very match with the composer this exists to create — and it
     was what turned an unmapped id into the shouted "CLAUDE-OPUS-4-8". */
  letter-spacing: 0.02em;
  opacity: 0.85;   /* measured 5.13:1 on the dark grid bg (WCAG AA; 0.75 was 4.11) */
  cursor: default;
}
.aclaw-code {
  position: relative;
  margin: 8px 0; padding: 8px 12px;
  background: var(--ableclaw-code-bg);
  color: var(--ableclaw-code-fg);
  border-radius: 6px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  font-size: 12px;
  overflow-x: auto;
}
.aclaw-code-copy {
  position: absolute; top: 6px; right: 6px;
  padding: 2px 8px;
  background: rgba(255, 255, 255, 0.1);
  color: inherit;
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-radius: 4px;
  font-size: 11px; line-height: 1.4; cursor: pointer;
  opacity: 0; transition: opacity 120ms;
}
.aclaw-code:hover .aclaw-code-copy { opacity: 1; }
.aclaw-code-copy:hover { background: rgba(255, 255, 255, 0.18); }

/* Widget lane — a mounted <able-lesson> becomes this sandboxed iframe. Sized
   by content via the postMessage resize bridge; 440px until the frame reports. */
.aclaw-widget {
  display: block;
  width: 100%;
  margin: 12px 0;
  border: 1px solid var(--ableclaw-table-border, rgba(0, 0, 0, 0.12));
  border-radius: 12px;
  background: #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
  min-height: 120px;
}

/* Streaming stand-in for an <able-lesson> — shown while the turn is still
   streaming so the real iframe mounts exactly ONCE, on the final render
   (see _updateStreamingBubble). Prevents per-delta iframe reload flicker. */
.aclaw-widget-loading {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  margin: 12px 0;
  padding: 16px 14px;
  border: 1px dashed var(--ableclaw-table-border, rgba(0, 0, 0, 0.18));
  border-radius: 12px;
  color: var(--ableclaw-muted, #6b7280);
  font-size: 13px;
  background: rgba(0, 0, 0, 0.02);
}

/* ═══════ The widget-attachment lane (tool-initiated delivery) ═════════════
   A widget a TOOL attached to the reply — metadata.widgets on the assistant row,
   projected as the wire's widgets array — never markup the model typed.
   The score kind synthesizes the same able-score element the tag lane paints, so
   it borrows that family's face and needs nothing here; these are the two faces
   the lane owns itself: the datagrid receipt table, and the identity-only card
   every honest degrade lands on (an oversize payload C4 deferred fetching for, a
   kind this build does not know, a src that failed the same-origin rule).
   Composer card body, same as .aclaw-stage-card — a widget is a rich block.
   (No backticks in this block: it lives inside the chrome-CSS template literal,
   where one would end the literal and take the whole runtime bundle with it.) */
.aclaw-widgets { display: block; width: 100%; max-width: 100%; box-sizing: border-box; margin: 8px 0 0; }
.aclaw-widget-card {
  margin: 10px 0; padding: 12px 14px; border-radius: 12px;
  background: var(--surface-card, #fff);
  border: 1px solid var(--border-subtle, rgba(0,0,0,.08));
  box-shadow: var(--shadow-sm, 0 1px 3px rgba(0,0,0,.05));
  font-size: 13px; line-height: 1.45; overflow-wrap: break-word;
}
/* The card's heading — a title for the table under it, not a field label. It was
   11px uppercase muted, which reads as an eyebrow and let the table start with no
   apparent owner; primary colour and weight carry it now, and it stays compact. */
.aclaw-widget-card-title {
  font-size: var(--font-sm, 13px); font-weight: 600; letter-spacing: 0;
  color: var(--text-primary, #1d1c17); margin: 0 0 7px;
}
.aclaw-widget-card-caption { margin: 0; color: inherit; }
.aclaw-widget-card-note {
  margin: 7px 0 0; font-size: 12px; font-style: italic;
  color: var(--text-muted, #6b7280);
}
/* The datagrid face — a read-only receipt. No sorting, no paging: the reply's
   prose carries the reading, this carries the rows it was read from. Wide grids
   scroll INSIDE their own box so a bubble never scrolls sideways. */
.aclaw-widget-grid-scroll { overflow-x: auto; margin: 7px 0 0; }
.aclaw-widget-grid {
  width: 100%; border-collapse: collapse; font-size: 13px; color: inherit;
}
.aclaw-widget-grid th, .aclaw-widget-grid td {
  padding: 6px 10px; text-align: left; color: inherit;
  border: 1px solid var(--ableclaw-table-border);
  background: var(--ableclaw-table-row-bg);
}
.aclaw-widget-grid th {
  background: var(--ableclaw-table-header-bg);
  color: var(--ableclaw-table-header-fg);
  font-weight: 600; white-space: nowrap;
}
/* Quantities right-align and share one digit width, so the column reads as a
   column: magnitudes stack, decimal points line up. Same data-c / data-num
   vocabulary the claw's other tables use — the header follows its cells. */
.aclaw-widget-grid th[data-num] { text-align: right; }
.aclaw-widget-grid td[data-c="money"], .aclaw-widget-grid td[data-c="num"] {
  text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap;
}
.aclaw-widget-grid-more {
  margin: 6px 0 0; font-size: 12px; color: var(--text-muted, #6b7280);
}

/* ═══════════ THE STAGE — rail · board · glance · status · receipts ═══════ */
/* Rail — the day's work at the top of the sidebar; conversations demote. */
/* Legacy left-rail (now unused — the route/map lives in the right workspace). */
.aclaw-rail { display: none; }
.aclaw-convs-toggle {
  display: block; width: calc(100% - 20px); margin: 2px 10px; padding: 7px 10px;
  font-size: 11px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: var(--text-muted, #6b7280); background: transparent;
  border: none; border-top: 1px solid var(--border-subtle, rgba(0,0,0,.08));
  text-align: left; cursor: pointer; min-height: 34px;
}
/* Stage cards — shared by rail + board. Wave 2 (C-spec §5): the stage tier
   adopts the COMPOSER-card face family — surface-card / radius 16 /
   border-subtle / shadow-sm, padding 14×16 — same body as every rich block. */
.aclaw-stage-card {
  margin: 0 0 10px; padding: 14px 16px; border-radius: 16px;
  background: var(--surface-card, #fff);
  border: 1px solid var(--border-subtle, rgba(0,0,0,.08));
  box-shadow: var(--shadow-sm, 0 1px 3px rgba(0,0,0,.05));
  font-size: 13px; line-height: 1.45; overflow-wrap: break-word;
}
.aclaw-stage-card-title {
  font-size: 11px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase;
  color: var(--text-muted, #6b7280); margin: 0 0 7px;
}
@keyframes aclaw-stage-pulse { 0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 35%, transparent); } 100% { box-shadow: 0 0 0 9px color-mix(in srgb, var(--accent) 0%, transparent); } }
.aclaw-stage-card--pulse { animation: aclaw-stage-pulse .5s ease-out 1; }
.aclaw-board { padding: 12px; overflow-y: auto; }
/* Thread ref line — where an extracted card / pinned widget used to be.
   The view affordance is a real CHIP of the face family (tokens only). */
.aclaw-card-ref {
  display: flex; align-items: center; gap: 7px; margin: 6px 0; min-width: 0;
  font-size: 13px; color: var(--text-muted, #6b7280);
}
.aclaw-card-ref i { opacity: .7; }
.aclaw-card-ref-open {
  border: 1px solid var(--border-default, rgba(0,0,0,.14)); background: transparent;
  border-radius: 999px; padding: 3px 11px; font-size: 11.5px; font-weight: 600;
  color: inherit; cursor: pointer; min-height: 26px;
}
/* Receipts — the ✓ one-liner for every completed write (mid-stream styling;
   at settle _groupReceipts upgrades every run into the ledger card below). */
able-receipt {
  display: flex; align-items: baseline; gap: 8px; margin: 8px 0;
  padding: 9px 12px; border-radius: 10px; font-size: 13px; font-weight: 600;
  background: color-mix(in srgb, var(--success) 9%, transparent);
  border: 1px solid color-mix(in srgb, var(--success) 30%, transparent);
  color: inherit;
}
/* THE COMPOSER CARD (C-spec §2) — ONE surface for every rich content block:
   figures, vocab groups, receipt groups, degrade cards (math/ask keep their
   already-carded chrome). Semantic tokens only — dark mode by construction. */
.aclaw-face {
  background: var(--surface-card); border: 1px solid var(--border-subtle);
  border-radius: 16px; box-shadow: var(--shadow-sm);
  padding: 14px 16px; margin: 12px 0;
}
/* w7/W24 — the receipt-group LEDGER: consecutive receipts in ONE card (a ✓
   glyph column + one row per receipt, single border, success-tinted wash). */
.aclaw-receipt-group {
  padding: 4px 12px;
  background: color-mix(in srgb, var(--success) 5%, var(--surface-card));
  border-color: color-mix(in srgb, var(--success) 24%, transparent);
}
.aclaw-receipt-item {
  display: flex; align-items: baseline; gap: 9px; padding: 8px 2px;
  font-size: 13px; font-weight: 600; line-height: 1.45;
}
.aclaw-receipt-item + .aclaw-receipt-item {
  border-top: 1px solid color-mix(in srgb, var(--success) 14%, transparent);
}
.aclaw-receipt-glyph { flex: 0 0 auto; color: var(--success); font-weight: 700; }
.aclaw-receipt-body { flex: 1 1 auto; min-width: 0; overflow-wrap: break-word; }
/* Pin slot — the docked widget above the thread. */
.aclaw-pinslot {
  position: sticky; top: 0; z-index: 5;
  padding: 8px 16px 4px; background: inherit;
}
.aclaw-pinslot .aclaw-widget { margin: 0; box-shadow: 0 6px 18px -8px rgba(0,0,0,.25); }
.aclaw-pinslot > .claw-post, .aclaw-pinslot > .aclaw-face { margin: 0; }
/* ── pin-layout: the SIDE form — the pinned thing is a LEFT COLUMN and the
   conversation runs beside it (the review-desk geometry, claw-owned).
   data-pin-side is set by _updatePinLayout only while the dock is occupied
   AND the host asked for it (pin-layout="side", or "auto" on a wide claw) —
   an absent pin-layout attribute changes nothing anywhere. NO BACKTICKS in this
   block: it lives inside a JS template literal. */
/* Both forms name EVERY row the chat grid can carry (the helper header row is
   empty outside helper mode and collapses to 0). The mode-qualified selectors
   outrank the helper-mode grid defined further down, which would otherwise
   win on order and leave the dock and the surface header auto-placed. */
able-claw[data-pin-top] .aclaw-chat,
able-claw[data-mode="helper"][data-pin-top] .aclaw-chat {
  grid-template-columns: minmax(0, 1fr);
  /* The pin ROW is bounded, not just the pinned element: an auto row sized by
     the card's intrinsic height ate the stream's 1fr down to zero on a phone.
     The pin takes the larger share of the claw — the pinned entity is what the
     surface is for, and its art has to read at phone size — and the conversation
     the rest; the dock scrolls for the copy below the art. */
  grid-template-rows: auto auto minmax(0, 62%) minmax(0, 1fr) auto auto auto;
  grid-template-areas:
    "helper"
    "shead"
    "pin"
    "stream"
    "attachments"
    "input"
    "footer";
}
able-claw[data-pin-side] .aclaw-chat,
able-claw[data-mode="helper"][data-pin-side] .aclaw-chat {
  grid-template-columns: minmax(280px, 38%) minmax(0, 1fr);
  grid-template-rows: auto auto minmax(0, 1fr) auto auto auto;
  grid-template-areas:
    "helper helper"
    "shead shead"
    "pin stream"
    "pin attachments"
    "pin input"
    "pin footer";
}
able-claw[data-pin-top] .aclaw-surface-header,
able-claw[data-pin-side] .aclaw-surface-header { grid-area: shead; }
able-claw[data-pin-top] .aclaw-pinslot {
  grid-area: pin; position: static; z-index: auto;
  overflow-y: auto; min-height: 0;
  padding-bottom: 28px;
}
/* The top form bounds the dock at 45% of the chat, so a tall card is cut. The fade
   says so, and the padding above is what lets the last line scroll clear of it. */
able-claw[data-pin-top] .aclaw-pinslot::after {
  content: ''; display: block; position: sticky; bottom: 0;
  height: 28px; margin-top: -28px; pointer-events: none;
  background: linear-gradient(to bottom, transparent, var(--lg-wp-1));
}
able-claw[data-pin-side] .aclaw-pinslot {
  grid-area: pin; position: static; z-index: auto;
  align-self: stretch; overflow-y: auto; min-height: 0;
  padding: 14px 16px;
  border-right: 1px solid var(--border-subtle, #e5e7eb);
}
/* ── surface="entity-review" — the claw as a one-entity review desk. The
   wandering chrome goes; the surface header (built by _renderSurfaceHeader)
   carries the entity line + the deterministic quick actions instead. */
able-claw[surface="entity-review"] .aclaw-sidebar,
able-claw[surface="entity-review"] .aclaw-convs-toggle,
able-claw[surface="entity-review"] .aclaw-rail-new-chat,
able-claw[surface="entity-review"] .aclaw-new-chat,
able-claw[surface="entity-review"] .aclaw-agent-selector,
able-claw[surface="entity-review"] .aclaw-model-pick,
able-claw[surface="entity-review"] .aclaw-model-chip,
able-claw[surface="entity-review"] .aclaw-greeting,
able-claw[surface="entity-review"] .aclaw-channel-divider,
able-claw[surface="entity-review"] .aclaw-welcome,
/* the footer strip reads "Read-only" — on a desk whose whole point is recording
   a decision, that line is false */
able-claw[surface="entity-review"] .aclaw-input-footer,
/* the floating thinking toggle sits in the top-right corner — over the
   surface header's Reject button; the activity trace stays reachable from the
   footer status verb */
able-claw[surface="entity-review"] .aclaw-thinking-toggle,
/* the helper header names the AGENT; on a review desk the surface header
   names the ENTITY and is the one header the spec asks for */
able-claw[surface="entity-review"] .aclaw-helper-header { display: none !important; }
/* The phone fade strip softens content scrolling under the hamburger; here
   nothing scrolls under the top — the surface header IS the top — and the
   strip painted over its first row (the title vanished on a phone). */
able-claw[surface="entity-review"] .aclaw-chat::after { content: none; }
/* The surface header already states the entity's status; on the docked card
   the status pill would say it a second time and squeeze the brand name. */
able-claw[surface="entity-review"] .aclaw-pinslot .claw-post-head .claw-pill { display: none; }
/* Above the stream the pin row is 62% of the claw: a full-size media band alone
   fills it and the caption starts below the fold. Bounding the media (never
   cropping it — the width follows) keeps the account row and the art in the row;
   the copy scrolls inside the dock. The art is what is being reviewed, so it takes
   most of the row — a 9:16 reel at 220px was a strip a third of the width. */
able-claw[data-pin-top] .aclaw-pinslot .claw-post-media img,
able-claw[data-pin-top] .aclaw-pinslot .claw-post-media video { max-height: min(600px, 52dvh); }
/* Beside the stream the column is as tall as the claw. The cap follows the
   viewport so the card fits the column on short screens, and it is tall enough
   that a portrait reel reads as a reel rather than a thumbnail. */
able-claw[data-pin-side] .aclaw-pinslot .claw-post-media img,
able-claw[data-pin-side] .aclaw-pinslot .claw-post-media video { max-height: min(640px, 58dvh); }
/* surface="entity-review": the pinned card is a FIXED, FULLY-VISIBLE reference at
   the top of the thread — the surface header (Approve/Reject) above it, the whole
   card shown with no internal scroll, and the message stream the ONLY thing that
   scrolls beneath it. The card is a REFERENCE (the full copy lives in the review
   pane beside the claw), so the art is capped and the caption shows a preview: a
   pane-width square card would fill the pane and leave the conversation no room.
   The pin row is bounded so the stream always keeps its share. Scoped to this
   surface so every other narrow pin-top claw keeps the generic bounded dock. */
able-claw[surface="entity-review"][data-pin-top] .aclaw-chat {
  grid-template-rows: auto auto minmax(0, 60%) minmax(0, 1fr) auto auto auto;
}
able-claw[surface="entity-review"][data-pin-top] .aclaw-pinslot {
  overflow: hidden; padding-bottom: 0;
}
able-claw[surface="entity-review"][data-pin-top] .aclaw-pinslot::after { content: none; }
able-claw[surface="entity-review"][data-pin-top] .aclaw-pinslot .claw-post-media img,
able-claw[surface="entity-review"][data-pin-top] .aclaw-pinslot .claw-post-media video {
  max-height: 24dvh; width: auto; max-width: 100%; height: auto;
}
able-claw[surface="entity-review"][data-pin-top] .aclaw-pinslot .claw-post-caption {
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
/* The collapse control + backdrop exist only in the phone overlay (below); off by default. */
.aclaw-pin-collapse, .aclaw-pin-backdrop { display: none; }
/* < 768px (phones — reviewers work here): the pinned card is a COMPACT STRIP at the
   top — a thumbnail + the headline — with the status line and Approve/Reject on the
   row above it; the thread and its composer own the rest of the height. Tapping the
   strip opens the FULL card as a scrollable overlay over the thread (the X or a tap on
   the dimmed thread closes it; the status + Approve/Reject stay reachable above it).
   Desktop keeps the full fixed card (the rules above, outside this query). */
@media (max-width: 767px) {
  able-claw[surface="entity-review"][data-pin-top] .aclaw-chat {
    position: relative;
    grid-template-rows: auto auto auto minmax(0, 1fr) auto auto auto;
  }
  able-claw[surface="entity-review"][data-pin-top] .aclaw-surface-header {
    padding: 6px 10px; gap: 8px; flex-wrap: nowrap; align-items: center;
  }
  able-claw[surface="entity-review"][data-pin-top] .aclaw-surface-who {
    flex: 1 1 auto; min-width: 0;
  }
  able-claw[surface="entity-review"][data-pin-top] .aclaw-surface-name { font-size: 13px; }
  able-claw[surface="entity-review"][data-pin-top] .aclaw-surface-ava { width: 26px; height: 26px; font-size: 12px; }
  able-claw[surface="entity-review"][data-pin-top] .aclaw-surface-actions { flex: 0 0 auto; }
  able-claw[surface="entity-review"][data-pin-top] .aclaw-rev-act { min-height: 32px; padding: 0 12px; font-size: 12px; }
  able-claw[surface="entity-review"][data-pin-top] .aclaw-pinslot { overflow: visible; padding: 0; }
  able-claw[surface="entity-review"][data-pin-top]:not([data-pin-expanded]) .aclaw-pinslot .claw-post {
    display: flex; flex-direction: row; align-items: center; gap: 10px;
    padding: 8px 34px 8px 8px; min-height: 68px; cursor: pointer; position: relative;
  }
  able-claw[surface="entity-review"][data-pin-top]:not([data-pin-expanded]) .aclaw-pinslot .claw-post-media {
    flex: 0 0 auto; width: 56px; height: 56px; margin: 0;
  }
  able-claw[surface="entity-review"][data-pin-top]:not([data-pin-expanded]) .aclaw-pinslot .claw-post-media img,
  able-claw[surface="entity-review"][data-pin-top]:not([data-pin-expanded]) .aclaw-pinslot .claw-post-media video {
    width: 56px; height: 56px; max-height: none; object-fit: cover; border-radius: 8px;
  }
  able-claw[surface="entity-review"][data-pin-top]:not([data-pin-expanded]) .aclaw-pinslot .claw-post-caption {
    margin: 0; font-weight: 600;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  }
  able-claw[surface="entity-review"][data-pin-top]:not([data-pin-expanded]) .aclaw-pinslot .claw-post-head,
  able-claw[surface="entity-review"][data-pin-top]:not([data-pin-expanded]) .aclaw-pinslot .claw-post-tags,
  able-claw[surface="entity-review"][data-pin-top]:not([data-pin-expanded]) .aclaw-pinslot .claw-post-meta {
    display: none;
  }
  able-claw[surface="entity-review"][data-pin-top]:not([data-pin-expanded]) .aclaw-pinslot .claw-post::after {
    content: ''; position: absolute; right: 12px; top: 50%; width: 9px; height: 9px;
    border-right: 2px solid var(--text-muted, #9aa4b2); border-bottom: 2px solid var(--text-muted, #9aa4b2);
    transform: translateY(-60%) rotate(45deg);
  }
  able-claw[surface="entity-review"][data-pin-top][data-pin-expanded] .aclaw-surface-header {
    position: relative; z-index: 7;
  }
  able-claw[surface="entity-review"][data-pin-top][data-pin-expanded] .aclaw-pin-backdrop {
    display: block; position: absolute; inset: 0; z-index: 5; background: rgba(0, 0, 0, 0.55);
  }
  able-claw[surface="entity-review"][data-pin-top][data-pin-expanded] .aclaw-pinslot {
    grid-area: 1 / 1 / -1 / -1; position: absolute; top: 56px; left: 12px; right: 12px; bottom: 84px;
    z-index: 6; overflow: auto; -webkit-overflow-scrolling: touch;
    background: var(--surface-card, #141415); border: 1px solid var(--border-default, #333);
    border-radius: 14px; box-shadow: 0 16px 48px rgba(0, 0, 0, 0.55); padding: 12px;
  }
  able-claw[surface="entity-review"][data-pin-top][data-pin-expanded] .aclaw-pinslot .claw-post-media img,
  able-claw[surface="entity-review"][data-pin-top][data-pin-expanded] .aclaw-pinslot .claw-post-media video {
    max-height: none; width: 100%; height: auto;
  }
  able-claw[surface="entity-review"][data-pin-top][data-pin-expanded] .aclaw-pinslot .claw-post-caption {
    -webkit-line-clamp: unset; display: block; overflow: visible;
  }
  able-claw[surface="entity-review"][data-pin-top][data-pin-expanded] .aclaw-pin-collapse {
    display: flex; position: sticky; top: 0; margin-left: auto;
    align-items: center; justify-content: center; width: 32px; height: 32px;
    border: none; border-radius: 8px; background: var(--surface-subtle, #222);
    color: var(--text-primary, #eee); font-size: 20px; line-height: 1; cursor: pointer;
  }
}
.aclaw-surface-header {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 8px 16px; border-bottom: 1px solid var(--border-subtle, #e5e7eb);
  background: var(--surface-subtle, transparent);
}
.aclaw-surface-header[hidden] { display: none !important; }
.aclaw-surface-who { display: flex; align-items: center; gap: 10px; min-width: 0; }
.aclaw-surface-ava {
  width: 30px; height: 30px; flex: 0 0 auto; border-radius: 50%;
  display: grid; place-items: center; color: #fff; font-weight: 800; font-size: 14px;
  background: var(--accent-secondary, var(--ableclaw-accent, var(--accent, #3b82f6)));
}
.aclaw-surface-idn { display: flex; flex-direction: column; min-width: 0; line-height: 1.25; }
.aclaw-surface-name {
  font-weight: 800; font-size: 14px; color: var(--text-primary, #111827);
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.aclaw-surface-sub {
  font-family: var(--font-mono, ui-monospace, monospace); font-size: 10.5px;
  letter-spacing: .04em; color: var(--text-muted, #6b7280);
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.aclaw-surface-actions { display: flex; gap: 6px; margin-left: auto; }
/* The working verb, on the surface that hides the footer the pill normally lives in.
   Between the entity line and the actions, so "what is it doing" sits where the reader
   already looks for "what is this". */
.aclaw-surface-status {
  display: inline-flex; align-items: center; gap: 6px; margin-left: 10px;
  font-size: 11.5px; font-weight: 600; letter-spacing: .01em; white-space: nowrap;
  color: var(--text-muted, #6b7280); min-width: 0; overflow: hidden; text-overflow: ellipsis;
}
.aclaw-surface-status[hidden] { display: none !important; }
/* The dock says the card is being worked on. A bar along the pinned card's own top edge
   rather than a spinner somewhere else: the thing that is about to change is the thing
   that carries the progress. Sticky so it stays put while the card scrolls under it. */
.aclaw-pin-working {
  position: sticky; top: 0; z-index: 6; display: block;
  margin: 0 0 8px; padding: 0; pointer-events: none;
}
.aclaw-pin-working[hidden] { display: none !important; }
.aclaw-pin-working-bar {
  display: block; height: 3px; border-radius: 999px; overflow: hidden;
  background: var(--border-subtle, #e5e7eb); position: relative;
}
.aclaw-pin-working-bar::after {
  content: ''; position: absolute; top: 0; left: 0; height: 100%; width: 38%;
  border-radius: 999px; background: var(--ableclaw-accent, #2563eb);
  animation: aclaw-pin-slide 1.15s ease-in-out infinite;
}
@keyframes aclaw-pin-slide {
  0% { left: -38%; } 100% { left: 100%; }
}
.aclaw-pin-working-note {
  display: block; margin-top: 5px; font-size: 11px; font-weight: 600;
  color: var(--text-muted, #6b7280);
}
/* The two review verbs. Reject is an outline in the danger tone; Approve is the
   filled primary. The status tokens fill a shape rather than read as ink, so the
   outline mixes the danger hue toward the theme text for a legible border/label,
   and the fill uses the accent pair straight. One declaration serves both modes. */
.aclaw-rev-act {
  display: inline-flex; align-items: center; gap: 6px;
  min-height: 34px; padding: 0 14px; border-radius: var(--radius-lg, 10px);
  font-weight: 700; font-size: 13px; cursor: pointer; white-space: nowrap;
  border: 1px solid transparent;
  transition: background-color 140ms ease, border-color 140ms ease, color 140ms ease;
}
.aclaw-rev-act .ic { font-size: 13px; }
.aclaw-rev-act--approve {
  background: var(--accent, #2563eb); color: var(--on-accent, #fff);
  border-color: var(--accent, #2563eb);
}
.aclaw-rev-act--reject {
  background: transparent;
  color: color-mix(in oklch, var(--danger, #b42318) 62%, var(--text-primary, #1d1c17));
  border-color: color-mix(in oklab, var(--danger, #b42318) 45%, var(--border-default, #d0d5dd));
}
@media (hover: hover) and (pointer: fine) {
  .aclaw-rev-act--approve:hover { background: var(--accent-hover, var(--accent, #2563eb)); border-color: var(--accent-hover, var(--accent, #2563eb)); }
  .aclaw-rev-act--reject:hover {
    border-color: var(--danger, #b42318);
    background: var(--danger-soft, color-mix(in oklab, var(--danger, #b42318) 10%, transparent));
  }
}
/* Armed-entity pill (the context wire). Wave-2 shell-chrome tokens (C10):
   the accent token carries the wash — dark by construction. */
.aclaw-armed {
  display: inline-flex; align-items: center; gap: 6px; margin: 0 16px 4px;
  align-self: flex-start; padding: 4px 6px 4px 12px; border-radius: 999px;
  font-size: 12px; font-weight: 700;
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
}
.aclaw-armed-clear {
  border: none; background: transparent; cursor: pointer; font-size: 14px;
  line-height: 1; padding: 4px 7px; color: inherit; border-radius: 999px; min-height: 24px;
}
[data-entity] { cursor: pointer; }
/* Status — the demoted reasoning verb in the footer. */
.aclaw-status {
  display: inline-flex; align-items: center; gap: 6px;
  border: none; background: transparent; cursor: pointer;
  font-size: 11.5px; font-weight: 600; color: var(--ableclaw-muted, #6b7280);
  padding: 2px 8px; border-radius: 999px; min-height: 24px;
}
.aclaw-status--done { color: var(--success); }
.aclaw-status-dot {
  width: 7px; height: 7px; border-radius: 50%; background: var(--accent);
  animation: aclaw-status-blink 1.1s ease-in-out infinite;
}
@keyframes aclaw-status-blink { 0%,100% { opacity: .35; } 50% { opacity: 1; } }
/* Live elapsed clock on the streaming status pill (mirrors iOS WorkingIndicator). */
.aclaw-status-clock { opacity: .6; font-variant-numeric: tabular-nums; font-size: .92em; }
/* Mic — push-to-talk (hidden unless the browser has speech recognition). */
.aclaw-mic {
  /* Sized + centered like its neighbours in the composer's control cluster
     (upload / send are both 38px round). It used to be a display:block button
     relying on line-height to center its glyph — it happened to land right,
     but nothing held it there. */
  display: inline-flex; align-items: center; justify-content: center;
  width: 38px; height: 38px; padding: 0; flex-shrink: 0;
  border: none; background: transparent; cursor: pointer; font-size: 15px;
  /* --text-muted (semantic, re-binds per mode) over the old hardcoded
     --ableclaw-muted #6b7280, which measured 3.58:1 on the dark composer. */
  color: var(--text-muted, #94a3b8); border-radius: 50%;
}
.aclaw-mic:hover { background: var(--ableclaw-sidebar-hover-bg, rgba(127, 127, 127, 0.10)); }
.aclaw-mic--live { color: var(--danger); animation: aclaw-status-blink 1s ease-in-out infinite; }
/* Glance layer — the ONE read-and-dismiss sheet. */
.aclaw-glance { position: fixed; inset: 0; z-index: 720; }
.aclaw-glance[hidden] { display: none !important; }
.aclaw-glance-backdrop { position: absolute; inset: 0; background: rgba(15,14,20,.38); }
.aclaw-glance-panel {
  position: absolute; top: 0; right: 0; bottom: 0;
  width: min(460px, 94vw); display: flex; flex-direction: column;
  background: var(--surface-card, #fff);
  box-shadow: -18px 0 48px -18px rgba(0,0,0,.4);
  animation: aclaw-glance-in 200ms cubic-bezier(.22,1,.36,1);
}
@keyframes aclaw-glance-in { from { transform: translateX(28px); opacity: 0; } to { transform: none; opacity: 1; } }
.aclaw-glance-header {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 13px 16px; border-bottom: 1px solid var(--border-subtle, rgba(0,0,0,.1));
  font-weight: 800; font-size: 14px;
}
.aclaw-glance-close {
  border: none; background: transparent; cursor: pointer; font-size: 16px;
  padding: 8px 10px; border-radius: 10px; color: inherit; min-height: 40px; min-width: 40px;
}
.aclaw-glance-body { padding: 14px 16px; overflow-y: auto; font-size: 13.5px; line-height: 1.5; }
.aclaw-glance-empty { color: var(--ableclaw-muted, #6b7280); }
@media (max-width: 640px) {
  .aclaw-glance-panel {
    top: auto; left: 0; width: 100%; max-height: 82dvh; border-radius: 16px 16px 0 0;
    animation: aclaw-glance-up 220ms cubic-bezier(.22,1,.36,1);
  }
  @keyframes aclaw-glance-up { from { transform: translateY(24px); opacity: 0; } to { transform: none; opacity: 1; } }
}
/* Ledger — dismissed sheets collapse to reopenable pills under the board. */
.aclaw-ledger { padding: 4px 12px 12px; display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.aclaw-ledger-label { font-size: 10.5px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--text-muted, #6b7280); }
.aclaw-ledger-pill {
  border: 1px solid var(--border-default, rgba(0,0,0,.14)); background: transparent;
  border-radius: 999px; padding: 4px 11px; font-size: 11.5px; font-weight: 600;
  color: inherit; cursor: pointer; min-height: 28px; max-width: 160px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Native asks — first-party question cards (data-only agent markup). */
.aclaw-ask {
  margin: 10px 0; padding: 14px 15px; border-radius: 14px;
  background: var(--surface-card, #fff);
  border: 1px solid var(--ableclaw-table-border, rgba(0,0,0,.12));
  box-shadow: 0 1px 3px rgba(0,0,0,.05);
}
.aclaw-ask-topic {
  font-size: 10.5px; font-weight: 800; letter-spacing: .07em; text-transform: uppercase;
  color: var(--text-muted, #6b7280); margin: 0 0 5px;
}
.aclaw-ask-prompt { font-size: 14.5px; font-weight: 700; line-height: 1.45; margin: 0 0 11px; }
/* The AUDIO face (L3). The play control is the item, so it is sized like one:
 * a 44px tap target that reads as the primary gesture, above the prompt. */
.aclaw-ask-audio {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 0 0 10px;
}
.aclaw-ask-play {
  display: inline-flex; align-items: center; gap: 9px;
  min-height: 44px; padding: 10px 18px 10px 15px;
  border: 1.5px solid var(--ableclaw-accent, #2563eb);
  border-radius: 999px; background: var(--ableclaw-accent, #2563eb); color: #fff;
  font: inherit; font-size: 15px; font-weight: 700; cursor: pointer;
  transition: transform 140ms cubic-bezier(.22,1,.36,1), opacity 140ms;
}
@media (hover: hover) and (pointer: fine) {
  .aclaw-ask-play:hover { opacity: .9; }
}
.aclaw-ask-play:active { transform: scale(.97); }
.aclaw-ask-play-glyph { font-size: 13px; line-height: 1; }
.aclaw-ask--playing .aclaw-ask-play { opacity: .72; }
.aclaw-ask--playing .aclaw-ask-play-glyph { animation: aclaw-ask-pulse 900ms ease-in-out infinite; }
@keyframes aclaw-ask-pulse { 0%,100% { opacity: 1; } 50% { opacity: .35; } }
/* THE HINT MUST WRAP, NOT RUN OFF. A bare inline span in a flex row will not
 * shrink below its content width, so the FAILURE sentence — the longest text
 * this row ever carries, and the one a learner most needs — ran off the right
 * edge of the card instead of wrapping under the button. Seen on a screenshot,
 * not in any check: flex-wrap on the parent cannot help an item that refuses to
 * narrow; min-width 0 is what lets it.
 * (And this comment is inside the chrome CSS template literal, so it carries no
 *  backtick and no dollar-brace — the first draft did, and took the whole
 *  bundle down. bundle-safety caught it in one run.) */
.aclaw-ask-replay-hint {
  font-size: 11.5px; color: var(--ableclaw-muted, #6b7280);
  flex: 1 1 200px; min-width: 0; line-height: 1.4;
}
.aclaw-ask--audio-failed .aclaw-ask-play { opacity: .5; }
.aclaw-ask--audio-failed .aclaw-ask-replay-hint { color: var(--ableclaw-danger, #b91c1c); }
@media (prefers-reduced-motion: reduce) {
  .aclaw-ask-play { transition: none; }
  .aclaw-ask--playing .aclaw-ask-play-glyph { animation: none; }
}
.aclaw-ask-opt {
  display: block; width: 100%; text-align: left; margin: 6px 0; padding: 11px 13px;
  border: 1.5px solid var(--ableclaw-table-border, rgba(0,0,0,.14));
  border-radius: 11px; background: transparent; color: inherit;
  font: inherit; font-size: 16px; line-height: 1.45; cursor: pointer; min-height: 44px;
  transition: border-color 140ms, background 140ms;
}
/* w5/L4 — the two-form options wrap. CHIP ROW (every option ≤28 graphemes):
   wrap-flex pills, 44px min tap. MARKER LIST (any longer): stacked buttons with
   a leading letter chip + hanging-indent text that wraps freely. */
.aclaw-ask-opts { display: flex; flex-direction: column; gap: 6px; margin: 6px 0 0; }
.aclaw-ask-opts .aclaw-ask-opt { margin: 0; }
.aclaw-ask-opts[data-form="chips"] { flex-direction: row; flex-wrap: wrap; gap: 8px; }
.aclaw-ask-opts[data-form="chips"] .aclaw-ask-opt {
  display: inline-flex; align-items: center; justify-content: center;
  width: auto; min-width: 44px; padding: 10px 16px; border-radius: 999px;
}
.aclaw-ask-opts[data-form="list"] .aclaw-ask-opt { display: flex; align-items: flex-start; gap: 10px; }
.aclaw-ask-marker {
  flex: 0 0 auto; width: 22px; height: 22px; margin-top: 1px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 11px; font-weight: 700; line-height: 1;
  color: var(--text-muted); background: var(--surface-subtle);
  border: 1px solid var(--border-subtle);
}
.aclaw-ask-opt-text { flex: 1 1 auto; min-width: 0; overflow-wrap: break-word; }
@media (hover: hover) and (pointer: fine) {
  .aclaw-ask-opt:not(:disabled):hover { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 6%, transparent); }
}
.aclaw-ask-opt:disabled { cursor: default; opacity: .92; }
.aclaw-ask-opt--right { border-color: var(--success); background: color-mix(in srgb, var(--success) 10%, transparent); }
.aclaw-ask-opt--wrong { border-color: var(--danger); background: color-mix(in srgb, var(--danger) 9%, transparent); }
.aclaw-ask-opt--picked { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 9%, transparent); }
.aclaw-ask-opt--reveal { border-color: var(--success); border-style: dashed; }
.aclaw-ask-why {
  margin: 10px 0 0; padding: 9px 12px; border-radius: 9px; font-size: 12.5px; line-height: 1.5;
  color: var(--text-secondary, #4b5563);
  background: color-mix(in srgb, var(--accent) 6%, transparent);
  border-left: 3px solid var(--accent);
}
/* Quiz-set progress footer — "3 of 8 answered" on each card in a riff set. */
.aclaw-ask-setbar {
  margin: 9px 0 0; padding-top: 8px; border-top: 1px dashed var(--ableclaw-border-soft, rgba(0,0,0,.1));
  font-size: 11px; font-weight: 700; letter-spacing: .02em; text-transform: uppercase;
  color: var(--text-muted, #9ca3af);
}
.aclaw-ask-setbar.is-complete { color: var(--ableclaw-accent-text, var(--accent)); }
.aclaw-ask-inputrow { display: flex; gap: 8px; min-width: 0; }
/* min-width:0 is load-bearing: a flex item's default min-width:auto is its
   INTRINSIC min (~180px here), so input + the ~80px submit overflowed the card
   at 320px and the submit's right edge landed 19px past the viewport (partly
   untappable). With min-width:0 the input yields and the row always fits. */
.aclaw-ask-input {
  flex: 1; min-width: 0; padding: 10px 12px; font: inherit; font-size: max(16px, 13.5px);
  border: 1.5px solid var(--ableclaw-table-border, rgba(0,0,0,.14));
  border-radius: 11px; background: transparent; color: inherit; min-height: 44px;
}
.aclaw-ask-submit {
  border: none; border-radius: 11px; padding: 10px 18px; font-weight: 700; font-size: 13px;
  background: var(--accent); color: var(--on-accent, #fff); cursor: pointer; min-height: 44px;
}
/* PRE-EXISTING AA FAIL, surfaced by the verdict row landing right beneath it: the
   --ableclaw-muted/#6b7280 pattern is mode-independent (the var is unset), so this
   line reads 3.58:1 on the dark card. Same one-token fix, same reason as the note. */
.aclaw-ask-answered { font-size: 13px; color: var(--text-muted, #6b7280); }
/* ── FREE-FORM VERDICT (the submit seam) — a typed answer is adjudicated by the
   server in ~1 s instead of by a ~40 s conversational turn, so the card needs a
   place to say so. Three honest states and no fourth: checking (bounded — the
   seam client aborts, so this can never become a permanent spinner), a verdict,
   or a degrade line. is-pending is the RULE 4 state: recorded, not judged.
   ⚠ NO BACKTICKS ANYWHERE IN THIS BLOCK — it is a template literal, so one
   backtick in a comment closes the CSS and the WHOLE bundle stops parsing
   (i.e. chat dies fleet-wide). node --check catches it; run it before push. */
.aclaw-ask-verdict {
  margin: 8px 0 0; display: flex; align-items: baseline; gap: 8px;
  font-size: 12.5px; line-height: 1.45; color: var(--ableclaw-muted, #4b5563);
}
.aclaw-ask-verdict b { font-weight: 700; letter-spacing: .01em; }
.aclaw-ask-verdict.is-checking { opacity: .72; }
.aclaw-ask-verdict.is-checking b::after {
  content: '…'; display: inline-block; animation: aclawAskDots 1.1s steps(4, end) infinite;
}
@keyframes aclawAskDots { 0% { opacity: .35; } 50% { opacity: 1; } 100% { opacity: .35; } }
.aclaw-ask-verdict.is-correct b { color: var(--success); }
.aclaw-ask-verdict.is-partial b { color: var(--warning); }
.aclaw-ask-verdict.is-incorrect b { color: var(--danger); }
.aclaw-ask-verdict.is-pending b, .aclaw-ask-verdict.is-degraded b { color: var(--text-muted); }
/* --text-muted, NOT the --ableclaw-muted/#6b7280 pattern the rest of this card uses:
   --ableclaw-muted is UNSET, so that pattern resolves to the hardcoded literal, which
   is mode-INDEPENDENT — 4.83:1 on the light card but 3.58:1 on the dark one, i.e. it
   fails AA in exactly the mode you were not looking at. Measured live on build.
   --text-muted is semantic and re-binds: 4.83 light / 5.62 dark. */
.aclaw-ask-verdict-note { color: var(--text-muted, #6b7280); }
@media (prefers-reduced-motion: reduce) {
  .aclaw-ask-verdict.is-checking b::after { animation: none; }
}
/* Answer pill — the honest transcript record of a click (not a fake bubble). */
.aclaw-message-bubble--answer { background: transparent !important; border: none !important; box-shadow: none !important; padding: 0 !important; }
.aclaw-answer-pill {
  display: inline-flex; align-items: baseline; gap: 7px;
  padding: 6px 13px; border-radius: 999px; font-size: 12.5px; font-weight: 600;
  border: 1px solid var(--ableclaw-table-border, rgba(0,0,0,.14));
  color: var(--text-secondary, #4b5563); background: var(--surface-card, #fff);
}
.aclaw-answer-pill-title { font-weight: 500; color: var(--text-muted, #6b7280); }
.aclaw-answer-pill--correct { border-color: color-mix(in srgb, var(--success) 45%, transparent); }
.aclaw-answer-pill--correct b { color: var(--success); }
.aclaw-answer-pill--incorrect { border-color: color-mix(in srgb, var(--danger) 40%, transparent); }
.aclaw-answer-pill--incorrect b { color: var(--danger); }

/* ── INPUT-REQUIRED question card (INPUT-REQUIRED-CONTRACT §3.1) — ask-shaped, with
   NO verdict: a verb asked the person for a decisive value, and a decision is not a
   graded quiz. It borrows the ask option/input/submit chrome above (one vocabulary,
   both themes, every colour a semantic token); only this thin layout is its own.
   ⚠ NO BACKTICK, NO dollar-brace ANYWHERE in this block — it lives in the chrome
   template literal, so either one closes the CSS and takes the whole bundle down. */
.aclaw-question-message {
  font-size: 14px; line-height: 1.5; color: var(--text-primary); margin: 0 0 4px;
}
.aclaw-question-item { margin: 12px 0 0; }
.aclaw-question-item:first-child { margin-top: 0; }
.aclaw-question-other { margin: 8px 0 0; }
.aclaw-question-actions { margin: 12px 0 0; display: flex; justify-content: flex-end; }
.aclaw-q-send:disabled { opacity: .5; cursor: default; }

/* The [hidden] contract — our display: declarations above would beat the UA's
   [hidden]{display:none} (lower specificity), leaving "hidden" stage chrome
   visibly rendered (an empty armed pill, a stray Conversations toggle). */
.aclaw-rail[hidden], .aclaw-convs-toggle[hidden], .aclaw-pinslot[hidden],
.aclaw-armed[hidden], .aclaw-status[hidden], .aclaw-mic[hidden],
.aclaw-ledger[hidden] { display: none !important; }
/* ── HERO-SLOT-FLIP (council spec) — the wide column follows the persona. ──
   Study: the THREAD is the hero (Max tunnels on the lesson), the board is a
   fixed readout. Floor: the BOARD/dossier is the hero (the collector stares
   at it), the thread narrows to a coaching column. Driven by data-stage which
   the agent sets via <able-stage name>. */
able-claw[data-stage="study-hall"][data-mode="full"] > .aclaw-grid:has(.aclaw-artifact:not([hidden])) {
  grid-template-columns: var(--ableclaw-sidebar-width) 1fr 380px;   /* thread hero */
}
able-claw[data-stage="the-floor"][data-mode="full"] > .aclaw-grid:has(.aclaw-artifact:not([hidden])) {
  grid-template-columns: 320px minmax(380px, 0.9fr) 1.35fr;         /* dossier hero */
}

/* ── THE WORKSPACE (right pane) — one sticky head + one scroll, NO tabs
   (restructure spec). The route (rail), the board, and the reasoning drawer
   all co-scroll; the head holds the adaptive title + the stable fly-to meter. */
.aclaw-workspace-head {
  position: sticky; top: 0; z-index: 2; flex-shrink: 0;
  display: flex; align-items: center; gap: 10px;
  padding: 10px 12px 10px 16px;
  border-bottom: 1px solid var(--ableclaw-border-soft);
  background: var(--ableclaw-artifact-bg, var(--ableclaw-bg));
}
.aclaw-ws-title {
  font-family: var(--font-display, inherit);
  font-size: 13px; font-weight: 700; letter-spacing: -0.01em;
  color: var(--ableclaw-fg, #1d1c17); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; flex: 0 1 auto;
}
.aclaw-ws-meter { flex: 1 1 auto; min-width: 0; }   /* stable reward fly-to target */
.aclaw-ws-head-actions { flex: 0 0 auto; display: flex; align-items: center; }
.aclaw-ws-head-actions .aclaw-artifact-close {
  border: none; background: transparent; cursor: pointer; color: inherit;
  padding: 6px 8px; min-height: 32px; border-radius: 8px; opacity: .6;
}
.aclaw-ws-head-actions .aclaw-artifact-close:hover { opacity: 1; background: var(--ableclaw-sidebar-hover-bg, rgba(127,127,127,.1)); }
.aclaw-workspace-scroll {
  flex: 1 1 auto; min-height: 0; overflow-y: auto;
  display: flex; flex-direction: column; gap: 12px;
  padding: 12px;
}
.aclaw-ws-rail:empty, .aclaw-ws-board:empty { display: none; }
.aclaw-ws-rail, .aclaw-ws-board { display: flex; flex-direction: column; gap: 10px; }
/* Reasoning drawer — a co-scrolling <details> at the bottom, collapsed default. */
.aclaw-reasoning-drawer {
  margin-top: auto;   /* sink to the bottom of the scroll when content is short */
  border-top: 1px solid var(--ableclaw-border-soft);
  padding-top: 8px;
}
.aclaw-reasoning-summary {
  display: flex; align-items: center; gap: 7px; cursor: pointer;
  list-style: none; font-size: 12px; font-weight: 700;
  color: var(--ableclaw-text-muted, #6b7280); padding: 4px 2px;
  user-select: none;
}
.aclaw-reasoning-summary::-webkit-details-marker { display: none; }
.aclaw-reasoning-summary::before {
  content: "▸"; font-size: 10px; transition: transform .15s ease;
}
.aclaw-reasoning-drawer[open] .aclaw-reasoning-summary::before { transform: rotate(90deg); }
.aclaw-reasoning-summary i { color: var(--ableclaw-accent-text, #6366f1); }
.aclaw-reasoning-count {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 16px; height: 16px; padding: 0 4px; border-radius: 999px;
  font-size: 10px; font-weight: 800; color: #fff; background: var(--ableclaw-accent, #6366f1);
}
.aclaw-reasoning-drawer .aclaw-timeline { padding: 8px 2px 4px; }

/* ⭐ Instant-reward chip — flies from a correct answer into the mastery meter. */
.aclaw-reward-chip {
  position: fixed; z-index: 900; pointer-events: none;
  padding: 4px 11px; border-radius: 999px;
  font-family: var(--font-mono, 'IBM Plex Mono', monospace);
  font-size: 14px; font-weight: 800; letter-spacing: .02em;
  color: #fff; background: #16a34a;
  box-shadow: 0 4px 14px rgba(22,163,74,.45);
  transform: translate(0,0);
}
@keyframes aclaw-board-reward { 0% { box-shadow: 0 0 0 0 rgba(22,163,74,.5); } 100% { box-shadow: 0 0 0 12px rgba(22,163,74,0); } }
.aclaw-board--reward { animation: aclaw-board-reward .7s ease-out 1; border-radius: 12px; }
@media (prefers-reduced-motion: reduce) { .aclaw-board--reward { animation: none; } }

/* Collector queue keyboard focus (j/k/↑/↓ + Enter, floor mode). */
.aclaw-chip--focus {
  outline: 2px solid var(--ableclaw-accent, #6366f1);
  outline-offset: 1px;
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--ableclaw-accent, #6366f1) 20%, transparent);
}

/* ═══ REALTIME VOICE — compact in-thread strip (the overlay is gone; z-700 freed) ═══ */
.aclaw-voice-strip {
  position: relative;
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  min-height: 44px; padding: 4px 12px; margin: 0 0 6px;
  border: 1px solid var(--ableclaw-border, rgba(127,127,127,.2)); border-radius: 12px;
  background: var(--ableclaw-bubble-agent-bg, rgba(127,127,127,.06));
  font-size: 12.5px; color: var(--ableclaw-fg, #1d1c17);
}
/* State dot — the orb's pulse keyframes, recycled at strip scale. */
.aclaw-voice-strip-dot {
  width: 10px; height: 10px; border-radius: 50%; flex: 0 0 auto;
  background: radial-gradient(circle at 35% 30%, var(--ableclaw-accent, #6366f1), color-mix(in srgb, var(--ableclaw-accent,#6366f1) 55%, #000));
  box-shadow: 0 0 0 0 color-mix(in srgb, var(--ableclaw-accent,#6366f1) 40%, transparent);
}
.aclaw-voice-strip[data-vstate="listening"] .aclaw-voice-strip-dot { animation: aclaw-voice-listen 1.6s ease-in-out infinite; }
.aclaw-voice-strip[data-vstate="speaking"]  .aclaw-voice-strip-dot { animation: aclaw-voice-speak 0.8s ease-in-out infinite; }
.aclaw-voice-strip[data-vstate="connecting"] .aclaw-voice-strip-dot,
.aclaw-voice-strip[data-vstate="reconnecting"] .aclaw-voice-strip-dot { animation: aclaw-voice-think 1s linear infinite; }
.aclaw-voice-strip[data-vstate="error"] .aclaw-voice-strip-dot { background: #dc2626; animation: none; }
@keyframes aclaw-voice-listen { 0%,100%{ box-shadow:0 0 0 0 color-mix(in srgb,var(--ableclaw-accent,#6366f1) 40%,transparent);} 50%{ box-shadow:0 0 0 6px color-mix(in srgb,var(--ableclaw-accent,#6366f1) 0%,transparent);} }
@keyframes aclaw-voice-speak { 0%,100%{ transform:scale(1);} 50%{ transform:scale(1.3);} }
@keyframes aclaw-voice-think { 0%{opacity:.5;} 50%{opacity:1;} 100%{opacity:.5;} }
.aclaw-voice-strip-state { font-weight: 600; white-space: nowrap; }
.aclaw-voice-strip-level { flex: 1 1 60px; max-width: 140px; height: 6px; border-radius: 999px; background: color-mix(in srgb, var(--ableclaw-fg,#888) 12%, transparent); overflow: hidden; }
.aclaw-voice-strip-level-fill { display: block; height: 100%; width: 0%; border-radius: 999px; background: color-mix(in srgb, var(--ableclaw-fg,#888) 35%, transparent); transition: width 90ms linear, background 120ms; }
.aclaw-voice-strip-level[data-live="1"] .aclaw-voice-strip-level-fill { background: #16a34a; }
.aclaw-voice-strip-notice { flex: 1 1 auto; min-width: 0; font-size: 11.5px; color: #b45309; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.aclaw-voice-strip button { font: inherit; cursor: pointer; }
.aclaw-voice-strip-start { padding: 6px 14px; border-radius: 999px; border: 1px solid transparent; background: var(--ableclaw-accent,#6366f1); color: var(--on-accent,#fff); font-weight: 700; font-size: 12px; }
.aclaw-voice-strip-mute, .aclaw-voice-strip-more { border: none; background: transparent; color: var(--ableclaw-muted,#6b7280); padding: 6px 8px; border-radius: 8px; min-width: 32px; min-height: 32px; }
.aclaw-voice-strip-mute.is-muted { color: #dc2626; }
.aclaw-voice-strip-end { padding: 5px 12px; border-radius: 999px; border: 1px solid var(--ableclaw-border, rgba(127,127,127,.3)); background: transparent; color: inherit; font-size: 12px; font-weight: 600; }
/* Voice options = an EXPANSION ROW inside the strip (flex-basis 100% wraps it
   to its own line) — the strip grows downward and the layout reflows; it never
   floats OVER the chat thread (the old absolute popover covered the working
   screen — Raul, 2026-07-04). */
.aclaw-voice-strip-pop {
  flex-basis: 100%; min-width: 0;
  padding: 8px 2px 6px; display: flex; align-items: center; gap: 10px;
  border-top: 1px dashed var(--ableclaw-border, rgba(127,127,127,.2));
}
.aclaw-voice-strip-pop .aclaw-voice-mic-select { flex: 1 1 auto; min-width: 0; max-width: 340px; }
.aclaw-voice-strip-poplabel { font-size: 10px; font-weight: 800; text-transform: uppercase; letter-spacing: .06em; color: var(--ableclaw-text-muted,#6b7280); }
.aclaw-voice-mic-select { font: inherit; font-size: 12px; padding: 6px 10px; border-radius: 8px; border: 1px solid var(--ableclaw-border, rgba(127,127,127,.3)); background: var(--ableclaw-bg,#fff); color: inherit; }
.aclaw-voice-mic-select[data-attn="1"] { border-color: #d97706; box-shadow: 0 0 0 3px color-mix(in srgb, #d97706 22%, transparent); }
.aclaw-voice-strip-hint { font-size: 12px; line-height: 1.45; background: color-mix(in srgb, #d97706 12%, transparent); border: 1px solid color-mix(in srgb,#d97706 30%,transparent); border-radius: 8px; padding: 8px 10px; }
@media (prefers-reduced-motion: reduce) { .aclaw-voice-strip-dot { animation: none !important; } }
@media (prefers-reduced-motion: reduce) {
  .aclaw-stage-card--pulse, .aclaw-glance-panel, .aclaw-status-dot, .aclaw-mic--live { animation: none !important; }
  .aclaw-pin-working-bar::after { animation: none !important; width: 100% !important; left: 0 !important; }
}
/* ═════════════════════ END THE STAGE CSS ═════════════════════ */

.aclaw-table {
  width: 100%; border-collapse: collapse;
  margin: 10px 0;
  font-size: 13px;
  color: inherit;
}
.aclaw-table th, .aclaw-table td {
  padding: 6px 10px;
  border: 1px solid var(--ableclaw-table-border);
  text-align: left;
  color: inherit;
  background: var(--ableclaw-table-row-bg);
}
.aclaw-table th {
  background: var(--ableclaw-table-header-bg);
  color: var(--ableclaw-table-header-fg);
  font-weight: 600;
}
/* Raw enrichment tags are REPLACED by their rendered figure on mount. Hide the
   un-rendered tag until then so its inner spec/JSON never flashes as visible text
   mid-stream (an <able-diagram> whose JSON spec sits as inner content was leaking
   the raw JSON for a beat before the chart drew). Once the renderer swaps the tag
   for a <figure>/<div>, that element shows normally — this only hides the source tag.

   ⚠ THIS LIST IS THE FLOOR, AND IT MUST CARRY EVERY TAG IN ENRICHMENT_RENDERERS.
   It was written for the seven tags that existed then; the composer-enterprise
   four (kpis / datagrid / approval / report) and mermaid were added to the
   vocabulary without being added here, so those were the only tags in the
   vocabulary whose JSON could paint — which is exactly the family the grids and
   charts belong to. An unregistered custom element is an inline box that renders
   its text node, so "not in this list" means "shows the payload".
   A tag added to ENRICHMENT_RENDERERS belongs here in the same change. */
.aclaw-messages able-diagram, .claw-root able-diagram,
.aclaw-messages able-kpis, .claw-root able-kpis,
.aclaw-messages able-kpi, .claw-root able-kpi,
.aclaw-messages able-datagrid, .claw-root able-datagrid,
.aclaw-messages able-approval, .claw-root able-approval,
.aclaw-messages able-post-preview, .claw-root able-post-preview,
.aclaw-messages able-report, .claw-root able-report,
.aclaw-messages able-intake, .claw-root able-intake,
.aclaw-messages able-journey, .claw-root able-journey,
.aclaw-messages able-capture, .claw-root able-capture,
.aclaw-messages able-mermaid, .claw-root able-mermaid { display: none; }

/* …and when the mount deliberately HELD a tag back (its renderer is in the lazy
   enrichments chunk and that chunk is still in flight), hiding it alone would
   leave a gap that the component later pops into. It becomes a SKELETON instead:
   the space is reserved, the payload stays unrenderable text inside a clipped
   box (font-size:0), and the swap has somewhere to land.

   Only the mount sets this attribute, and only while it is waiting — a tag that
   mounts in the same tick never wears it, so there is no one-frame shimmer in
   front of a component that was ready all along. */
.aclaw-messages [data-ent-pending], .claw-root [data-ent-pending] {
  display: block;
  font-size: 0; color: transparent;
  overflow: hidden;
  margin: 10px 0;
  border-radius: var(--ableclaw-radius-sm, 8px);
  border: 1px solid var(--ableclaw-border-soft);
  background-image: linear-gradient(90deg,
    color-mix(in srgb, var(--ableclaw-fg) 5%, transparent) 25%,
    color-mix(in srgb, var(--ableclaw-fg) 11%, transparent) 37%,
    color-mix(in srgb, var(--ableclaw-fg) 5%, transparent) 63%);
  background-size: 400% 100%;
  animation: aclaw-ent-pending 1.4s ease infinite;
}
/* able-math / able-audio / able-video / able-vocab / able-image / able-deck are
   registered blueprints (able-math.js / able-audio.js / able-video.js /
   able-vocab.js / able-image.js / able-deck.js). The claw owns only WHEN: it hides
   the un-revealed element during the stream (so a still-streaming formula never
   flashes half-written source, a media card never flickers as it rebuilds token by
   token, and a vocab row never shows its raw script mid-write) and reveals it
   (data-enriched), by which point the blueprint has upgraded it. Chat-scoped only;
   on a template page they paint at once. able-math/-audio/-video/-vocab reveal at
   the done-render (settle-only, mountEnrichments' reveal pass). able-image is EAGER:
   revealed as each element SEALS (revealStreamingImages, off the seal advance), so
   its resolve fires mid-stream — the reserved aspect box keeps that a zero-shift
   fill, and the IntersectionObserver only fires for a VISIBLE box, so a hidden tail
   element never resolves early. able-deck is the PARTIAL graduation: the blueprint
   owns the face render, but the browse/study orchestration is conversation-coupled,
   so the floor hides the raw tag until settle and mountDecks MOUNTS it through the
   chunk (which paints faces via the blueprint) — the raw tag never flashes its card
   payload mid-stream. able-test is settle-only the same way: the blueprint
   (able-test.js) hydrates the attempt through the submit seam, so it mounts ONCE,
   at the done-render, never mid-stream to be destroyed and re-hydrated a frame
   later. */
.aclaw-messages able-math:not([data-enriched]), .claw-root able-math:not([data-enriched]),
.aclaw-messages able-audio:not([data-enriched]), .claw-root able-audio:not([data-enriched]),
.aclaw-messages able-video:not([data-enriched]), .claw-root able-video:not([data-enriched]),
.aclaw-messages able-image:not([data-enriched]), .claw-root able-image:not([data-enriched]),
.aclaw-messages able-deck:not([data-enriched]), .claw-root able-deck:not([data-enriched]),
.aclaw-messages able-test:not([data-enriched]), .claw-root able-test:not([data-enriched]),
.aclaw-messages able-vocab:not([data-enriched]), .claw-root able-vocab:not([data-enriched]) {
  display: none;
}

/* It SAYS what it is waiting for, in the same words the streaming placeholder
   used a moment earlier (maskStreamingWidgets). Without this the person watches
   a labelled "Preparing the data grid…" card become an anonymous grey block and
   then a grid — three states for one thing. The label rides a pseudo-element, so
   it is unaffected by the font-size:0 that keeps the payload unrenderable. */
.aclaw-messages [data-ent-pending]::before, .claw-root [data-ent-pending]::before {
  content: 'Preparing…';
  display: block;
  padding: 10px 12px;
  font-size: 12px; font-weight: 600;
  color: var(--ableclaw-text-muted, var(--text-secondary, #64748b));
}
.aclaw-messages able-kpis[data-ent-pending]::before, .claw-root able-kpis[data-ent-pending]::before { content: 'Preparing the metrics…'; }
.aclaw-messages able-datagrid[data-ent-pending]::before, .claw-root able-datagrid[data-ent-pending]::before { content: 'Preparing the data grid…'; }
.aclaw-messages able-approval[data-ent-pending]::before, .claw-root able-approval[data-ent-pending]::before { content: 'Staging the approval…'; }
.aclaw-messages able-report[data-ent-pending]::before, .claw-root able-report[data-ent-pending]::before { content: 'Preparing the saved report…'; }
.aclaw-messages able-post-preview[data-ent-pending]::before, .claw-root able-post-preview[data-ent-pending]::before { content: 'Preparing the post…'; }
.aclaw-messages able-diagram[data-ent-pending]::before, .claw-root able-diagram[data-ent-pending]::before,
.aclaw-messages able-mermaid[data-ent-pending]::before, .claw-root able-mermaid[data-ent-pending]::before { content: 'Drawing the figure…'; }

/* Reserve something close to what each family actually occupies, so the swap
   settles the column instead of jolting it. */
.aclaw-messages able-kpis[data-ent-pending], .claw-root able-kpis[data-ent-pending] { height: 76px; }
.aclaw-messages able-datagrid[data-ent-pending], .claw-root able-datagrid[data-ent-pending] { height: 180px; }
.aclaw-messages able-diagram[data-ent-pending], .claw-root able-diagram[data-ent-pending],
.aclaw-messages able-mermaid[data-ent-pending], .claw-root able-mermaid[data-ent-pending] { height: 200px; }
.aclaw-messages able-approval[data-ent-pending], .claw-root able-approval[data-ent-pending],
.aclaw-messages able-report[data-ent-pending], .claw-root able-report[data-ent-pending] { height: 104px; }
/* Taller than its siblings on purpose: a post preview is head + art + caption,
   so reserving an approval-sized box would still jolt the column on the swap. */
.aclaw-messages able-post-preview[data-ent-pending], .claw-root able-post-preview[data-ent-pending] { height: 260px; }
@keyframes aclaw-ent-pending {
  0%   { background-position: 100% 50%; }
  100% { background-position: 0 50%; }
}
@media (prefers-reduced-motion: reduce) {
  .aclaw-messages [data-ent-pending], .claw-root [data-ent-pending] { animation: none; }
}

/* Content images (lesson photos + inline <img> from agent/lesson HTML) — cap so
   a full-resolution portrait/landscape never dominates the column or bleeds past
   it (a 1280×1968 photo was rendering at natural size). Small chrome
   (avatars/logos) sits far under these caps, so it's unaffected. */
.aclaw-messages img, .claw-root img {
  max-width: 100%;
  max-height: min(52vh, 440px);
  height: auto;
  object-fit: contain;
}
/* Bare <th> / <td> emitted in dark contexts without an .aclaw-table wrapper
   (e.g. sanitized agent output that loses the class). Inherit color and
   apply a subtle tinted header so text is always readable on dark surfaces. */
.aclaw-messages table, .aclaw-artifact-body table {
  width: 100%; border-collapse: collapse;
  margin: 10px 0; font-size: 13px;
  color: inherit;
}
.aclaw-messages table th, .aclaw-artifact-body table th {
  background: var(--ableclaw-table-header-bg);
  color: var(--ableclaw-table-header-fg);
  font-weight: 600;
  padding: 6px 10px;
  border: 1px solid var(--ableclaw-table-border);
  text-align: left;
}
.aclaw-messages table td, .aclaw-artifact-body table td {
  background: var(--ableclaw-table-row-bg);
  color: inherit;
  padding: 6px 10px;
  border: 1px solid var(--ableclaw-table-border);
  text-align: left;
}

.aclaw-attachments {
  display: flex; flex-wrap: wrap; gap: 6px;
  padding: 8px 16px 0 16px;
  background: var(--surface-subtle, rgba(0, 0, 0, 0.12));
}
.aclaw-attachments[hidden] { display: none !important; }
.aclaw-attachment {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 8px;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid var(--border-default, rgba(255, 255, 255, 0.10));
  border-radius: 14px;
  font-size: 12px; line-height: 1.2;
  max-width: 240px;
}
.aclaw-attachment.is-loading { opacity: 0.7; }
.aclaw-attachment.is-error {
  border-color: #b91c1c;
  background: rgba(185, 28, 28, 0.12);
}
.aclaw-attachment-icon { font-size: 12px; opacity: 0.85; }
.aclaw-attachment-name {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  max-width: 160px;
}
.aclaw-attachment-remove {
  background: transparent; color: inherit; border: 0;
  cursor: pointer; opacity: 0.6; font-size: 14px; line-height: 1;
  padding: 0 2px;
}
.aclaw-attachment-remove:hover { opacity: 1; }
.aclaw-attachment-retry {
  background: transparent; color: inherit; border: 0;
  cursor: pointer; opacity: 0.7; font-size: 11px; padding: 0 4px;
  text-decoration: underline;
}
.aclaw-attachment-retry:hover { opacity: 1; }

/* Bug #107 — image attachment chip becomes a real thumbnail card.
   Wraps tighter than the text-chip layout, lifts the filename to a
   bottom hover-only label, and gives the thumbnail click affordance. */
.aclaw-attachment.is-image {
  padding: 0;
  border-radius: 10px;
  overflow: hidden;
  border-color: var(--border-default, rgba(255, 255, 255, 0.18));
  position: relative;
  background: rgba(255, 255, 255, 0.03);
}
.aclaw-attachment-thumb {
  display: block;
  width: 56px; height: 56px;
  object-fit: cover;
  cursor: zoom-in;
  background: rgba(0, 0, 0, 0.15);
  transition: filter 120ms ease-out;
}
.aclaw-attachment-thumb:hover { filter: brightness(0.85); }
.aclaw-attachment.is-image .aclaw-attachment-name { display: none; }
.aclaw-attachment.is-image .aclaw-attachment-remove {
  position: absolute;
  top: 2px; right: 2px;
  width: 18px; height: 18px;
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  border-radius: 50%;
  font-size: 12px;
  display: inline-flex; align-items: center; justify-content: center;
  opacity: 0.9;
}
.aclaw-attachment.is-image .aclaw-attachment-remove:hover {
  background: rgba(220, 38, 38, 0.85);
}

/* Bug #108 — attachment block hanging below user message text. */
.aclaw-message-attachments {
  display: flex; flex-wrap: wrap; gap: 6px;
  margin-top: 8px;
  /* A media element is the LAST thing in the bubble, and a bare block child leaves the
     gap under it reading tighter than the one above — the inset looks like a mistake
     rather than a frame. Match the top so the attachment sits centred in its own space. */
  margin-bottom: 2px;
}
.aclaw-message-attachment-thumb {
  display: block;
  width: 200px; max-width: 100%;
  max-height: 240px;
  object-fit: cover;
  border-radius: 10px;
  cursor: zoom-in;
  border: 1px solid var(--border-default, rgba(255, 255, 255, 0.08));
  background: rgba(0, 0, 0, 0.15);
  transition: filter 120ms ease-out;
}
.aclaw-message-attachment-thumb:hover { filter: brightness(0.92); }
/* An image the AGENT delivered is the answer, not a receipt. The 200px thumb above is
   right for "what I sent" — a chip beside your own words — and wrong for a photo you
   asked to SEE: at that size a 1600x1200 shot is a link with extra steps, which is the
   complaint this whole lane exists to end. Sized like the video/audio element right
   below (bounded on both axes, "auto" on both dimensions so the picture lands on its
   OWN aspect — a portrait shot resolves against the height cap, a landscape one against
   the width) and contained rather than cropped, because cropping to a fixed box is what
   makes a photo look like a thumbnail even when it is large. */
.aclaw-message-attachment-thumb--delivered {
  width: auto; height: auto;
  max-width: min(100%, 420px);
  max-height: 440px;
  object-fit: contain;
}
/* Agent-produced video/audio (the attachMedia tool). Bounded on BOTH axes and sized by
   the clip's own aspect: a fixed width plus a height cap letterboxes a portrait clip
   (720x1280 from a phone or a social post is the common shape) into black bars, while an
   unbounded width lets it push the thread off screen. "auto" on both dimensions inside the
   two maxima lets the element land on its real aspect — portrait resolves against the
   height cap, landscape against the width. */
.aclaw-message-attachment-media {
  display: block;
  width: auto;
  height: auto;
  max-width: min(100%, 420px);
  max-height: 440px;
  border-radius: 10px;
  background: #000;
  outline: none;
}
audio.aclaw-message-attachment-media {
  width: min(100%, 320px);
  max-height: none;
  background: transparent;
}
.aclaw-message-attachment-file {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 6px 10px;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid var(--border-default, rgba(255, 255, 255, 0.10));
  border-radius: 14px;
  font-size: 13px; line-height: 1.2;
  max-width: 240px;
  color: inherit; text-decoration: none;
  transition: background 120ms;
}
.aclaw-message-attachment-file:hover {
  background: rgba(255, 255, 255, 0.10);
}
.aclaw-message-attachment-icon { font-size: 14px; opacity: 0.85; }
.aclaw-message-attachment-name {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  max-width: 200px;
}

/* Bug #109 — full-screen image lightbox. Backdrop click + close button
   + Esc all dismiss. Uses fixed positioning to escape blueprint scroll. */
.aclaw-lightbox {
  position: fixed; inset: 0;
  background: rgba(0, 0, 0, 0.85);
  z-index: 10000;
  display: flex; align-items: center; justify-content: center;
  padding: 32px;
  cursor: zoom-out;
  animation: aclaw-lightbox-fade 140ms ease-out;
}
.aclaw-lightbox[hidden] { display: none !important; }
.aclaw-lightbox-img {
  /* dvh, never vh — on iOS Safari 100vh does not shrink for the URL bar, so a
     tall image overflowed the visible viewport and its bottom edge was
     unreachable (29-template-mobile.md §4). */
  max-width: 100%; max-height: calc(100dvh - 120px);
  border-radius: 6px;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
  cursor: default;
}
.aclaw-lightbox-close {
  position: absolute; top: 16px; right: 20px;
  width: 36px; height: 36px;
  border-radius: 50%;
  border: 0;
  background: rgba(255, 255, 255, 0.15);
  color: #fff;
  font-size: 22px; line-height: 1;
  cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
}
.aclaw-lightbox-close:hover { background: rgba(255, 255, 255, 0.30); }
.aclaw-lightbox-close:focus-visible {
  outline: 2px solid #fff; outline-offset: 2px;
}
.aclaw-lightbox-caption {
  position: absolute; bottom: 24px; left: 50%;
  transform: translateX(-50%);
  color: rgba(255, 255, 255, 0.85);
  font-size: 13px;
  background: rgba(0, 0, 0, 0.45);
  padding: 6px 12px;
  border-radius: 12px;
  max-width: 60%;
  text-align: center;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
@keyframes aclaw-lightbox-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* Bug #112 — drag-over visual cue. The dropzone is the .aclaw-chat
   container; we set data-dragging="true" on it during a drag with files. */
.aclaw-chat[data-dragging="true"]::after {
  content: "Drop file to attach";
  position: absolute; inset: 0;
  background: rgba(99, 102, 241, 0.18);
  border: 2px dashed var(--ableclaw-accent, #6366f1);
  border-radius: 12px;
  display: flex; align-items: center; justify-content: center;
  color: var(--ableclaw-accent-text, #6366f1);
  font-size: 16px; font-weight: 600;
  pointer-events: none;
  z-index: 1000;
}

/* TWO-ROW composer: the textarea owns row 1, the control row is row 2 (attach
   + mode LEFT, model + mic + send RIGHT). A COLUMN flex container, so every
   direct child stacks; the control clusters are their own flex rows below. */
/* The composer size line (_noteComposerSize). Injected as the input bar's FIRST
   child so it sits above the typed text — a receipt reads before the thing it
   describes, and putting it under the control row would hide it behind the
   send button on a phone.

   It is deliberately quiet in the ordinary case (a muted line stating how much
   arrived) and loud only on a refusal, because the refusal is the only state
   where the user is holding text that did not go in. Semantic tokens throughout
   so it survives the dark-mode flip — an absolute grey here would be invisible
   in one of the two modes. */
.aclaw-paste-note {
  font-size: 11px;
  line-height: 1.45;
  padding: 6px 10px;
  border-radius: 10px;
  color: var(--ableclaw-text-muted, var(--text-muted, #6b7280));
  background: var(--ableclaw-surface-subtle, var(--surface-subtle, rgba(127,127,127,0.08)));
  border: 1px solid var(--ableclaw-border-soft, var(--border-subtle, rgba(127,127,127,0.18)));
  /* Long refusal text must wrap, never widen the composer. */
  overflow-wrap: anywhere;
}
.aclaw-paste-note--refused {
  color: var(--on-danger, #fff);
  background: var(--danger, #b91c1c);
  border-color: var(--danger-hover, #991b1b);
  font-weight: 500;
}

.aclaw-input-bar {
  /* gap 8 = the air between the typed line and the control row; with the
     bar's own inset (10 above, 8 below) the composer at rest is ONE text line
     over one control row — compact is the house density. */
  display: flex; flex-direction: column; align-items: stretch; gap: 8px;
  padding: 8px 10px;
  border-top: 1px solid var(--ableclaw-border-soft);
  background: var(--ableclaw-input-bar-bg);
  flex-shrink: 0;
  box-sizing: border-box;
  /* Encapsulate the row in a soft pill — gives the input a modern
     "command bar" feel rather than the dated chat-form look. */
  border-radius: 18px;
  margin: 10px 16px 0;
  border-top: 0;
  border: 1px solid var(--ableclaw-border-soft);
  background: var(--ableclaw-input-bg);
  transition: border-color 120ms, box-shadow 120ms;
}
/* The control row. It WRAPS as the last-resort safety valve: on a viewport too
   narrow for left+right side by side the right cluster drops to its own line
   (still right-aligned) instead of overflowing the composer. */
.aclaw-composer-row {
  display: flex; align-items: center; flex-wrap: wrap;
  gap: 6px; row-gap: 6px;
  width: 100%; min-width: 0;
}
.aclaw-composer-left {
  display: flex; align-items: center; gap: 6px;
  min-width: 0; flex: 1 1 auto;
}
.aclaw-composer-right {
  /* 8px, not the row's 6 — the model pill sits directly beside a round icon
     button and read as crowded at 6. */
  display: flex; align-items: center; gap: 8px;
  flex: 0 0 auto; margin-left: auto;
}
.aclaw-input-bar:focus-within {
  border-color: var(--ableclaw-accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--ableclaw-accent) 18%, transparent);
}
/* Page-provided quick actions: a chip row at the top of the composer (above the typed
   line), rendered from the host quick-actions attribute. Sends a prompt on tap. */
/* The composer stack occupies the input grid cell and stacks the quick row
   above the input bar; the chat is a grid, so this is what keeps the row from
   auto-placing into a stray cell. */
.aclaw-composer-stack { grid-area: input; display: flex; flex-direction: column; min-width: 0; }
.aclaw-quickrow { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; min-width: 0; margin: 6px 16px 0; }
@media (max-width: 640px) { .aclaw-quickrow { margin: 6px 8px 0; } }
.aclaw-quickrow-k {
  font-family: var(--font-mono, ui-monospace, monospace); font-size: 10px; font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase; color: var(--text-muted);
}
.aclaw-quickchip {
  min-height: 30px; padding: 0 12px; border-radius: 999px;
  border: 1px solid var(--ableclaw-border-soft, var(--border-default)); background: transparent;
  color: var(--text-secondary); font-family: inherit; font-size: 12px; font-weight: 600; cursor: pointer;
  transition: border-color 140ms ease, color 140ms ease, background-color 140ms ease;
}
.aclaw-quickchip:focus-visible { outline: 2px solid var(--ableclaw-accent, var(--accent)); outline-offset: 2px; }
@media (hover: hover) and (pointer: fine) {
  .aclaw-quickchip:hover { border-color: var(--ableclaw-accent, var(--accent)); color: var(--text-primary); background: color-mix(in srgb, var(--ableclaw-accent, var(--accent)) 8%, transparent); }
}
@media (prefers-reduced-motion: reduce) { .aclaw-quickchip { transition: none; } }
/* Entity-event pills in the stream: a render/refresh is a quiet mono refline; a
   human decision is a small toned pill (warning sent-back, success approved). */
.aclaw-message--event {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin: 2px 0; padding: 0 2px;
}
.aclaw-event-line {
  display: inline-flex; align-items: center; gap: 6px; min-width: 0;
  font-family: var(--font-mono, ui-monospace, monospace); font-size: 11px;
}
.aclaw-event-line .ic { font-size: 12px; flex: 0 0 auto; }
.aclaw-event-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The pinned card promotes to the dock; its leftover in-stream reference is
   noise beside the terse pill (and names a stale version), so drop it. */
.aclaw-message--event .aclaw-widgets { display: none; }
.aclaw-event-line--ref { color: var(--text-secondary, #52525b); }
.aclaw-event-line--ref .ic { color: var(--success, #157f3d); }
.aclaw-event-line--decision {
  padding: 3px 10px; border-radius: var(--radius-full, 999px); font-weight: 700;
  background: var(--warning-soft, color-mix(in oklab, var(--warning, #b45309) 14%, transparent));
  border: 1px solid color-mix(in oklab, var(--warning, #b45309) 40%, transparent);
  color: color-mix(in oklch, var(--warning, #b45309) 60%, var(--text-primary, #1d1c17));
}
.aclaw-event-line--decision.aclaw-event-line--ok {
  background: var(--success-soft, color-mix(in oklab, var(--success, #157f3d) 14%, transparent));
  border-color: color-mix(in oklab, var(--success, #157f3d) 40%, transparent);
  color: color-mix(in oklch, var(--success, #157f3d) 58%, var(--text-primary, #1d1c17));
}
.aclaw-event-line--decision .ic { color: inherit; }
.aclaw-input-footer {
  /* Bug #93 — opacity 0.55 on 11px text was below WCAG AA. Raised to
     0.78 which puts contrast close to 4.5:1 against typical bg, and
     pin a deliberate color instead of inheriting near-black. */
  display: flex; align-items: center; justify-content: space-between;
  padding: 4px 16px 8px 16px;
  background: var(--ableclaw-input-bar-bg);
  font-size: 11px; opacity: 0.78;
  color: var(--ableclaw-text-muted, #475569);
  flex-shrink: 0;
  box-sizing: border-box;
  /* STABILITY: the footer + the live status verb must NEVER move the input.
     Fixed height + a centered ABSOLUTE status (below) so the composer above
     stays put no matter what the agent is doing. */
  position: relative;
  min-height: 26px;
}
/* The live status floats centered, out of flow — its text can change every
   second ("thinking…" → "checking the data…" → "done") without nudging the
   pinned "Read-only" text or the ⌘↩ hint, so the input never shifts. */
.aclaw-input-footer .aclaw-status {
  position: absolute; left: 50%; transform: translateX(-50%);
  max-width: 60%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.aclaw-input-footer-shortcut {
  font-variant-numeric: tabular-nums;
}
.aclaw-upload {
  width: 32px; height: 32px; border-radius: 999px;
  background: transparent; color: inherit; border: 0;
  cursor: pointer; opacity: 0.55;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 14px;
  flex-shrink: 0;
  transition: opacity 120ms, background-color 120ms, transform 120ms;
}
.aclaw-upload:hover:not(:disabled) {
  opacity: 1;
  background: var(--ableclaw-sidebar-hover-bg, rgba(127, 127, 127, 0.10));
}
.aclaw-upload:disabled { cursor: not-allowed; opacity: 0.4; }
.aclaw-upload i { font-size: 14px; line-height: 1; }
.aclaw-input {
  /* Row 1 of the two-row composer: full width, its own line. The flex:none is
     load-bearing — the bar is a COLUMN container, so a flex:1 here would grow
     the textarea VERTICALLY to fill the composer instead of hugging its
     content the way the auto-grow handler sizes it.
     NOTE: no backticks anywhere in this CSS — it lives in a template literal. */
  flex: 0 0 auto;
  width: 100%;
  /* No vertical padding — the BAR's padding owns the composer's inset, so the
     two must not stack (that is what made the box top-heavy). The 10px on the
     sides puts the typed text's left edge on the same rail as the attach
     glyph below it (the glyph is centred in a 38px button, so it sits ~10px
     in; at 6px the placeholder started 6.3px to its left). */
  padding: 0 10px;
  background: transparent; color: var(--ableclaw-input-fg);
  border: 0;
  border-radius: 0;
  font: inherit; font-size: 14px; line-height: 1.35;
  resize: none;
  /* At rest the textarea is exactly its one row (rows="1" x line-height) — the
     bar's inset is the only air around the typed line; it grows by content. */
  min-height: 0; max-height: 160px;
  min-width: 0;
  box-sizing: border-box;
}
.aclaw-input:focus {
  outline: none;
}
.aclaw-input::placeholder {
  opacity: 0.55;
}
.aclaw-send {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 7px 14px;
  background: var(--ableclaw-accent); color: var(--ableclaw-accent-fg);
  border: 0; border-radius: 999px; cursor: pointer;
  font-size: 13px; font-weight: 600;
  transition: opacity 120ms, transform 120ms, box-shadow 120ms;
  flex-shrink: 0;
}
.aclaw-send:hover:not(:disabled) {
  opacity: 0.95;
  box-shadow: 0 2px 8px color-mix(in srgb, var(--ableclaw-accent) 30%, transparent);
}
.aclaw-send:active:not(:disabled) { transform: scale(0.97); }
.aclaw-send:disabled { opacity: 0.5; cursor: not-allowed; }
.aclaw-send i { font-size: 11px; }
.aclaw-stop {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 7px 14px;
  background: var(--ableclaw-stop-bg); color: var(--ableclaw-accent-fg);
  border: 0; border-radius: 999px; cursor: pointer;
  font-size: 13px; font-weight: 600; line-height: 1;
  transition: opacity 120ms, background-color 120ms;
  flex-shrink: 0;
}
.aclaw-stop:hover { opacity: 0.95; background: var(--ableclaw-stop-bg-hover); }
.aclaw-stop i { font-size: 11px; }
/* In-flight: hide Send, show Stop. */
.aclaw-input-bar--in-flight .aclaw-send { display: none; }
.aclaw-stop[hidden] { display: none !important; }
.aclaw-input-bar--in-flight .aclaw-stop { display: inline-flex; align-items: center; }

/* Drag-and-drop overlay — covers the chat surface with a soft frosted
   panel + drop-target card when files are being dragged in. Hidden by
   default; .aclaw-chat[data-dragging="true"] flips visibility. */
.aclaw-dropzone {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  pointer-events: none;
  opacity: 0;
  background: color-mix(in srgb, var(--ableclaw-bg) 70%, transparent);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  z-index: 60;
  transition: opacity 140ms;
}
.aclaw-chat[data-dragging="true"] .aclaw-dropzone {
  opacity: 1;
  pointer-events: auto;
}
.aclaw-dropzone-card {
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  padding: 32px 40px;
  border-radius: 18px;
  background: var(--ableclaw-bg);
  border: 2px dashed var(--ableclaw-accent);
  color: var(--ableclaw-fg);
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.18);
  pointer-events: none;
  text-align: center;
}
.aclaw-dropzone-icon {
  font-size: 36px;
  color: var(--ableclaw-accent-text);
  margin-bottom: 2px;
}
.aclaw-dropzone-title {
  font-size: 16px; font-weight: 700;
  letter-spacing: 0.01em;
}
.aclaw-dropzone-sub {
  font-size: 12px; opacity: 0.6;
  letter-spacing: 0.04em;
}

.aclaw-artifact {
  display: flex; flex-direction: column;
  min-width: 0; min-height: 0;
  border-left: 1px solid var(--ableclaw-border-soft);
  background: var(--ableclaw-artifact-bg);
  color: var(--ableclaw-fg);
  overflow: hidden;
}
.aclaw-artifact[hidden] { display: none !important; }
.aclaw-artifact-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: 10px 14px;
  border-bottom: 1px solid var(--ableclaw-border-soft);
  font-size: 13px; font-weight: 600;
  flex-shrink: 0;
}
.aclaw-artifact-type { opacity: 0.85; }
.aclaw-artifact-actions {
  display: inline-flex; align-items: center; gap: 4px;
}
.aclaw-artifact-close, .aclaw-artifact-clear {
  background: transparent; color: inherit; border: 0; cursor: pointer;
  font-size: 16px; line-height: 1; opacity: 0.7;
  padding: 2px 6px; border-radius: 4px;
  transition: opacity 120ms, background-color 120ms;
}
.aclaw-artifact-close:hover, .aclaw-artifact-clear:hover {
  opacity: 1; background: rgba(255, 255, 255, 0.06);
}
.aclaw-artifact-body {
  flex: 1; min-height: 0;
  overflow-y: auto;
  padding: 14px;
  font-size: 13px; line-height: 1.5;
}

/* Brand mark in the Agent Thinking header (purple rounded square w/ white
   brain icon). Matches legacy <askAble> visual where the brain logo sits
   next to the title. */
.aclaw-artifact-brand {
  width: 24px; height: 24px;
  border-radius: 6px;
  background: linear-gradient(135deg, #7c3aed 0%, #6366f1 100%);
  color: #ffffff;
  display: inline-flex; align-items: center; justify-content: center;
  margin-right: 8px;
  flex-shrink: 0;
  box-shadow: 0 1px 3px rgba(124, 58, 237, 0.25);
}
.aclaw-artifact-brand i { font-size: 12px; }
.aclaw-artifact-header > .aclaw-artifact-type {
  flex: 1; min-width: 0;
}

/* Subtle footer hint at the bottom of the panel — matches the legacy
   "Agent reasoning log" placeholder strip. */
.aclaw-artifact-footer {
  flex-shrink: 0;
  padding: 10px 14px;
  border-top: 1px solid var(--ableclaw-border-soft);
  font-size: 12px;
  color: var(--grey-500, #94a3b8);
  font-style: italic;
}

/* Brain TOGGLE button — always-visible floating control in the top-right
   of .aclaw-chat. Clicking it shows/hides the Agent Thinking panel.
   Closed state: white background with purple brain. Open state (host
   carries data-thinking-open): purple background with white brain.
   Matches the visual swap in legacy askAble image 1 -> image 2. */
.aclaw-thinking-toggle {
  position: absolute;
  top: 12px; right: 12px;
  z-index: 25;
  width: 36px; height: 36px;
  border-radius: 10px;
  border: 1px solid var(--ableclaw-border-soft);
  background: var(--ableclaw-bg, #ffffff);
  color: #7c3aed;
  display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer;
  transition: background-color 160ms ease, color 160ms ease,
              transform 120ms ease, box-shadow 160ms ease;
  box-shadow: 0 1px 3px rgba(15, 17, 23, 0.06);
}
/* Active-conversation copy chip — relocated OUT of the chat chrome
   (where it floated top-right and overlapped message content, always on)
   and INTO the sidebar header (the conversation-selection menu, below the
   search). It now appears ONLY when that menu is displayed — on desktop the
   sidebar is always present; on mobile the sidebar is a slide-in drawer, so
   the chip is hidden until the user opens conversation history. In normal
   flow, full-width, shown only when an active conversation exists (.hidden
   attr toggled via _setActiveConvChip). Click copies the full UUID; the
   visible id shows the first 8 chars so users can verify which conversation. */
.aclaw-active-conv-chip {
  margin-top: 2px;
  width: 100%;
  min-height: 30px;
  padding: 6px 10px;
  border-radius: 8px;
  border: 1px solid var(--ableclaw-border-soft);
  background: var(--ableclaw-sidebar-hover-bg, rgba(127, 127, 127, 0.10));
  color: var(--ableclaw-sidebar-fg, var(--text-primary, #1e293b));
  display: flex; align-items: center; gap: 6px;
  font-size: 11px;
  font-family: var(--font-mono, ui-monospace, monospace);
  cursor: pointer;
  text-align: left;
  transition: background-color 160ms ease, transform 120ms ease,
              box-shadow 160ms ease, opacity 160ms ease;
  box-shadow: 0 1px 2px rgba(15, 17, 23, 0.04);
}
.aclaw-active-conv-chip[hidden] { display: none; }
.aclaw-active-conv-chip:hover {
  background: color-mix(in srgb, #7c3aed 8%, var(--ableclaw-bg, #ffffff));
  transform: translateY(-1px);
  box-shadow: 0 4px 10px rgba(124, 58, 237, 0.18);
}
.aclaw-active-conv-chip:active { transform: translateY(0); }
.aclaw-active-conv-chip.is-copied {
  color: var(--success, #16a34a);
  border-color: var(--success, #16a34a);
  background: color-mix(in srgb, var(--success, #16a34a) 10%, var(--ableclaw-bg, #ffffff));
}
.aclaw-active-conv-chip-label {
  /* Bug #93 — opacity 0.65 on 10px text dipped under WCAG AA.
     Raised to 0.85 and made the color deliberate. */
  opacity: 0.85;
  color: var(--ableclaw-text-muted, #475569);
  font-family: var(--font-sans, system-ui, sans-serif);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.aclaw-active-conv-chip-id { font-weight: 600; }
.aclaw-active-conv-chip-glyph { opacity: 0.6; font-size: 12px; margin-left: auto; }
.aclaw-thinking-toggle:hover {
  background: color-mix(in srgb, #7c3aed 8%, var(--ableclaw-bg, #ffffff));
  transform: translateY(-1px);
  box-shadow: 0 4px 10px rgba(124, 58, 237, 0.18);
}
.aclaw-thinking-toggle:active { transform: translateY(0); }
.aclaw-thinking-toggle i { font-size: 16px; }
able-claw[data-thinking-open="true"] .aclaw-thinking-toggle {
  background: linear-gradient(135deg, #7c3aed 0%, #6366f1 100%);
  color: #ffffff;
  border-color: transparent;
  box-shadow: 0 4px 12px rgba(124, 58, 237, 0.32);
}

/* Unread-activity dot — small accent on the brain button when tool / reasoning
   / lifecycle events have landed since the panel was last closed. Reset when
   the user opens the panel (see _openArtifactPane). The dot mirrors the
   notification-bell pattern used elsewhere in the platform. */
.aclaw-thinking-toggle { position: absolute; transition: transform 220ms cubic-bezier(0.22,1,0.36,1), opacity 220ms ease; }
/* While the stream is actively scrolling, the toggle tucks off the right edge
   so it never covers bubbles or steals taps mid-navigation; it returns at rest. */
able-claw[data-msgs-scrolling="true"] .aclaw-thinking-toggle {
  transform: translateX(calc(100% + 16px));
  opacity: 0.4;
  pointer-events: none;
}
.aclaw-thinking-toggle::after {
  content: '';
  position: absolute;
  top: 4px; right: 4px;
  width: 8px; height: 8px;
  border-radius: 50%;
  background: #f59e0b;
  border: 2px solid var(--ableclaw-bg, #ffffff);
  opacity: 0;
  transform: scale(0.5);
  transition: opacity 160ms ease, transform 160ms ease;
}
able-claw[data-thinking-unread="true"] .aclaw-thinking-toggle::after {
  opacity: 1;
  transform: scale(1);
}
/* Hide the unread dot when the panel is open — opening clears the marker
   but this rule prevents a flash mid-transition. */
able-claw[data-thinking-open="true"] .aclaw-thinking-toggle::after {
  opacity: 0 !important;
}

/* When toggled OPEN in helper / launcher modes (where the artifact pane is
   normally hidden because the grid only has 1 column), surface the pane
   as a right-edge floating slide-in overlay. Full mode keeps the existing
   3-col grid layout — the pane is just an additional column there. */
able-claw[data-mode="helper"][data-thinking-open="true"] > .aclaw-grid > .aclaw-artifact,
able-claw[data-mode="launcher"][data-thinking-open="true"] > .aclaw-grid > .aclaw-artifact {
  display: flex !important;
  position: absolute;
  top: 0; right: 0; bottom: 0;
  width: min(360px, 92%);
  z-index: 30;
  border-left: 1px solid var(--ableclaw-border-soft);
  box-shadow: -8px 0 24px rgba(15, 17, 23, 0.18);
  background: var(--ableclaw-artifact-bg, var(--ableclaw-bg, #ffffff));
  animation: aclaw-thinking-slide-in 200ms ease-out;
}
@keyframes aclaw-thinking-slide-in {
  from { transform: translateX(8px); opacity: 0; }
  to   { transform: translateX(0);   opacity: 1; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   PREFERENCES panel — the standing rules the agent follows, per user.

   Reads /able-api/ai/preferences, which is SELF-SCOPED with no cross-user lane
   (able_ai/surfaces/preferences.py). There is no "user" param to send and none
   to add: this is a personal surface, and its predecessor became a leak the one
   time a bare list could widen with the caller's role.
   (No backticks in this block, ever — JS ends the CSS template literal at the
   first one while the CSS extractor reads straight past it, so the file can look
   fine and still take every blueprint on every page down at runtime.)

   Unlike the retired memories pane, the user WRITES here — an agent saving a
   rule and a person writing one land in the same table through the same store,
   so this panel carries Add, an active switch, and the budget the writes are
   charged against.

   Mechanism mirrors the thinking panel: a host attribute (data-prefs-open) + one
   CSS block reveals the pane in all three modes. The pane is its own absolutely
   positioned element rather than a borrowed .aclaw-artifact, so it never fights
   _ensureWorkspace for that node — and being position:absolute it does NOT
   participate in the 3-track grid (no implicit 4th column).
   ═══════════════════════════════════════════════════════════════════════════ */

/* The sliders button — beside the brain in the chat-column cluster. The brain
   keeps right:12px (learned position, unchanged); this sits to its left.
   The FLOATING geometry is scoped to the chat-column copy: helper/launcher
   render a second copy inside .aclaw-helper-actions, which must stay an
   in-flow .aclaw-helper-action button.

   ⚠ These rules are what keep the SSR'd button a 36px floating control. When
   the user-memory retirement deleted the equivalent block but left the SSR
   emitting the button, it fell back to UA defaults — display:block inside a
   grid parent — and painted as a full-width grey bar above the composer. */
.aclaw-chat > .aclaw-prefs-toggle {
  position: absolute;
  top: 12px; right: 56px;
  z-index: 25;
  width: 36px; height: 36px;
  border-radius: 10px;
  border: 1px solid var(--ableclaw-border-soft);
  background: var(--ableclaw-bg, #ffffff);
  color: #7c3aed;
  display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer;
  transition: background-color 160ms ease, color 160ms ease,
              transform 120ms ease, box-shadow 160ms ease, opacity 220ms ease;
  box-shadow: 0 1px 3px rgba(15, 17, 23, 0.06);
}
.aclaw-chat > .aclaw-prefs-toggle:hover {
  background: color-mix(in srgb, #7c3aed 8%, var(--ableclaw-bg, #ffffff));
  transform: translateY(-1px);
  box-shadow: 0 4px 10px rgba(124, 58, 237, 0.18);
}
.aclaw-chat > .aclaw-prefs-toggle:active { transform: translateY(0); }
.aclaw-chat > .aclaw-prefs-toggle i { font-size: 15px; }
/* Open-state fill applies to BOTH copies so the feedback is identical in every
   mode (specificity 0,2,1 beats .aclaw-helper-action's 0,1,0). */
able-claw[data-prefs-open="true"] .aclaw-prefs-toggle {
  background: linear-gradient(135deg, #7c3aed 0%, #6366f1 100%);
  color: #ffffff;
  border-color: transparent;
  opacity: 1;
  box-shadow: 0 4px 12px rgba(124, 58, 237, 0.32);
}
/* Travels with the brain when the stream is scrolling (chat copy only). */
able-claw[data-msgs-scrolling="true"] .aclaw-chat > .aclaw-prefs-toggle {
  transform: translateX(calc(100% + 60px));
  opacity: 0.4;
  pointer-events: none;
}
/* helper / launcher put the control in the header action row instead — hide the
   floating one so there is exactly ONE per mode. The header copy carries the
   same class, so the delegated click handler serves both without re-wiring. */
able-claw[data-mode="helper"] .aclaw-chat > .aclaw-prefs-toggle,
able-claw[data-mode="launcher"] .aclaw-chat > .aclaw-prefs-toggle {
  display: none;
}

/* The pane. Absolute in EVERY mode (unlike the thinking pane, a 3rd grid column
   in full mode) — it is a transient curation surface, not a side-by-side
   reading surface, and staying absolute keeps the grid untouched. */
.aclaw-prefs {
  display: none;
  position: absolute;
  top: 0; right: 0; bottom: 0;
  width: min(400px, 92%);
  z-index: 40;
  flex-direction: column;
  min-width: 0; min-height: 0;
  overflow: hidden;
  border-left: 1px solid var(--ableclaw-border-soft);
  box-shadow: -8px 0 24px rgba(15, 17, 23, 0.18);
  /* OPAQUE — the pane sits ON TOP of the chat, so it must fully cover the
     message stream behind it (--ableclaw-artifact-bg is translucent). */
  background: var(--ableclaw-bg, #ffffff);
  color: var(--ableclaw-fg);
}
able-claw[data-prefs-open="true"] > .aclaw-grid > .aclaw-prefs {
  display: flex;
  animation: aclaw-thinking-slide-in 200ms ease-out;
}
/* The dialog takes focus on open (see _openPrefs) purely to move the keyboard
   and the screen reader into it — it is not an interactive control, so it does
   not draw a ring. Every control INSIDE it keeps the theme's focus style. */
.aclaw-prefs:focus { outline: none; }

.aclaw-pref-header {
  display: flex; align-items: center; gap: 8px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--ableclaw-border-soft);
  font-size: 13px; font-weight: 600;
  flex-shrink: 0;
}
.aclaw-pref-brand {
  width: 24px; height: 24px;
  border-radius: 6px;
  background: linear-gradient(135deg, #7c3aed 0%, #6366f1 100%);
  color: #ffffff;
  display: inline-flex; align-items: center; justify-content: center;
  flex-shrink: 0;
  box-shadow: 0 1px 3px rgba(124, 58, 237, 0.25);
}
.aclaw-pref-brand i { font-size: 12px; }
.aclaw-pref-title { flex: 1; min-width: 0; }
.aclaw-pref-close {
  background: transparent; color: inherit; border: 0; cursor: pointer;
  font-size: 16px; line-height: 1; opacity: 0.7;
  min-width: 30px; min-height: 30px;
  display: inline-flex; align-items: center; justify-content: center;
  padding: 4px; border-radius: 6px;
  transition: opacity 120ms, background-color 120ms;
}
.aclaw-pref-close:hover {
  opacity: 1; background: var(--ableclaw-sidebar-hover-bg, rgba(127, 127, 127, 0.10));
}

/* The intro strip — what this list IS, plus the budget the writes spend. */
.aclaw-pref-intro {
  padding: 10px 14px 0;
  flex-shrink: 0;
  display: flex; flex-direction: column; gap: 8px;
}
.aclaw-pref-lede {
  margin: 0;
  font-size: 12px; line-height: 1.45;
  color: var(--ableclaw-text-muted, var(--text-secondary, #64748b));
}
/* The budget reads as ONE object — a bordered strip holding its caption, its
   number, its bar and (only when it applies) its instruction. As four loose
   lines under the lede, the bar looked like a stray rule and the sentence like
   a caption belonging to the lede above it. */
.aclaw-pref-meter {
  display: flex; flex-direction: column; gap: 6px;
  padding: 8px 10px;
  border-radius: var(--ableclaw-radius-sm, 8px);
  border: 1px solid var(--ableclaw-border-soft);
  background: color-mix(in srgb, var(--ableclaw-fg) 4%, transparent);
}
/* Same [hidden] trap as .aclaw-pref-add: _renderPrefsMeter hides this when the
   server sends no budget, and display:flex on the class would have kept an empty
   strip on screen regardless. */
.aclaw-pref-meter[hidden] { display: none; }
.aclaw-pref-meter-top {
  display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
}
.aclaw-pref-meter-cap {
  font-size: 10px; font-weight: 700;
  letter-spacing: 0.06em; text-transform: uppercase;
  color: color-mix(in srgb, var(--ableclaw-fg) 55%, transparent);
}
.aclaw-pref-meter-note {
  font-size: 11px; line-height: 1.4;
  color: color-mix(in srgb, #b45309 55%, var(--ableclaw-fg));
}
.aclaw-pref-meter-note[hidden] { display: none; }
.aclaw-pref-meter-track {
  height: 4px; border-radius: 999px;
  /* Blended toward the FOREGROUND, not tinted with a surface token: the obvious
     choice here (--ableclaw-sidebar-hover-bg) resolves to a 40%-opaque NEAR-WHITE
     in the shipped theme, so on this near-white pane the track vanished and a
     low-usage fill read as a stray dot rather than the left end of a bar. fg is
     dark on light and light on dark by definition, so this is visible in both. */
  background: color-mix(in srgb, var(--ableclaw-fg) 14%, transparent);
  overflow: hidden;
}
.aclaw-pref-meter-fill {
  height: 100%; width: 0;
  border-radius: 999px;
  background: linear-gradient(90deg, #7c3aed 0%, #6366f1 100%);
  transition: width 220ms ease, background-color 220ms ease;
}
/* Past 80% the store stops reporting a number and starts INSTRUCTING the agent
   to consolidate; the meter changes colour at the same threshold so the person
   and the agent are looking at the same signal. */
.aclaw-pref-meter.is-tight .aclaw-pref-meter-fill {
  background: linear-gradient(90deg, #b45309 0%, #d97706 100%);
}
.aclaw-pref-meter-label {
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  color: var(--ableclaw-text-muted, var(--text-secondary, #64748b));
}
.aclaw-pref-meter.is-tight .aclaw-pref-meter-label {
  color: color-mix(in srgb, #b45309 55%, var(--ableclaw-fg));
}

.aclaw-pref-body {
  flex: 1; min-height: 0;
  overflow-y: auto;
  padding: 12px 14px;
  display: flex; flex-direction: column; gap: 8px;
  /* macOS overlay scrollbars take zero layout width and fade at rest, so a
     scrollable list shows NO affordance that more rows exist. Same treatment
     the conversation list uses: a thin always-present thumb. */
  scrollbar-width: thin;
  scrollbar-color: var(--ableclaw-scrollbar-thumb, rgba(120, 120, 130, 0.32)) transparent;
}
.aclaw-pref-body::-webkit-scrollbar { width: 8px; background: transparent; }
.aclaw-pref-body::-webkit-scrollbar-track { background: transparent; }
.aclaw-pref-body::-webkit-scrollbar-thumb {
  background: var(--ableclaw-scrollbar-thumb, rgba(120, 120, 130, 0.30));
  border-radius: 4px;
  border: 2px solid transparent;
  background-clip: padding-box;
  transition: background-color 140ms ease;
}
.aclaw-pref-body::-webkit-scrollbar-thumb:hover {
  background: var(--ableclaw-scrollbar-thumb-active, rgba(120, 120, 130, 0.6));
  background-clip: padding-box;
}

.aclaw-pref-footer {
  padding: 10px 14px 12px;
  /* The pane is bottom:0 pinned, so on a phone this line is the thing the home
     indicator sits over. max() keeps the padding everywhere the inset is 0. */
  padding-bottom: max(12px, env(safe-area-inset-bottom, 0px));
  flex-shrink: 0;
  /* Column, not a row: the count runs to ~40 characters and the link to ~30, so
     side by side they wrap unpredictably at 400px and not at all on a phone
     where the pane goes full width. Stacked, both are always whole. */
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 1px;
  font-size: 11px;
  color: var(--ableclaw-text-muted, var(--text-secondary, #94a3b8));
  border-top: 1px solid var(--ableclaw-border-soft);
}
/* The way out of the pane. Sized and coloured as .aclaw-pref-more, which is the
   pane's own solved version of this problem: the raw accent sits too close to
   the background in dark themes, so it is blended toward the foreground, which
   is light on dark and dark on light by definition. */
.aclaw-pref-foot-link {
  align-self: flex-start;
  padding: 5px 0;
  font-size: 11px; font-weight: 600;
  color: color-mix(in srgb, var(--ableclaw-accent-text, #6366f1) 45%, var(--ableclaw-fg));
  text-decoration: underline;
  text-underline-offset: 2px;
}
.aclaw-pref-foot-link:hover { text-decoration-thickness: 2px; }

/* One preference. THE RULE LEADS: it is the only thing being scanned, so it
   holds the top-left at reading scale and the metadata drops to a quiet caption
   band underneath. The content is the elastic element; the chip + date are
   intrinsic-width and drop to caption scale so they never squeeze it.

   The left rail is 3px of state, not a glyph column: accent while the rule is in
   force, gone when it is paused, so a scan down the list separates the two
   without reading a word. */
.aclaw-pref-row {
  border: 1px solid var(--ableclaw-border-soft);
  border-left: 3px solid color-mix(in srgb, #7c3aed 55%, transparent);
  border-radius: var(--ableclaw-radius-sm, 8px);
  background: var(--ableclaw-bubble-agent-bg, rgba(127, 127, 127, 0.06));
  padding: 10px 12px;
  display: flex; flex-direction: column; gap: 8px;
  transition: border-color 140ms ease, background-color 140ms ease;
}
.aclaw-pref-row:hover {
  background: color-mix(in srgb, var(--ableclaw-fg) 5%,
                        var(--ableclaw-bubble-agent-bg, rgba(127, 127, 127, 0.06)));
}
/* A suspended rule still reads, but must not look like it is in force. Fading
   the CARD faded its own switch with it — the one control that turns it back on
   was the hardest thing in the pane to see. The text dims; the controls do not. */
.aclaw-pref-row.is-off {
  border-left-color: color-mix(in srgb, var(--ableclaw-fg) 22%, transparent);
  background: transparent;
}
.aclaw-pref-row.is-off .aclaw-pref-content { opacity: 0.55; }
.aclaw-pref-row.is-off .aclaw-pref-date { opacity: 0.7; }
/* The caption band: date, category, state — then the controls, pinned right. */
.aclaw-pref-row-foot {
  display: flex; align-items: center; gap: 8px;
  min-width: 0;
  padding-top: 2px;
  border-top: 1px solid color-mix(in srgb, var(--ableclaw-fg) 8%, transparent);
}
.aclaw-pref-chip {
  flex-shrink: 0;
  font-size: 10px; font-weight: 700;
  letter-spacing: 0.04em; text-transform: uppercase;
  padding: 3px 7px; border-radius: 999px;
  border: 1px solid transparent;
  /* The category is FREE TEXT (no vocabulary, no DB check), so there is no hue
     map to key on — one tint, blended toward the foreground so the 10px
     uppercase label clears 4.5:1 on a light card and a dark one alike. */
  background: rgba(124, 58, 237, 0.16);
  color: color-mix(in srgb, #7c3aed 50%, var(--ableclaw-fg));
  flex: 0 1 auto; max-width: 45%;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Paused is a STATE, not a suffix on the date. As "2d ago · paused" it read as
   part of the timestamp; as its own chip it sits where every other state chip in
   the chrome sits, and it keeps its contrast while the rule's text dims. */
.aclaw-pref-chip--off {
  background: color-mix(in srgb, var(--ableclaw-fg) 12%, transparent);
  color: color-mix(in srgb, var(--ableclaw-fg) 70%, transparent);
}
.aclaw-pref-date {
  flex: 0 1 auto; min-width: 0;
  font-size: 11px;
  color: var(--ableclaw-text-muted, var(--text-secondary, #64748b));
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Edit sits next to DELETE, which has no undo. 2px between two 44px touch
   targets is a misclick waiting to happen, so the gap widens with the targets
   on coarse pointers (see the @media block below). */
/* margin-left:auto absorbs the slack, so the actions pin right whether or not
   the row carries a category — no element on this band has to be the elastic
   one, and the date keeps a fixed left edge down the list. */
.aclaw-pref-row-actions { display: inline-flex; gap: 4px; flex-shrink: 0; margin-left: auto; }
.aclaw-pref-act {
  background: transparent; color: inherit; border: 0; cursor: pointer;
  font-size: 12px; line-height: 1; opacity: 0.6;
  min-width: 28px; min-height: 28px;
  display: inline-flex; align-items: center; justify-content: center;
  padding: 4px; border-radius: 6px;
  transition: opacity 120ms, background-color 120ms, color 120ms;
}
.aclaw-pref-act:hover {
  opacity: 1; background: var(--ableclaw-sidebar-hover-bg, rgba(127, 127, 127, 0.12));
}
.aclaw-pref-act--del:hover { color: var(--danger, #b91c1c); }
.aclaw-pref-act[aria-pressed="true"] { opacity: 0.95; color: #16a34a; }

.aclaw-pref-content {
  margin: 0;
  font-size: 13px; line-height: 1.5;
  color: var(--ableclaw-fg);
  white-space: pre-wrap; word-break: break-word;
  /* Truncated to 3 lines; the row's expand button lifts the clamp. */
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
  overflow: hidden;
}
.aclaw-pref-row.is-expanded .aclaw-pref-content {
  -webkit-line-clamp: unset; display: block; overflow: visible;
}
.aclaw-pref-more {
  align-self: flex-start;
  background: transparent; border: 0; cursor: pointer;
  padding: 2px 0;
  font-size: 11px; font-weight: 600;
  /* The raw accent is a mid-blue that sits too close to the pane background in
     dark themes. Blending it toward the FOREGROUND keeps the accent hue while
     guaranteeing contrast in BOTH modes — fg is light on dark and dark on light
     by definition. */
  color: color-mix(in srgb, var(--ableclaw-accent-text, #6366f1) 45%, var(--ableclaw-fg));
  text-decoration: underline;
  text-underline-offset: 2px;
}
.aclaw-pref-more:hover { color: var(--ableclaw-fg); }

/* ── The composer (Add) and the in-place editor — one markup, one style block.
   It is a COMPOSED CARD, not a stack of naked inputs: a titled head, labelled
   fields that keep their guidance after the first keystroke, starters that fill
   the box, a counter against the server's own cap, and an action row where the
   primary carries its weight. */
.aclaw-pref-form-head {
  display: flex; align-items: center; gap: 8px;
  font-size: 12px; font-weight: 700;
  letter-spacing: 0.02em;
}
.aclaw-pref-form-badge {
  width: 22px; height: 22px; flex-shrink: 0;
  border-radius: 6px;
  background: linear-gradient(135deg, #7c3aed 0%, #6366f1 100%);
  color: #ffffff;
  display: inline-flex; align-items: center; justify-content: center;
  box-shadow: 0 1px 3px rgba(124, 58, 237, 0.28);
}
.aclaw-pref-form-badge i { font-size: 10px; }
.aclaw-pref-form-title { min-width: 0; }

/* A field is label + hint + control, so the guidance survives the first
   keystroke — a placeholder that carried both disappeared exactly when the
   person started needing it. The label WRAPS its control (no id), so two open
   forms can never collide. */
.aclaw-pref-field {
  display: flex; flex-direction: column; gap: 4px;
  min-width: 0;
}
/* 11px uppercase, not 10: a label the reader has to lean in for is not doing the
   job the label exists for, and this panel already spends its small sizes on the
   caption band under each row. */
.aclaw-pref-field-label {
  font-size: 11px; font-weight: 700;
  letter-spacing: 0.05em; text-transform: uppercase;
  color: color-mix(in srgb, var(--ableclaw-fg) 68%, transparent);
  display: flex; align-items: center; gap: 6px;
}
.aclaw-pref-field-opt {
  font-size: 10px; font-weight: 600; letter-spacing: 0.03em;
  text-transform: none;
  padding: 1px 6px; border-radius: 999px;
  background: color-mix(in srgb, var(--ableclaw-fg) 10%, transparent);
  color: color-mix(in srgb, var(--ableclaw-fg) 60%, transparent);
}
.aclaw-pref-field-hint {
  font-size: 12px; line-height: 1.5;
  color: var(--ableclaw-text-muted, var(--text-secondary, #64748b));
}
.aclaw-pref-edit-area {
  width: 100%; box-sizing: border-box;
  min-height: 88px; resize: vertical;
  margin-top: 2px;
  padding: 9px 11px;
  border-radius: var(--ableclaw-radius-sm, 8px);
  border: 1px solid var(--ableclaw-input-border);
  background: var(--ableclaw-input-bg);
  color: var(--ableclaw-input-fg);
  /* 13px is the panel's own body scale (hint 12, confirm-text 13). This USED to
     read max(16px, 13px) reaching for the iOS anti-zoom floor, but max() of two
     CONSTANTS is a constant — it resolved to 16px on every viewport, desktop
     included, so the two form controls rendered larger than everything around
     them (measured 16px against an 11-13px panel and a 15px composer). The
     floor belongs in a touch-only query, where it now lives (pointer: coarse). */
  font-size: 13px; line-height: 1.55;
  font-family: inherit;
  transition: border-color 140ms ease, box-shadow 140ms ease;
}
/* Chrome's UA placeholder is #757575 — a FIXED grey that measured byte-identical
   in light and dark, i.e. it never re-binds with the theme. That is the same trap
   the composer hit (see .aclaw-input-bar .aclaw-input::placeholder, fixed at a
   measured 1.88:1); --text-muted is semantic, so it follows the theme. opacity:1
   because stacking alpha on an already-muted ink is what made the composer fail. */
.aclaw-pref-edit-area::placeholder, .aclaw-pref-cat::placeholder {
  color: var(--text-muted, #94a3b8);
  opacity: 1;
}
.aclaw-pref-edit-area:hover, .aclaw-pref-cat:hover {
  border-color: color-mix(in srgb, var(--ableclaw-fg) 28%, transparent);
}
.aclaw-pref-edit-area:focus, .aclaw-pref-cat:focus {
  outline: 2px solid var(--ableclaw-accent, #6366f1);
  outline-offset: 1px;
  border-color: transparent;
}
/* Starters on the left, the counter on the right — ONE row that always exists,
   so nothing under the cursor moves when the counter changes. */
.aclaw-pref-under {
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px; flex-wrap: wrap;
  min-height: 22px;
  margin-top: -2px;
}
.aclaw-pref-examples {
  display: inline-flex; align-items: center; gap: 5px; flex-wrap: wrap;
  min-width: 0;
}
/* line-height:1 so the small-caps label centres on the chips' optical axis
   instead of riding low on its own leading. */
.aclaw-pref-examples-label {
  font-size: 11px; font-weight: 700; line-height: 1;
  letter-spacing: 0.05em; text-transform: uppercase;
  color: color-mix(in srgb, var(--ableclaw-fg) 52%, transparent);
}
.aclaw-pref-ex {
  font: inherit;
  font-size: 11px; font-weight: 600;
  padding: 3px 9px; border-radius: 999px;
  cursor: pointer;
  border: 1px solid color-mix(in srgb, #7c3aed 32%, transparent);
  background: color-mix(in srgb, #7c3aed 8%, transparent);
  color: color-mix(in srgb, #7c3aed 50%, var(--ableclaw-fg));
  transition: background-color 120ms ease, border-color 120ms ease;
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.aclaw-pref-ex:hover {
  background: color-mix(in srgb, #7c3aed 18%, transparent);
  border-color: color-mix(in srgb, #7c3aed 55%, transparent);
}
.aclaw-pref-count {
  font-size: 10px;
  font-variant-numeric: tabular-nums;
  color: color-mix(in srgb, var(--ableclaw-fg) 45%, transparent);
  margin-left: auto; flex-shrink: 0;
  transition: color 160ms ease;
}
/* Blended toward the FOREGROUND, like the meter's own tight label: raw #b45309 is
   a dark amber that all but disappears on a dark pane, and fg is light on dark
   and dark on light by definition. */
.aclaw-pref-count.is-tight {
  color: color-mix(in srgb, #b45309 55%, var(--ableclaw-fg));
  font-weight: 700;
}
.aclaw-pref-cat {
  width: 100%; box-sizing: border-box;
  margin-top: 2px;
  padding: 8px 10px;
  border-radius: var(--ableclaw-radius-sm, 8px);
  border: 1px solid var(--ableclaw-input-border);
  background: var(--ableclaw-input-bg);
  color: var(--ableclaw-input-fg);
  /* 13px — the SAME as the rule textarea. Same constant-max story as
     .aclaw-pref-edit-area above, but landing on one size rather than two: a
     single step (13 vs 12) between two peer inputs in one form is too small to
     read as hierarchy and just big enough to read as an accident. The touch
     rule already collapses both to 16px, so matching here makes the two fields
     one rail on every pointer. Label (11) and hint (12) still separate cleanly. */
  font-size: 13px; font-family: inherit;
  transition: border-color 140ms ease;
}
/* The person's OWN labels, offered as chips. Re-using one is what makes a list
   groupable, and a chip is faster and less error-prone than retyping it. */
/* Sits UNDER the category input as a row of its own, on the same left edge, with
   a real gap. Tucked up against the field it read as a stray token rather than a
   set of choices belonging to the box above it. */
.aclaw-pref-cats {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
  margin-top: 2px;
}
.aclaw-pref-catchip {
  font: inherit;
  font-size: 10px; font-weight: 700;
  letter-spacing: 0.03em;
  padding: 3px 9px; border-radius: 999px;
  cursor: pointer;
  border: 1px dashed color-mix(in srgb, var(--ableclaw-fg) 26%, transparent);
  background: transparent;
  color: color-mix(in srgb, var(--ableclaw-fg) 62%, transparent);
  transition: background-color 120ms ease, border-color 120ms ease, color 120ms ease;
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.aclaw-pref-catchip:hover {
  border-color: color-mix(in srgb, #7c3aed 50%, transparent);
  color: var(--ableclaw-fg);
}
.aclaw-pref-catchip[aria-pressed="true"] {
  border-style: solid;
  border-color: transparent;
  background: rgba(124, 58, 237, 0.16);
  color: color-mix(in srgb, #7c3aed 50%, var(--ableclaw-fg));
}
/* The action row is its own band, not a tail on the category line: Save is the
   commitment being made and reads at full weight, Cancel stays a ghost. */
.aclaw-pref-form-actions {
  display: flex; align-items: center; justify-content: flex-end; gap: 8px;
  /* The commit band gets air on BOTH sides of its rule — at 8px above and the
     card's own 12px below, the buttons sat on the card's bottom edge on a phone
     and read as overflowing it. */
  padding-top: 11px; margin-bottom: 2px;
  border-top: 1px solid color-mix(in srgb, var(--ableclaw-fg) 8%, transparent);
}
.aclaw-pref-btn {
  padding: 8px 14px;
  border-radius: var(--ableclaw-radius-sm, 8px);
  /* NOT --ableclaw-border-soft: that is a SEPARATOR weight, and in the dark theme
     it resolves to 6% white — measured on the composer card, which made Cancel
     read as floating text rather than as the button beside Save. A control's edge
     is blended toward the foreground so it holds in both modes. */
  border: 1px solid color-mix(in srgb, var(--ableclaw-fg) 22%, transparent);
  background: transparent;
  color: var(--ableclaw-fg);
  font-size: 12px; font-weight: 600; font-family: inherit;
  cursor: pointer;
  transition: background-color 120ms, border-color 120ms, opacity 120ms,
              box-shadow 140ms ease, transform 100ms ease;
}
.aclaw-pref-btn:hover { background: var(--ableclaw-sidebar-hover-bg, rgba(127, 127, 127, 0.12)); }
.aclaw-pref-btn:active { transform: translateY(1px); }
.aclaw-pref-btn[disabled] { opacity: 0.5; cursor: default; transform: none; }
/* Save is the commitment on this card, so it carries the panel's own gradient
   and a shadow — the same identity as the header badge and the open toggle,
   rather than a flat chip the same size as Cancel. */
.aclaw-pref-btn--primary {
  background: linear-gradient(135deg, #7c3aed 0%, #6366f1 100%);
  border-color: transparent;
  color: #ffffff;
  padding: 8px 16px;
  box-shadow: 0 2px 8px rgba(124, 58, 237, 0.28);
}
.aclaw-pref-btn--primary:hover {
  background: linear-gradient(135deg, #6d28d9 0%, #4f46e5 100%);
  box-shadow: 0 4px 12px rgba(124, 58, 237, 0.36);
}
/* A disabled button that looks identical to an idle one reads as a dead click.
   While the write is in flight the label is replaced by a spinner IN PLACE, so
   the button keeps its width and nothing on the card moves. */
.aclaw-pref-btn--primary.is-busy {
  color: transparent;
  position: relative;
  opacity: 1;
}
.aclaw-pref-btn--primary.is-busy::after {
  content: '';
  position: absolute; top: 50%; left: 50%;
  width: 13px; height: 13px;
  margin: -7px 0 0 -7px;
  border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, 0.35);
  border-top-color: #ffffff;
  animation: aclaw-pref-spin 620ms linear infinite;
}
@keyframes aclaw-pref-spin { to { transform: rotate(360deg); } }
.aclaw-pref-btn--danger {
  background: var(--danger, #b91c1c);
  border-color: transparent;
  color: var(--on-danger, #ffffff);
}
.aclaw-pref-btn--danger:hover {
  background: color-mix(in srgb, var(--danger, #b91c1c) 85%, #000);
}
.aclaw-pref-error-inline {
  font-size: 11px; line-height: 1.45;
  color: var(--danger, #b91c1c);
  padding: 7px 9px;
  border-radius: var(--ableclaw-radius-sm, 8px);
  border: 1px solid color-mix(in srgb, var(--danger, #b91c1c) 35%, transparent);
  background: color-mix(in srgb, var(--danger, #b91c1c) 10%, transparent);
}

/* The Add affordance — a full-width dashed slot at the top of the list, which
   reads as "there is room for one more" rather than as another row. */
.aclaw-pref-add {
  /* --ableclaw-border-soft is a hairline meant to SEPARATE, and a dashed one at
     that weight read as decoration rather than as the edge of a control. This is
     the only affordance for adding a rule, so its boundary is blended toward the
     foreground — visible in both modes, still quieter than a real button. */
  border: 1px dashed color-mix(in srgb, var(--ableclaw-fg) 28%, transparent);
  border-radius: var(--ableclaw-radius-sm, 8px);
  background: transparent;
  color: color-mix(in srgb, var(--ableclaw-accent-text, #6366f1) 45%, var(--ableclaw-fg));
  font-size: 12px; font-weight: 600; font-family: inherit;
  padding: 9px 12px;
  cursor: pointer;
  display: flex; align-items: center; justify-content: center; gap: 6px;
  transition: background-color 120ms, border-color 120ms;
}
.aclaw-pref-add:hover {
  background: var(--ableclaw-sidebar-hover-bg, rgba(127, 127, 127, 0.10));
  border-color: var(--ableclaw-accent, #6366f1);
}
/* REQUIRED, not defensive. The UA sheet hides [hidden] with a type-selector rule,
   which a class setting display:flex outranks — so _prefBeginAdd could set
   .hidden = true, report success, and leave the slot on screen above the very
   composer it opened. Any element whose display is set by a class needs this
   line before its hidden property means anything. */
.aclaw-pref-add[hidden] { display: none; }
/* The composer card. It EMERGES from the Add slot it replaces — opacity and a
   short lift, never height (a layout property animating every frame), so the
   rows below settle once instead of being re-laid-out 60 times a second. */
.aclaw-pref-new,
.aclaw-pref-row.is-editing {
  border: 1px solid color-mix(in srgb, #7c3aed 45%, transparent);
  border-left-width: 3px;
  border-left-color: #7c3aed;
  border-radius: var(--ableclaw-radius-sm, 8px);
  background: color-mix(in srgb, #7c3aed 5%, var(--ableclaw-bg, #ffffff));
  box-shadow: 0 4px 16px rgba(124, 58, 237, 0.12);
  padding: 12px 13px;
  display: flex; flex-direction: column; gap: 10px;
  animation: aclaw-pref-compose-in 200ms cubic-bezier(0.22, 1, 0.36, 1);
}
@keyframes aclaw-pref-compose-in {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .aclaw-pref-new,
  .aclaw-pref-row.is-editing { animation: none; }
  .aclaw-pref-btn--primary.is-busy::after { animation: none; }
}

/* Delete confirm — replaces the row body in place. No undo, so it asks. */
.aclaw-pref-confirm { display: flex; flex-direction: column; gap: 8px; }
.aclaw-pref-confirm-text { font-size: 13px; line-height: 1.45; }
.aclaw-pref-confirm-actions { display: flex; align-items: center; gap: 8px; }

/* Loading skeleton */
.aclaw-pref-skel {
  border: 1px solid var(--ableclaw-border-soft);
  border-radius: var(--ableclaw-radius-sm, 8px);
  padding: 10px 12px;
  display: flex; flex-direction: column; gap: 8px;
}
.aclaw-pref-skel-bar {
  height: 9px; border-radius: 4px;
  background: linear-gradient(90deg,
    var(--ableclaw-sidebar-hover-bg, rgba(127, 127, 127, 0.10)) 25%,
    var(--ableclaw-border-soft) 37%,
    var(--ableclaw-sidebar-hover-bg, rgba(127, 127, 127, 0.10)) 63%);
  background-size: 400% 100%;
  animation: aclaw-pref-shimmer 1.4s ease infinite;
}
.aclaw-pref-skel-bar.is-short { width: 45%; }
@keyframes aclaw-pref-shimmer {
  0%   { background-position: 100% 50%; }
  100% { background-position: 0 50%; }
}
@media (prefers-reduced-motion: reduce) {
  .aclaw-pref-skel-bar { animation: none; }
  able-claw[data-prefs-open="true"] > .aclaw-grid > .aclaw-prefs { animation: none; }
}

/* Empty / error states */
.aclaw-pref-state {
  margin: auto 0;
  padding: 22px 6px;
  text-align: center;
  display: flex; flex-direction: column; align-items: center; gap: 10px;
}
.aclaw-pref-state-icon {
  font-size: 22px;
  color: var(--ableclaw-text-muted, var(--text-secondary, #94a3b8));
  opacity: 0.75;
}
.aclaw-pref-state-title { font-size: 13px; font-weight: 600; }
.aclaw-pref-state-sub {
  font-size: 12px; line-height: 1.5;
  color: var(--ableclaw-text-muted, var(--text-secondary, #64748b));
  max-width: 260px;
}

/* Full-width on a phone — a 400px drawer over a 390px viewport is the whole
   screen anyway, and the sliver of chat behind it only invites a mis-tap. */
@media (max-width: 480px) {
  .aclaw-prefs { width: 100%; border-left: 0; }
  /* The commit row FILLS the width on a phone. Sized to their labels the two
     buttons came to 70px and 129px in a 315px row — 108px of it empty, a third
     of the band, with the two targets huddled at the right edge. Save keeps the
     larger share, so filling the row costs nothing in hierarchy and buys both
     controls a wider tap. */
  .aclaw-pref-form-actions .aclaw-pref-btn { flex: 1 1 0; }
  .aclaw-pref-form-actions .aclaw-pref-btn--primary { flex: 1.7 1 0; }
  /* …and once it IS the whole screen, nothing underneath may still be tappable.
     The sidebar-expand chevron carries z-index:100 — ABOVE this pane's 40 — so
     it pierced the overlay and sat on top of the "Preferences" title, taking
     taps meant for the panel and opening the drawer instead. Only hidden at
     this width: on a wider viewport the pane is a right-edge drawer and that
     chevron is far outside it, still legitimately reachable.

     !important is REQUIRED, not habit: the collapsed-sidebar rule above
     (able-claw[data-sidebar-collapsed="true"] .aclaw-sidebar-expand) already
     carries one, and on a phone the sidebar is collapsed by default — so a
     plain declaration here loses to it and the chevron stays on the title.
     Same specificity, later in the sheet, so this wins. */
  able-claw[data-prefs-open="true"] .aclaw-sidebar-expand,
  able-claw[data-prefs-open="true"] .aclaw-rail-new-chat { display: none !important; }
}

/* Touch / coarse pointers — the 44px minimum the rest of the chrome honours.
   EVERY tappable control in the pane is listed, not just the icon buttons: the
   Add slot measured 345x35 and the expander 60x18 on a 375px viewport, and a
   control being wide does not excuse it being short. */
@media (pointer: coarse) {
  .aclaw-prefs-toggle,
  .aclaw-pref-act,
  .aclaw-pref-close {
    min-width: 44px; min-height: 44px;
    display: inline-flex; align-items: center; justify-content: center;
  }
  .aclaw-pref-add,
  .aclaw-pref-btn { min-height: 44px; }
  /* A text field is a tap target too. The category input measured 315x38 on a
     375px viewport — wide, and still under the floor, which is the same "a
     control being wide does not excuse it being short" the Add slot was fixed
     for. (The rule textarea clears it on min-height alone.) */
  .aclaw-pref-cat { min-height: 44px; }
  /* iOS anti-zoom — a focused field under 16px makes iPhone Safari zoom to 1.3x
     and it never zooms back. This is the floor the base rules' max(16px, Npx)
     was reaching for; keyed on pointer (not width) because an iPad at 1024px
     zooms too, while a narrow DESKTOP window does not — a width query there
     would just re-inflate the type on the pointer that showed the bug. */
  .aclaw-pref-edit-area, .aclaw-pref-cat { font-size: 16px; }
  /* The composer's chips are tappable controls like any other — a pill being
     pretty does not exempt it from the 44px floor the rest of the pane honours.
     They keep their pill shape and grow vertically.

     The padding is what keeps them PILLS. At 44px tall with 12px of side padding
     a three-letter label ("ddd") came out 45x44 — a circle, which reads as an
     avatar or a status dot, not as a word you can press. 18px of side padding
     plus a floor on the width keeps the shape wider than it is tall whatever the
     label says. */
  .aclaw-pref-ex,
  .aclaw-pref-catchip {
    min-height: 44px; min-width: 68px;
    padding-left: 18px; padding-right: 18px;
    display: inline-flex; align-items: center; justify-content: center;
  }
  /* 5px of air between a small-caps label and a 44px pill reads as the two being
     stuck together. The chips only grow to 44px on this pointer, so the gap that
     suits them on a desktop is the wrong one here. */
  .aclaw-pref-examples { gap: 9px; }
  .aclaw-pref-cats { gap: 9px; }
  /* An inline text link, so it gets the height without becoming a block. */
  .aclaw-pref-more {
    min-height: 44px;
    display: inline-flex; align-items: center;
  }
  /* Separate edit from DELETE once both are 44px wide — a 2px gap between a
     benign and a destructive target invites the wrong tap. */
  .aclaw-pref-row-actions { gap: 10px; }
  .aclaw-chat > .aclaw-prefs-toggle { right: 64px; }
}

/* Tool-progress timeline (mounted by _appendToolEvent) */
.aclaw-timeline {
  display: flex; flex-direction: column; gap: 8px;
  max-height: 100%; overflow-y: auto;
}
.aclaw-tool-row {
  display: flex; gap: 8px; align-items: flex-start;
  font-size: 13px; line-height: 1.4;
}
.aclaw-tool-row::before {
  content: '';
  width: 8px; height: 8px; border-radius: 50%;
  margin-top: 5px;
  flex-shrink: 0;
}
.aclaw-tool-tool_call::before    { background: #3b82f6; }
.aclaw-tool-tool_result::before  { background: #10b981; }
.aclaw-tool-reasoning::before {
  background: transparent;
  border: 2px solid var(--grey-500, #94a3b8);
  width: 6px; height: 6px;
}
/* Lifecycle rows: neutral, slightly transparent. Subtle gradient dot so
   they read as "scaffolding" — visible but not competing with real tool
   events for attention. The subkind-driven arrow lives inside the body. */
.aclaw-tool-lifecycle::before {
  background: var(--grey-400, #cbd5e1);
  opacity: 0.7;
}
.aclaw-tool-lifecycle {
  color: var(--grey-600, #64748b);
  font-size: 12px;
  white-space: pre-wrap; word-break: break-word;
}
.aclaw-tool-lifecycle-arrow {
  display: inline-block;
  font-weight: 600;
  margin-right: 4px;
  color: var(--grey-700, #475569);
}
.aclaw-tool-name { font-weight: 600; }
.aclaw-tool-args, .aclaw-tool-result-content {
  color: var(--grey-700, #64748b);
  white-space: pre-wrap; word-break: break-word;
  max-width: 100%;
}
.aclaw-tool-reasoning {
  font-style: italic;
  color: var(--grey-600, #94a3b8);
  white-space: pre-wrap; word-break: break-word;
}
/* Per-row glyph + kind label — visual cue for "this row is a reasoning
   block / tool invocation / tool result". Mirrors the prod UX where each
   row in the Agent Thinking panel starts with an icon + short uppercase
   tag so users can scan the timeline at a glance. */
.aclaw-tool-glyph {
  display: inline-block;
  margin-right: 6px;
  font-size: 13px;
  line-height: 1;
  vertical-align: -1px;
  opacity: 0.85;
}
.aclaw-tool-kind-label {
  display: inline-block;
  margin-right: 8px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--grey-600, #94a3b8);
  vertical-align: 1px;
}
.aclaw-tool-time {
  font-size: 10px; opacity: 0.5;
  flex-shrink: 0;
  font-variant-numeric: tabular-nums;
  align-self: flex-start;
  margin-top: 3px;
  min-width: 36px; text-align: right;
}
.aclaw-tool-body {
  flex: 1; min-width: 0;
}

/* Per-tool latency badge (2026-05-20).
   Server tags tool_result with data-duration-ms; renderer adds a colored
   pill so the user can scan for slow rows at a glance.
   Tiers chosen to match the operator's mental model of "snappy / normal /
   slow / problematic" rather than absolute milliseconds. */
.aclaw-tool-duration {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 6px;
  font-size: 10px;
  font-variant-numeric: tabular-nums;
  border-radius: 8px;
  font-weight: 600;
  vertical-align: 1px;
}
.aclaw-tool-duration--snappy      { background: rgba(34, 197, 94, 0.15);  color: #16a34a; }
.aclaw-tool-duration--normal      { background: rgba(234, 179, 8, 0.15);  color: #ca8a04; }
.aclaw-tool-duration--slow        { background: rgba(249, 115, 22, 0.18); color: #ea580c; }
.aclaw-tool-duration--problematic { background: rgba(220, 38, 38, 0.20);  color: #dc2626; }

/* Server waterfall (turn_trace) — the end-of-turn AgentTurnTrace rendered as
   compact rows: span name | ms (+% of turn) | detail (model/tokens/cache/ttft).
   The non-MCP telemetry surface for live testing. */
.aclaw-turn-trace { font-size: 11px; line-height: 1.5; }
.aclaw-trace-head { font-weight: 600; margin-bottom: 4px; }
.aclaw-trace-hint { font-weight: 400; opacity: 0.6; font-size: 10px; }
.aclaw-trace-row { display: flex; gap: 8px; align-items: baseline; padding: 1px 0; }
.aclaw-trace-row code { font-family: var(--font-mono, ui-monospace, Menlo, monospace); font-size: 10.5px; min-width: 150px; }
.aclaw-trace-ms { font-variant-numeric: tabular-nums; font-weight: 600; white-space: nowrap; }
.aclaw-trace-detail { opacity: 0.65; font-size: 10px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Billed output that produced no characters. Warning-toned and full-opacity against
   the dimmed row: it is the one number in the waterfall that is money rather than
   latency, and on a stalled turn it is the majority of the turn's billed output. */
.aclaw-trace-unseen { color: var(--warning, #b45309); opacity: 1; font-weight: 600; }

/* Recurring-failure warning row — surfaces inside the tool_result body
   when the SAME (tool, entity) pair has failed N>1 times in the turn.
   Operator-visible signal of broken accessor / missing MV / migration debt. */
.aclaw-tool-recurring {
  margin-top: 4px;
  padding: 4px 8px;
  font-size: 11px;
  font-weight: 500;
  background: rgba(220, 38, 38, 0.08);
  border-left: 3px solid #dc2626;
  border-radius: 0 4px 4px 0;
  color: #991b1b;
  line-height: 1.4;
}
.aclaw-tool-recurring code {
  font-family: var(--font-mono, ui-monospace, Menlo, monospace);
  font-size: 10px;
  padding: 0 4px;
  background: rgba(220, 38, 38, 0.10);
  border-radius: 3px;
}

/* End-of-turn summary box — closes the Thinking panel timeline with the
   wall-clock breakdown so the user can answer "why was this turn slow?"
   without inspecting individual rows. */
.aclaw-turn-summary {
  margin: 8px 0 4px 0;
  padding: 10px 12px;
  border-radius: 8px;
  background: var(--grey-50, #f8fafc);
  border: 1px solid var(--grey-200, #e2e8f0);
  font-size: 12px;
  line-height: 1.5;
}
.aclaw-turn-summary--error {
  border-color: #fca5a5;
  background: rgba(254, 226, 226, 0.4);
}
.aclaw-turn-summary-headline {
  font-size: 13px;
  margin-bottom: 4px;
  display: flex;
  align-items: center;
  gap: 6px;
}
.aclaw-turn-summary-glyph { font-size: 14px; }
.aclaw-turn-summary-status {
  margin-left: auto;
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  color: var(--grey-600, #64748b);
}
.aclaw-turn-summary--error .aclaw-turn-summary-status {
  color: #dc2626;
}
.aclaw-turn-summary-row {
  color: var(--grey-700, #475569);
  margin-top: 2px;
  font-variant-numeric: tabular-nums;
}
.aclaw-turn-summary-row--waste {
  color: #b91c1c;
  font-weight: 500;
}
.aclaw-turn-summary-failed {
  color: #dc2626;
  font-weight: 500;
}
.aclaw-turn-summary-pct {
  color: #6b7280;
  font-weight: 400;
  font-size: 11px;
}
.aclaw-turn-summary-where {
  margin-bottom: 6px;
}
/* Section blocks — slowest calls, per-tool, failing entities */
.aclaw-turn-summary-section {
  margin-top: 10px;
  padding-top: 8px;
  border-top: 1px dashed rgba(15, 23, 42, 0.12);
}
.aclaw-turn-summary-section--action {
  background: rgba(220, 38, 38, 0.05);
  margin-left: -10px;
  margin-right: -10px;
  padding: 8px 10px 6px;
  border-radius: 4px;
  border-top: none;
}
.aclaw-turn-summary-section-title {
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: #6b7280;
  margin-bottom: 4px;
}
.aclaw-turn-summary-section--action .aclaw-turn-summary-section-title {
  color: #b91c1c;
}
/* Slowest-call row */
.aclaw-turn-summary-call {
  display: grid;
  grid-template-columns: 18px 50px 1fr auto;
  gap: 6px;
  align-items: baseline;
  padding: 2px 0;
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  overflow: hidden;
}
.aclaw-turn-summary-call-glyph { font-size: 12px; }
.aclaw-turn-summary-call-ms {
  font-weight: 600;
  color: #0f172a;
}
.aclaw-turn-summary-call-tool {
  color: #1e3a8a;
  overflow: hidden;
  text-overflow: ellipsis;
}
.aclaw-turn-summary-call-entity {
  color: #475569;
  font-style: italic;
}
.aclaw-turn-summary-call-err {
  color: #b91c1c;
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.aclaw-turn-summary-call--error .aclaw-turn-summary-call-ms { color: #b91c1c; }
/* Per-tool row */
.aclaw-turn-summary-tool {
  display: grid;
  grid-template-columns: 1fr auto auto;
  gap: 8px;
  align-items: baseline;
  padding: 2px 0;
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}
.aclaw-turn-summary-tool-name {
  color: #1e3a8a;
  font-weight: 500;
}
.aclaw-turn-summary-tool-count { color: #6b7280; }
.aclaw-turn-summary-tool-failed { color: #dc2626; }
.aclaw-turn-summary-tool-ms {
  color: #0f172a;
  font-weight: 600;
}
/* Failing entity row */
.aclaw-turn-summary-entity {
  padding: 2px 0;
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}
.aclaw-turn-summary-entity-name {
  color: #0f172a;
  font-weight: 600;
}
.aclaw-turn-summary-entity-count {
  color: #b91c1c;
  font-weight: 600;
}
.aclaw-turn-summary-entity-err {
  color: #b91c1c;
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
/* LLM round-trip breakdown rows (v3) */
.aclaw-turn-summary-llm-row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 8px;
  align-items: baseline;
  padding: 2px 0;
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}
.aclaw-turn-summary-llm-label {
  color: #475569;
}
.aclaw-turn-summary-llm-value {
  color: #0f172a;
  text-align: right;
}
.aclaw-turn-summary-llm-warn {
  color: #b91c1c;
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-left: 6px;
}
/* Per-round-trip drill-down table (v4) */
.aclaw-turn-summary-rt-details {
  margin-top: 8px;
}
.aclaw-turn-summary-rt-summary {
  cursor: pointer;
  font-size: 11px;
  color: #1e3a8a;
  user-select: none;
  padding: 2px 0;
}
.aclaw-turn-summary-rt-summary:hover {
  color: #1d4ed8;
}
.aclaw-turn-summary-rt-table {
  margin-top: 6px;
  font-size: 11px;
  font-variant-numeric: tabular-nums;
}
.aclaw-turn-summary-rt-head,
.aclaw-turn-summary-rt-row {
  display: grid;
  grid-template-columns: 26px 50px 50px 50px 36px 1fr;
  gap: 6px;
  padding: 2px 0;
  align-items: baseline;
}
.aclaw-turn-summary-rt-head {
  font-weight: 600;
  color: #6b7280;
  border-bottom: 1px solid rgba(15, 23, 42, 0.08);
  padding-bottom: 4px;
  margin-bottom: 2px;
}
.aclaw-turn-summary-rt-row { color: #475569; }
.aclaw-turn-summary-rt-idx { color: #6b7280; }
.aclaw-turn-summary-rt-ms  { color: #0f172a; font-weight: 600; }
.aclaw-turn-summary-rt-row--slow {
  background: rgba(245, 158, 11, 0.1);
  margin-left: -6px;
  margin-right: -6px;
  padding-left: 6px;
  padding-right: 6px;
  border-radius: 3px;
}
.aclaw-turn-summary-rt-row--slow .aclaw-turn-summary-rt-ms { color: #b45309; }
/* stop_reason badges */
.aclaw-turn-summary-rt-stop {
  display: inline-block;
  font-size: 9px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 1px 4px;
  border-radius: 2px;
  line-height: 1.4;
}
.aclaw-turn-summary-rt-stop--end {
  background: rgba(34, 197, 94, 0.12);
  color: #15803d;
}
.aclaw-turn-summary-rt-stop--tool {
  background: rgba(59, 130, 246, 0.12);
  color: #1e40af;
}
.aclaw-turn-summary-rt-stop--truncated {
  background: rgba(220, 38, 38, 0.12);
  color: #b91c1c;
}
.aclaw-turn-summary-rt-stop--other {
  background: rgba(107, 114, 128, 0.12);
  color: #4b5563;
}
.aclaw-turn-summary-rt-stop-empty { color: #9ca3af; }

/* Dark-theme overrides for the end-of-turn summary box. The whole
   .aclaw-turn-summary-* family was authored in light-mode hex (slate labels,
   near-black values, dark-blue tool names, light-pink error surface, dark
   hairline borders) and none of it flips with the theme, so on the dark
   Thinking panel the labels + section headers render near-invisible. Re-color
   to dark-appropriate values. (Box bg/border already flip via var(--grey-*).) */
[data-theme="dark"] able-claw .aclaw-turn-summary-llm-label,
[data-theme="dark"] able-claw .aclaw-turn-summary-call-entity,
[data-theme="dark"] able-claw .aclaw-turn-summary-rt-row { color: #cbd5e1; }
[data-theme="dark"] able-claw .aclaw-turn-summary-status,
[data-theme="dark"] able-claw .aclaw-turn-summary-section-title,
[data-theme="dark"] able-claw .aclaw-turn-summary-pct,
[data-theme="dark"] able-claw .aclaw-turn-summary-tool-count,
[data-theme="dark"] able-claw .aclaw-turn-summary-rt-head,
[data-theme="dark"] able-claw .aclaw-turn-summary-rt-idx,
[data-theme="dark"] able-claw .aclaw-turn-summary-rt-stop-empty { color: #94a3b8; }
[data-theme="dark"] able-claw .aclaw-turn-summary-llm-value,
[data-theme="dark"] able-claw .aclaw-turn-summary-call-ms,
[data-theme="dark"] able-claw .aclaw-turn-summary-tool-ms,
[data-theme="dark"] able-claw .aclaw-turn-summary-entity-name,
[data-theme="dark"] able-claw .aclaw-turn-summary-rt-ms { color: #f1f5f9; }
[data-theme="dark"] able-claw .aclaw-turn-summary-call-tool,
[data-theme="dark"] able-claw .aclaw-turn-summary-tool-name,
[data-theme="dark"] able-claw .aclaw-turn-summary-rt-summary { color: #93c5fd; }
[data-theme="dark"] able-claw .aclaw-turn-summary-rt-summary:hover { color: #bfdbfe; }
/* error / warning reds run a touch dark on the panel — lighten for contrast */
[data-theme="dark"] able-claw .aclaw-turn-summary-row--waste,
[data-theme="dark"] able-claw .aclaw-turn-summary-failed,
[data-theme="dark"] able-claw .aclaw-turn-summary-tool-failed,
[data-theme="dark"] able-claw .aclaw-turn-summary-call-err,
[data-theme="dark"] able-claw .aclaw-turn-summary-entity-count,
[data-theme="dark"] able-claw .aclaw-turn-summary-entity-err,
[data-theme="dark"] able-claw .aclaw-turn-summary-llm-warn,
[data-theme="dark"] able-claw .aclaw-turn-summary--error .aclaw-turn-summary-status,
[data-theme="dark"] able-claw .aclaw-turn-summary-section--action .aclaw-turn-summary-section-title { color: #fca5a5; }
/* hairline dividers + the error surface tint */
[data-theme="dark"] able-claw .aclaw-turn-summary-section { border-top-color: rgba(255, 255, 255, 0.12); }
[data-theme="dark"] able-claw .aclaw-turn-summary-rt-head { border-bottom-color: rgba(255, 255, 255, 0.10); }
[data-theme="dark"] able-claw .aclaw-turn-summary--error {
  background: rgba(127, 29, 29, 0.22);
  border-color: rgba(248, 113, 113, 0.5);
}
/* per-round-trip stop badges — light text on their translucent tints */
[data-theme="dark"] able-claw .aclaw-turn-summary-rt-stop--end       { color: #86efac; }
[data-theme="dark"] able-claw .aclaw-turn-summary-rt-stop--tool      { color: #93c5fd; }
[data-theme="dark"] able-claw .aclaw-turn-summary-rt-stop--truncated { color: #fca5a5; }
[data-theme="dark"] able-claw .aclaw-turn-summary-rt-stop--other     { color: #cbd5e1; }

/* Narrow viewports — sidebar becomes a slide-in DRAWER overlay (legacy
   askAble pattern: hamburger toggle reveals it). Chat takes the full
   width; tapping outside the drawer or the chevron toggles it closed.

   Sidebar is rendered as a fixed-position overlay sliding in from the
   left, with a translucent backdrop behind it. The collapse / expand
   buttons toggle the same data-sidebar-collapsed attribute on the host
   — keeping ONE source of truth for both desktop and mobile state. */
@media (max-width: 720px) {
  .aclaw-grid { grid-template-columns: 1fr !important; }
  .aclaw-grid:has(.aclaw-artifact:not([hidden])) { grid-template-columns: 1fr !important; }
  .aclaw-artifact { display: none; }

  /* Sidebar becomes a slide-in drawer, hidden by default, slides in when
     data-sidebar-collapsed="false" (the default attribute state at boot
     should be data-sidebar-collapsed="true" on mobile — the runtime now
     forces this on first paint when matchMedia(720px) matches). */
  .aclaw-sidebar {
    position: fixed;
    top: 0; bottom: 0; left: 0;
    width: min(86vw, 320px);
    z-index: 200;
    border-right: 1px solid var(--ableclaw-border-soft);
    box-shadow: 0 12px 32px rgba(0, 0, 0, 0.25);
    transform: translateX(-100%);
    transition: transform 220ms cubic-bezier(0.32, 0.72, 0.32, 1);
    visibility: visible !important;
    pointer-events: auto !important;
    /* When data-sidebar-collapsed="false" on the host, slide IN */
  }
  able-claw:not([data-sidebar-collapsed="true"]) > .aclaw-grid > .aclaw-sidebar {
    transform: translateX(0);
  }
  /* Translucent backdrop behind the drawer — taps dismiss the drawer.
     Rendered as a ::before on the chat column so we don't need new DOM. */
  able-claw:not([data-sidebar-collapsed="true"]) .aclaw-chat::before {
    content: '';
    position: fixed;
    inset: 0;
    background: oklch(0% 0 0 / 0.40);
    z-index: 150;
    backdrop-filter: blur(2px);
    -webkit-backdrop-filter: blur(2px);
    animation: aclaw-fade-in 200ms ease;
  }
  @keyframes aclaw-fade-in {
    from { opacity: 0; }
    to   { opacity: 1; }
  }

  /* Drawer opener at the chat's top-left. Declared 32px here; the later
     "@media (pointer: coarse)" block floors it at 44px on touch, and min-*
     beats width/height, so on a phone it actually renders 44px — the stream's
     margin-top below is sized against THAT, not against this 32.

     The glyph is upgraded from the SSR's bare "›" by _upgradeSsrShell step (6)
     (there is no "data-mobile-bars" attribute; that was a stale claim in this
     comment). It is deliberately NOT a hamburger — the shell header renders one
     right above it. */
  .aclaw-sidebar-expand {
    display: inline-flex !important;
    top: 8px; left: 8px;
    width: 32px; height: 32px;
    font-size: 14px;
    z-index: 100;
  }
  /* The rail's second control is HIDDEN on a phone — the drawer this rail's
     hamburger opens carries "New Conversation" as its first, full-width button,
     so the function is one tap away and nothing is lost.

     It is removed because it was the WORST offender of the four floating
     controls, in two ways at once (measured on build, 390x844, 2026-08-17):

       - it COLLIDED WITH THE HAMBURGER ABOVE IT. The tops here (8px / 44px)
         were laid out for the 32px boxes declared on this line; the later
         "@media (pointer: coarse)" block floors every chrome control at 44px,
         and min-height beats height — so on every touch device the two boxes
         rendered 8→52 and 44→88 and overlapped by 8px.
       - it reached 88px down the chat, 32px below the deepest other control,
         which is what forced the top reservation to be large enough to be
         worth arguing about.

     With it gone the rail is ONE control and the deepest float is 56px (the
     right-hand pair), which is what the stream inset below is sized against.

     THE SELECTOR MUST CARRY THE ATTRIBUTE, exactly like the prefs-open rule
     does for the same reason: the rule that SHOWS this control is
     "able-claw[data-sidebar-collapsed=true] .aclaw-rail-new-chat" at (0,2,1)
     WITH !important, and on a phone the sidebar is collapsed by default. A bare
     ".aclaw-rail-new-chat { display: none !important }" is (0,1,0) — it loses on
     specificity and the button stays on screen (verified in the browser before
     this shipped). Same specificity, later in the sheet, so this one wins. */
  able-claw[data-sidebar-collapsed="true"] .aclaw-rail-new-chat {
    display: none !important;
  }
  /* Fade-out strip at the top of the chat — content fades into the
     hamburger area instead of getting clipped/half-overlapping. Uses
     the design-file pattern (linear gradient + backdrop-filter blur).
     Z-index 50 sits above messages (which default to 1) but below
     the hamburger (z-index 100). pointer-events:none lets taps go
     through to scroll/select. */
  .aclaw-chat::after {
    content: '';
    position: absolute;
    top: 0; left: 0; right: 0;
    height: 48px;
    background: linear-gradient(
      to bottom,
      var(--lg-wp-1) 30%,
      color-mix(in oklch, var(--lg-wp-1) 60%, transparent) 70%,
      transparent 100%
    );
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    z-index: 50;
    pointer-events: none;
    /* Mask matches the fade so the blur transitions out cleanly */
    mask-image: linear-gradient(to bottom, black 50%, transparent);
    -webkit-mask-image: linear-gradient(to bottom, black 50%, transparent);
  }

  /* Welcome screen — tighter padding + smaller avatar + smaller headlines.
     The padding-top clears the hamburger (top:8 + 44px tap-target = 52px
     bottom) so the agent brief isn't hidden underneath. !important beats the
     Liquid-Glass able-claw .aclaw-welcome padding:32px rule (0,1,1) that
     appears later in source and would otherwise win on specificity.
     (No backticks in this CSS string — they close the JS template literal.) */
  .aclaw-welcome { padding: 60px 14px 14px !important; gap: 8px; }
  .aclaw-welcome-icon { width: 56px !important; height: 56px !important; }
  .aclaw-welcome-icon svg { width: 26px !important; height: 26px !important; }
  .aclaw-welcome-headline-h1 { font-size: 22px !important; }
  .aclaw-welcome-subtitle { font-size: 13px !important; line-height: 1.4; }
  .aclaw-welcome-categories {
    grid-template-columns: 1fr !important;
    gap: 12px !important;
    margin-top: 8px;
  }
  /* Welcome shortcut cards: horizontal layout (icon-side-text instead of
     icon-above-text) saves vertical space on phone */
  .aclaw-welcome-category-items .aclaw-shortcut {
    flex-direction: row !important;
    align-items: center !important;
    padding: 10px 12px !important;
    gap: 10px !important;
    min-height: 0;
  }
  .aclaw-welcome-category-items .aclaw-shortcut .aclaw-shortcut-icon {
    width: 28px !important; height: 28px !important;
  }
  .aclaw-welcome-category-items .aclaw-shortcut .aclaw-shortcut-text {
    font-size: 13px !important;
  }
  /* Category header smaller on mobile */
  .aclaw-welcome-category-header { font-size: 10px !important; padding: 2px 0 6px !important; }
  .aclaw-welcome-category-icon { width: 20px !important; height: 20px !important; font-size: 11px; }

  /* Main chat — tighter padding + larger bubbles, smaller divider.

     THE RESERVATION HAS TO SHRINK THE SCROLL BOX, NOT PAD ITS CONTENT.
     This rule used to read "padding: 60px 14px 12px", to clear the floating
     rail. padding-top on .aclaw-messages reserves space at the top of the
     SCROLLED CONTENT, so it holds only while the thread is scrolled to the very
     top — which a live conversation never is. Measured on build (390x844,
     victor@victor 952f0d6d, 2026-08-17): scrollTop 10991 of scrollHeight 11631,
     and all four floating controls sat ON the newest bubble — 8 overlapping
     pairs, the worst 44x44. Content scrolls underneath absolutely-positioned
     chrome; no amount of padding on that content changes it. (The
     .aclaw-chat::after fade strip above is the cosmetic softening of this same
     bug — it blurs what slides under instead of stopping it.)

     margin-top insets the scroll BOX itself: the stream now STARTS below the
     chrome, so nothing can ever pass beneath it at any scroll position. 64px =
     the deepest remaining float (the right-hand toggles, 12 + 44 = 56) plus 8px
     of air. Sized for the coarse-pointer case, which is the taller one — a
     fine-pointer window at this width tops out at 44px and simply gets more air.

     Keep this in sync with the floats: if a control is added to the rail or its
     top/height changes, this number is what stops it landing on the reader's
     conversation. */
  .aclaw-messages { padding: 12px 14px 2px !important; margin-top: 64px; }
  /* NO font-size here. This selector is (0,1,0) and 'able-claw
     .aclaw-message-bubble' is (0,1,1), so a font-size/padding declared here has
     NEVER applied — the 14px that used to sit on this line was dead code, and
     it wanted to go SMALLER than the desktop size, which is backwards. Reading
     size is ONE value for every width; set it at the (0,1,1) rule. (The padding
     below is dead for the same reason — left in place, flagged, not relied on.) */
  .aclaw-message-bubble { max-width: 90%; padding: 9px 13px; }
  .aclaw-channel-divider { margin: 12px 0 8px; gap: 10px; }
  .aclaw-channel-divider-pill { padding: 2px 8px; font-size: 9px; }

  /* Input bar — edge-to-edge pill, smaller padding, hide footer shortcut hint */
  .aclaw-input-bar {
    margin: 8px 10px 0;
    padding: 8px 10px;
    border-radius: 16px;
  }
  .aclaw-input-footer { padding: 4px 12px 6px 12px; font-size: 10px; }
  .aclaw-input-footer-shortcut { display: none; }
  .aclaw-upload { width: 30px; height: 30px; }
  .aclaw-send { padding: 6px 12px; font-size: 12px; }
  .aclaw-send span { display: none; } /* show icon only on mobile to save room */
  .aclaw-stop span { display: none; }

  /* Drop zone — smaller card on mobile so it fits viewport */
  .aclaw-dropzone-card { padding: 24px 28px; }
  .aclaw-dropzone-icon { font-size: 28px; }
  .aclaw-dropzone-title { font-size: 14px; }
  .aclaw-dropzone-sub { font-size: 11px; }

  /* iOS anti-zoom — textarea below 16px triggers iPhone auto-zoom on focus.
     The base .aclaw-input is 14px and the Liquid-Glass override pins it to
     15px (able-claw .aclaw-input) — BOTH zoom-trigger. Re-pin to 16px with
     the able-claw prefix so we out-specify the Liquid-Glass rule and keep
     iOS at 1.0x while the user types. */
  /* The .aclaw-input-bar qualifier raises specificity to (0,2,1) so this
     beats the Liquid-Glass able-claw-.aclaw-input font-size:15px rule
     (0,1,1) that appears LATER in source: a bare able-claw .aclaw-input
     selector here would tie and lose on source order.
     NOTE: never put backticks in this CSS string — it lives inside a JS
     template literal and a stray backtick closes it, breaking the bundle. */
  able-claw .aclaw-input-bar .aclaw-input { font-size: 16px; }
  /* Bug-#2 — the typed text must stay 16px (anti-zoom) but the PLACEHOLDER
     ("Ask about a customer, proposal, or cat-class…") is too wide for the
     narrow mobile input and wraps to a clipped 2nd line. Shrinking ONLY the
     placeholder font keeps the typed text at 16px (no zoom) while letting
     the hint fit. ::placeholder font-size is independent of the input's. */
  able-claw .aclaw-input-bar .aclaw-input::placeholder { font-size: 10px; }
  /* Same anti-zoom rule, one control over — and it was missed. The sidebar's
     conversation SEARCH ships font-size:12px (.aclaw-search, 0,1,0) with no
     mobile override, so tapping it on an iPhone zooms the page to ~1.3x and
     iOS never zooms back out; the user is left panning a chat surface that no
     longer fits. Off-screen while the sidebar is collapsed, which is why it
     outlived the composer's fix — the drawer is a real mobile surface.
     able-claw-qualified (0,1,1) to beat the base rule; the Liquid-Glass
     able-claw .aclaw-search block declares no font-size, so there is no
     later-in-source tie to lose. Flagged by the served layout audit as its
     only FAIL: INPUT_FONT_SIZE_TOO_SMALL. */
  able-claw .aclaw-search { font-size: 16px; }

  /* Bug #5 — Agent Thinking panel on mobile in FULL mode.
     At <=720px the grid collapses to 1fr and .aclaw-artifact is display:none
     (above), so clicking the brain toggle (which DOES set data-thinking-open)
     produced no visible panel — the helper/launcher slide-in overlay rule
     only targets those two modes. Surface the artifact as a right-edge
     slide-in overlay in full mode too, sized to fit the phone and stacked
     above the sidebar drawer (z 200) + backdrop (z 150). The panel's own
     header X (.aclaw-artifact-close) is the close affordance since the
     overlay covers the brain toggle. */
  able-claw[data-mode="full"][data-thinking-open="true"] > .aclaw-grid > .aclaw-artifact {
    display: flex !important;
    position: absolute;
    top: 0; right: 0; bottom: 0;
    width: min(440px, 100%);
    z-index: 210;
    border-left: 1px solid var(--ableclaw-border-soft);
    box-shadow: -8px 0 28px rgba(15, 17, 23, 0.32);
    /* OPAQUE background — the panel sits ON TOP of the chat, so it must fully
       cover the greeting + message stream behind it. --ableclaw-artifact-bg is
       a translucent frosted material (correct as a 3rd grid COLUMN, but it
       bleeds the chat through when used as an overlay). --ableclaw-bg resolves
       to the solid wallpaper (--lg-wp-1) in both light + dark. */
    background: var(--ableclaw-bg, #141415);
    animation: aclaw-thinking-slide-in 200ms ease-out;
  }
  /* When that overlay is open it fully covers the brain toggle (top-right),
     so hide the toggle to avoid it sitting behind / peeking around the panel.
     The panel's own header X is the close affordance. Mobile full-mode only —
     on desktop the panel is a 3rd grid column and the brain stays a real
     toggle. */
  able-claw[data-mode="full"][data-thinking-open="true"] .aclaw-thinking-toggle {
    display: none;
  }


  /* Safe-area inset — iPhone notch / home indicator clearance.
     env(safe-area-inset-bottom) resolves to non-zero ONLY on iOS notched
     devices (and Android edge-to-edge in PWA mode); 0px everywhere else,
     so this is a no-op on desktop / non-notched phones. Applied to the
     grid so the input bar doesn't sit under the home indicator. */
  able-claw[mode="full"] > .aclaw-grid,
  able-claw[data-mode="full"] > .aclaw-grid {
    padding-bottom: env(safe-area-inset-bottom, 0);
  }
}

/* ── LIFELINE: the scroll column can NEVER break, whatever a host page does ──
   The internal layout uses percentage heights (grid height:100% → chat
   height:100% → minmax(0,1fr) message row). A percentage chain silently
   fails if any ancestor's height isn't "definite" — e.g. a host page sets
   .home-claw{height:100%} against a percentage-height parent. When it fails,
   the chat balloons to CONTENT height, shoving the composer below the fold
   and killing the message scrollbar.
   Defense: in full mode, give the grid a DEFINITE dvh height directly (not a
   percentage), so it — and everything under it — always resolves regardless
   of the host. --ableclaw-viewport-offset lets a page with chrome above the
   claw shrink it. This is a hard invariant; do not weaken it. */
able-claw[data-mode="full"] > .aclaw-grid,
able-claw[mode="full"] > .aclaw-grid {
  height: calc(100dvh - var(--ableclaw-viewport-offset, 0px));
  max-height: calc(100dvh - var(--ableclaw-viewport-offset, 0px));
}
/* And a belt-and-suspenders clamp on the chat column itself: even in helper/
   launcher modes it must never exceed its grid cell, so the message row keeps
   a bounded height and scrolls. */
.aclaw-chat { max-height: 100%; }

/* Very narrow viewports (<= 380px) — push to absolute minimal */
@media (max-width: 380px) {
  /* Same story as the 720px block — no font-size here (dead at (0,1,0), and
     shrinking reading text on a narrow screen is the wrong direction anyway). */
  .aclaw-message-bubble { max-width: 92%; padding: 8px 11px; }
  .aclaw-input-bar { margin: 6px 6px 0; padding: 6px 8px; }
  .aclaw-messages { padding: 10px 10px; }
  .aclaw-welcome-headline-h1 { font-size: 20px; }
  .aclaw-channel-divider { margin: 10px 0 8px; }
}

/* Touch / coarse-pointer devices — iOS HIG + Android Material 44x44 minimum
   tap target on EVERY interactive chrome button. Desktop (fine pointer)
   stays dense; phones/tablets get the proper hit area. */
@media (pointer: coarse) {
  .aclaw-new-chat,
  .aclaw-sidebar-collapse,
  .aclaw-sidebar-expand,
  .aclaw-rail-new-chat,
  .aclaw-thinking-toggle,
  .aclaw-active-conv-chip,
  /* Copy-this-reply. It is HIDDEN until hover on a fine pointer, but on a
     coarse one it is visible and tappable by design — so it is a real touch
     target and owes the 44px minimum like every other control in this list. */
  .aclaw-message-copy,
  .aclaw-send,
  .aclaw-stop,
  /* .aclaw-upload (NOT -btn — the old selector matched nothing, so the attach
     button silently shipped without a touch target) + the rest of the two-row
     composer's control strip. */
  .aclaw-upload,
  .aclaw-mic,
  .aclaw-model-chip,
  .aclaw-mode-opt,
  .aclaw-message-bubble .claw-chip,
  .aclaw-shortcut {
    min-width: 44px;
    min-height: 44px;
  }
  /* The segment's own padding would stack ON TOP of its 44px options and make
     the control 48px; drop it so the seg matches the row's 44px rhythm. */
  .aclaw-mode-seg { padding: 0; }
  /* Shortcut cards already have ample padding — only enforce min-height. */
  .aclaw-shortcut { min-height: 56px; }
  /* Chip buttons are inline; bump padding so 44px height is reached without
     forcing block layout. */
  .aclaw-message-bubble .claw-chip {
    padding-top: 11px;
    padding-bottom: 11px;
  }
  /* Copy-this-reply sits in the 10px timestamp row, so a bare 44px box would
     make every assistant message's footer 44px tall. Let the target OVERHANG
     the row instead — 22px glyph, 44px hit area, row height unchanged. Same
     trick the vocab row's speaker button uses for the same reason. */
  .aclaw-message-copy {
    margin-top: -11px;
    margin-bottom: -11px;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
 * HELPER MODE — <able-claw mode="helper">
 *
 * Embeds the chat as a compact sidebar/drawer inside admin pages.
 * Hides the conversation list + artifact pane (single chat column),
 * renders tool events INLINE in the message stream (instead of pushing
 * them to the artifact pane), and adds a compact agent header at the
 * top of the chat column with a "+ new chat" button.
 *
 * Host pages mount it like a right-rail and drive context + prompts via
 * the public API (setContext, sendPrompt, newConversation, focus).
 * ═══════════════════════════════════════════════════════════════════════ */

able-claw[data-mode="helper"] > .aclaw-grid {
  grid-template-columns: 1fr;
}
able-claw[data-mode="helper"] > .aclaw-grid > .aclaw-sidebar,
able-claw[data-mode="helper"] > .aclaw-grid > .aclaw-artifact {
  display: none;
}
able-claw[data-mode="helper"] .aclaw-chat {
  /* Re-arrange the row track: helper-header (auto) + stream (1fr) + attachments + input + footer */
  grid-template-rows: auto minmax(0, 1fr) auto auto auto;
  grid-template-areas:
    "helper"
    "stream"
    "attachments"
    "input"
    "footer";
}
able-claw[data-mode="helper"] .aclaw-helper-header {
  grid-area: helper;
  display: flex; align-items: center; gap: 10px;
  padding: 12px 16px;
  border-bottom: 1px solid var(--ableclaw-border-soft);
}
able-claw[data-mode="full"] .aclaw-helper-header,
able-claw:not([data-mode="helper"]) .aclaw-helper-header {
  display: none;
}
.aclaw-helper-avatar {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; border-radius: 50%;
  background: var(--ableclaw-accent); color: var(--ableclaw-accent-fg);
  font-weight: 700; font-size: 13px;
  flex-shrink: 0;
}
.aclaw-helper-meta {
  flex: 1; min-width: 0;
  display: flex; flex-direction: column; gap: 2px;
}
.aclaw-helper-name {
  font-size: 13px; font-weight: 700; letter-spacing: 0.01em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.aclaw-helper-sub {
  font-size: 11px; opacity: 0.60;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.aclaw-helper-actions {
  display: inline-flex; align-items: center; gap: 4px;
  flex-shrink: 0;
}
.aclaw-helper-action {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; border-radius: 999px;
  background: transparent; color: inherit; border: 0;
  cursor: pointer; opacity: 0.65;
  transition: opacity 120ms, background-color 120ms;
}
.aclaw-helper-action:hover {
  opacity: 1;
  background: var(--ableclaw-sidebar-hover-bg, rgba(127, 127, 127, 0.10));
}
.aclaw-helper-action i { font-size: 12px; }

/* In helper mode, hide the big welcome screen — agent's greeting bubble
   replaces it (see _renderGreetingHtml). Conversation list is also hidden;
   the sidebar-expand chevron stays hidden since there's no sidebar to
   reopen. */
able-claw[data-mode="helper"] .aclaw-welcome { display: none !important; }
able-claw[data-mode="helper"] .aclaw-sidebar-expand,
able-claw[data-mode="helper"] .aclaw-rail-new-chat { display: none !important; }
able-claw[data-mode="helper"] .aclaw-messages {
  padding: 14px 16px 8px;
}
able-claw[data-mode="helper"] .aclaw-input-bar {
  margin: 0 12px 8px;
}

/* READ-ONLY mount (the 'readonly' attribute — a lab landing on one turn of a thread): the
   thread is the whole surface. Everything that could act is gone — the composer
   with its footer and attachments strip, the preferences door, the new-chat
   button, the retry and the tail's more-menu. Keyed on the attribute itself so
   an SSR shell hides the composer before hydration. */
able-claw[readonly] .aclaw-input-bar,
able-claw[readonly] .aclaw-input-footer,
able-claw[readonly] .aclaw-attachments,
able-claw[readonly] .aclaw-prefs-toggle,
able-claw[readonly] .aclaw-helper-new,
able-claw[readonly] .aclaw-sidebar-expand,
able-claw[readonly] .aclaw-rail-new-chat,
able-claw[readonly] .aclaw-message-more,
able-claw[readonly] .aclaw-error-retry,
able-claw[readonly] .aclaw-turn-retry { display: none !important; }
/* The landed row — scrollToMessage(id, {highlight: true}). */
.aclaw-message--focus > .aclaw-message-bubble {
  box-shadow: 0 0 0 2px var(--accent, #4f7df3);
}

/* Inline tool-call bubble — renders in the message stream when in helper
   mode. Looks like an editor diff: arrow + function name + monospace
   args. Distinct from the assistant chat bubble (no background tint,
   monospace font, condensed). */
.aclaw-message-tool {
  align-self: flex-start;
  max-width: 92%;
  padding: 6px 12px;
  border-radius: 8px;
  background: color-mix(in srgb, var(--ableclaw-accent) 8%, transparent);
  color: var(--text-muted, var(--ableclaw-fg));
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: 12px; line-height: 1.5;
  word-wrap: break-word;
  overflow-wrap: break-word;
}
.aclaw-message-tool-head {
  display: inline-flex; align-items: center; gap: 6px;
  font-weight: 600;
  color: var(--ableclaw-accent-text);
}
.aclaw-message-tool-head::before {
  content: '→';
  opacity: 0.7;
}
.aclaw-message-tool-args {
  color: inherit;
  word-break: break-all;
}
.aclaw-message-tool-result {
  margin-top: 4px;
  opacity: 0.65;
  font-style: italic;
}
.aclaw-message-reasoning {
  align-self: flex-start;
  max-width: 92%;
  padding: 4px 10px;
  font-size: 11px;
  font-style: italic;
  opacity: 0.55;
  color: var(--ableclaw-fg);
}

/* ═══════════════════════════════════════════════════════════════════════════
 * LAUNCHER MODE — <able-claw mode="launcher">
 *
 * Floating bottom-right pill (collapsed) that expands into a chat panel
 * (~380x580 by default) on click. The pill renders the agent's display
 * name + online dot; the expanded panel uses helper-mode internals
 * (compact header, single column, inline tool calls).
 *
 * Position knobs (override via CSS on host page if needed):
 *   --ableclaw-launcher-bottom: 20px;
 *   --ableclaw-launcher-right:  20px;
 *   --ableclaw-launcher-width:  380px;
 *   --ableclaw-launcher-height: 580px;
 * ═══════════════════════════════════════════════════════════════════════ */

able-claw[data-mode="launcher"] {
  --ableclaw-launcher-bottom: 20px;
  --ableclaw-launcher-right:  20px;
  --ableclaw-launcher-width:  380px;
  --ableclaw-launcher-height: 580px;
  /* Override the page-clamping height knob — launcher floats freely. */
  height: auto !important;
  min-height: 0 !important;
  /* The host element itself is invisible — only the FAB + panel render. */
  display: contents;
}

/* Floating pill (collapsed state) — always rendered, click-to-toggle.
   Colors are HARDCODED dark-on-white (intentional brand identity for the
   floating helper — Intercom/Drift style) so the pill stays legible
   regardless of the host tenant's theme. Themes that need a different
   pill color can override via --ableclaw-launcher-pill-bg / -text on
   the able-claw element. */
/* The floating launcher pill is for launcher mode (a FAB that opens the chat).
   On a full-page claw you're already IN the chat — it's redundant chrome. */
able-claw[data-mode="full"] .aclaw-launcher-pill,
able-claw[mode="full"] .aclaw-launcher-pill { display: none !important; }
/* A page can mount BOTH a full claw AND a shell launcher claw (the floating
   "Lucrux" FAB). When a full claw is present, the launcher FAB is redundant —
   hide the whole launcher-mode claw. :has() so it's global regardless of which
   instance's <style> this is. */
body:has(able-claw[data-mode="full"]) able-claw[data-mode="launcher"],
body:has(able-claw[mode="full"])      able-claw[mode="launcher"] { display: none !important; }
/* The footer strip ("Read-only · … · ⌘↩ to send") is low-value chrome that
   pushes the composer up. On a full-page claw, reclaim the space — the send
   button + streaming bubble already signal state, reasoning is a dock tab. */
able-claw[data-mode="full"] .aclaw-input-footer,
able-claw[mode="full"] .aclaw-input-footer { display: none !important; }
/* ── THE FAB's RESERVED BAND, published ONCE ────────────────────────────────
   The launcher pill is fixed and sits on top of whatever the page put in the
   bottom-right. Every page has it, so every page has the same overlap — measured
   on /day at 320px, the pill covered live time blocks (x243-305, y520-552).
   Fixing that per page means N pages hard-coding the same ~60px.

   So the band is DECLARED here, derived from the pill's own offsets, and anything
   bottom-anchored or full-height clears it in one line:

       max-height: calc(100dvh - var(--able-claw-safe-bottom));
       padding-bottom: var(--able-claw-safe-bottom);

   Because it is built from --ableclaw-launcher-* , a shell that MOVES the pill
   moves the safe band with it and no consumer has to be told. A page with no
   launcher pays nothing: the value is just an unused custom property. */
:root {
  --ableclaw-launcher-pill-h: 40px;
  --able-claw-safe-bottom: calc(
    var(--ableclaw-launcher-bottom, 20px) + var(--ableclaw-launcher-pill-h, 40px) + 8px);
}
.aclaw-launcher-pill {
  position: fixed;
  bottom: var(--ableclaw-launcher-bottom, 20px);
  right:  var(--ableclaw-launcher-right, 20px);
  display: inline-flex; align-items: center; gap: 10px;
  padding: 10px 16px;
  border-radius: 999px;
  background: var(--ableclaw-launcher-pill-bg, #1a2236);
  color: var(--ableclaw-launcher-pill-text, #f8fafc);
  border: 1px solid rgba(255, 255, 255, 0.08);
  font-size: 13px; font-weight: 600;
  cursor: pointer;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18), 0 2px 6px rgba(0, 0, 0, 0.12);
  z-index: 9000;
  transition: transform 140ms, box-shadow 140ms, opacity 140ms;
}
.aclaw-launcher-pill:hover {
  transform: translateY(-2px);
  box-shadow: 0 12px 28px rgba(0, 0, 0, 0.22), 0 3px 8px rgba(0, 0, 0, 0.14);
}
.aclaw-launcher-pill:active { transform: translateY(0); }
.aclaw-launcher-pill-icon {
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--ableclaw-accent-text, #f59e0b);
  font-size: 14px;
}
.aclaw-launcher-pill-text {
  white-space: nowrap;
}
.aclaw-launcher-pill-status {
  display: inline-block;
  width: 8px; height: 8px; border-radius: 50%;
  background: #10b981;
  box-shadow: 0 0 0 2px color-mix(in srgb, #10b981 30%, transparent);
  flex-shrink: 0;
}

/* Hide the grid (the chat surface) when launcher is collapsed; show it
   inside a fixed panel when expanded. */
able-claw[data-mode="launcher"] > .aclaw-grid {
  position: fixed;
  bottom: calc(var(--ableclaw-launcher-bottom, 20px) + 60px);
  right:  var(--ableclaw-launcher-right, 20px);
  width:  var(--ableclaw-launcher-width, 380px);
  height: var(--ableclaw-launcher-height, 580px);
  max-height: calc(100dvh - 100px);
  max-width:  calc(100vw - 40px);
  border-radius: 16px;
  border: 1px solid var(--ableclaw-border, rgba(127, 127, 127, 0.20));
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.25), 0 8px 20px rgba(0, 0, 0, 0.15);
  background: var(--ableclaw-bg);
  z-index: 8999;
  transform-origin: bottom right;
  transition: opacity 160ms, transform 160ms;
}
able-claw[data-mode="launcher"][data-launcher-open="false"] > .aclaw-grid {
  opacity: 0;
  transform: scale(0.92) translateY(8px);
  pointer-events: none;
}
able-claw[data-mode="launcher"][data-launcher-open="true"] > .aclaw-grid {
  opacity: 1;
  transform: scale(1) translateY(0);
}
/* PRE-HYDRATION GUARD — launcher mode.
 *
 * The host element is in the DOM as <able-claw mode="launcher"> before
 * able-claw.js hydrates. data-mode + data-launcher-open are added by JS
 * in connectedCallback. Without this rule, the .aclaw-grid would paint
 * in-flow (no fixed-position rule yet), then JS adds data-mode → panel
 * jumps to fixed-position visible, then data-launcher-open="false" →
 * 160ms opacity+scale transition kicks in and the user sees a flash of
 * "open then collapse" on every page reload.
 *
 * Matches the HTML attribute mode="launcher" (always present from the
 * source) AND the absence of data-launcher-open (added post-hydration).
 * Forces the closed state without a transition so first paint is silent.
 * After hydration, data-launcher-open exists and this rule stops matching;
 * the standard open/close rules above resume control.
 */
able-claw[mode="launcher"]:not([data-launcher-open]) > .aclaw-grid {
  opacity: 0 !important;
  pointer-events: none !important;
  transition: none !important;
}
/* In launcher mode the chat surface is always single-column (helper-like) */
able-claw[data-mode="launcher"] > .aclaw-grid {
  grid-template-columns: 1fr;
}
able-claw[data-mode="launcher"] > .aclaw-grid > .aclaw-sidebar,
able-claw[data-mode="launcher"] > .aclaw-grid > .aclaw-artifact {
  display: none;
}
able-claw[data-mode="launcher"] .aclaw-chat {
  grid-template-rows: auto minmax(0, 1fr) auto auto auto;
  grid-template-areas:
    "helper"
    "stream"
    "attachments"
    "input"
    "footer";
}
able-claw[data-mode="launcher"] .aclaw-welcome {
  display: none !important;
}
able-claw[data-mode="launcher"] .aclaw-input-bar {
  margin: 0 12px 8px;
}
able-claw[data-mode="launcher"] .aclaw-messages {
  padding: 12px 16px 8px;
}
able-claw[data-mode="launcher"] .aclaw-helper-header {
  grid-area: helper;
  display: flex; align-items: center; gap: 10px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--ableclaw-border-soft);
}
/* Close button inside the panel header (launcher-only) */
.aclaw-launcher-close {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; border-radius: 999px;
  background: transparent; color: inherit; border: 0;
  cursor: pointer; opacity: 0.65;
  transition: opacity 120ms, background-color 120ms;
}
.aclaw-launcher-close:hover {
  opacity: 1;
  background: var(--ableclaw-sidebar-hover-bg, rgba(127, 127, 127, 0.10));
}
.aclaw-launcher-close i { font-size: 12px; }

/* Mobile launcher — full-screen panel instead of floating card */
@media (max-width: 640px) {
  .aclaw-launcher-pill {
    padding: 8px 12px;
    font-size: 12px;
    bottom: 16px; right: 16px;
  }
  .aclaw-launcher-pill-text { display: none; }  /* icon only on phone */
  able-claw[data-mode="launcher"] > .aclaw-grid {
    inset: 0 !important;
    width: 100% !important;
    height: 100% !important;
    max-width: 100% !important;
    max-height: 100% !important;
    border-radius: 0;
    border: 0;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
 * iOS 2026 LIQUID GLASS — design overlay (from Anthropic design file
 * AbleClaw Chat.html, rBaa5XHjVDHCyLdc1As3tw).
 *
 * Translucent materials with backdrop-filter, oklch color tokens, vibrant
 * blue/purple user gradient + frosted agent bubbles, big radii (22px chat
 * bubbles, 26px composer + sidebar), green online-dot, channel chips
 * with mini colored icon squares.
 *
 * This block LAYERS OVER the structural rules above — no existing rule
 * is removed. Layout/grid/dataflow stays the same; only colors, radii,
 * shadows, and surface materials change.
 * ═══════════════════════════════════════════════════════════════════════ */

able-claw {
  /* Liquid Glass tokens — LIGHT defaults */
  --lg-wp-1: oklch(97% 0.012 250);
  --lg-wp-2: oklch(94% 0.020 245);
  --lg-wp-3: oklch(95% 0.018 260);
  --lg-ink-1: oklch(20% 0.014 255);
  --lg-ink-2: oklch(38% 0.012 255);
  --lg-ink-3: oklch(56% 0.010 255);
  --lg-ink-4: oklch(72% 0.008 255);
  --lg-mat-thick:   color-mix(in oklch, white 75%, transparent);
  --lg-mat-regular: color-mix(in oklch, white 60%, transparent);
  --lg-mat-thin:    color-mix(in oklch, white 40%, transparent);
  --lg-mat-ultra:   color-mix(in oklch, white 22%, transparent);
  --lg-mat-hover:   color-mix(in oklch, white 85%, transparent);
  --lg-line:        color-mix(in oklch, var(--lg-ink-1) 9%, transparent);
  --lg-line-soft:   color-mix(in oklch, var(--lg-ink-1) 5%, transparent);
  --lg-line-strong: color-mix(in oklch, var(--lg-ink-1) 18%, transparent);
  --lg-sys-blue:   oklch(56% 0.16 255);
  --lg-sys-blue-2: oklch(50% 0.18 258);
  --lg-sys-green:  oklch(64% 0.16 145);
  --lg-sys-purple: oklch(58% 0.14 268);
  --lg-grad-user: linear-gradient(135deg, oklch(60% 0.17 255), oklch(52% 0.18 260));
  --lg-grad-agent-avatar: linear-gradient(135deg, var(--lg-sys-purple), var(--lg-sys-blue-2));
  --lg-user-ink: oklch(99% 0.01 255);
  --lg-shadow-sm: 0 1px 0 oklch(0% 0 0 / 0.04), 0 2px 8px -4px oklch(0% 0 0 / 0.10);
  --lg-shadow:    0 2px 4px -2px oklch(0% 0 0 / 0.08), 0 12px 32px -12px oklch(0% 0 0 / 0.18);
  --lg-shadow-lg: 0 8px 16px -8px oklch(0% 0 0 / 0.16), 0 32px 80px -16px oklch(0% 0 0 / 0.28);
  /* Radii */
  --lg-r-xs: 8px; --lg-r-sm: 12px; --lg-r: 16px; --lg-r-md: 20px;
  --lg-r-lg: 26px; --lg-r-xl: 32px; --lg-r-pill: 999px;

  /* Re-point existing --ableclaw-* surfaces to the new tokens so any
     descendant rule that reads them inherits the Liquid Glass look. */
  --ableclaw-bg:                 var(--lg-wp-1);
  --ableclaw-fg:                 var(--lg-ink-1);
  --ableclaw-sidebar-bg:         var(--lg-mat-regular);
  --ableclaw-sidebar-fg:         var(--lg-ink-1);
  --ableclaw-sidebar-active-bg:  var(--lg-mat-thick);
  --ableclaw-sidebar-hover-bg:   var(--lg-mat-thin);
  --ableclaw-artifact-bg:        var(--lg-mat-regular);
  --ableclaw-border:             var(--lg-line);
  --ableclaw-border-soft:        var(--lg-line-soft);
  --ableclaw-bubble-user-bg:     var(--lg-grad-user);
  --ableclaw-bubble-user-fg:     var(--lg-user-ink);
  --ableclaw-bubble-agent-bg:    var(--lg-mat-thick);
  --ableclaw-bubble-agent-fg:    var(--lg-ink-1);
  --ableclaw-input-bg:           var(--lg-mat-regular);
  --ableclaw-input-fg:           var(--lg-ink-1);
  --ableclaw-input-border:       var(--lg-line-soft);
  --ableclaw-input-bar-bg:       transparent;
  --ableclaw-accent:             var(--lg-sys-blue);
  /* the liquid-glass skin re-binds --lg-sys-blue per mode, so the type twin can
     just follow it here rather than lightening a static value */
  --ableclaw-accent-text:        var(--lg-sys-blue);
  --ableclaw-accent-fg:          var(--lg-user-ink);
}

[data-theme="dark"] able-claw {
  --lg-wp-1: oklch(18% 0.018 255);
  --lg-wp-2: oklch(22% 0.024 260);
  --lg-wp-3: oklch(20% 0.020 245);
  --lg-ink-1: oklch(97% 0.008 255);
  --lg-ink-2: oklch(82% 0.010 255);
  --lg-ink-3: oklch(60% 0.010 255);
  --lg-ink-4: oklch(46% 0.010 255);
  --lg-mat-thick:   color-mix(in oklch, oklch(28% 0.014 255) 75%, transparent);
  --lg-mat-regular: color-mix(in oklch, oklch(30% 0.014 255) 60%, transparent);
  --lg-mat-thin:    color-mix(in oklch, oklch(34% 0.014 255) 42%, transparent);
  --lg-mat-ultra:   color-mix(in oklch, oklch(38% 0.014 255) 24%, transparent);
  --lg-mat-hover:   color-mix(in oklch, oklch(36% 0.014 255) 75%, transparent);
  --lg-line:        color-mix(in oklch, white 10%, transparent);
  --lg-line-soft:   color-mix(in oklch, white 6%, transparent);
  --lg-line-strong: color-mix(in oklch, white 22%, transparent);
  --lg-sys-blue:   oklch(66% 0.16 255);
  --lg-sys-blue-2: oklch(60% 0.18 260);
  --lg-sys-green:  oklch(70% 0.16 145);
  --lg-sys-purple: oklch(66% 0.14 268);
  --lg-grad-user: linear-gradient(135deg, oklch(66% 0.16 255), oklch(58% 0.18 262));
  --lg-shadow-sm: 0 1px 0 oklch(0% 0 0 / 0.3), 0 2px 8px -4px oklch(0% 0 0 / 0.4);
  --lg-shadow:    0 2px 4px -2px oklch(0% 0 0 / 0.35), 0 12px 32px -12px oklch(0% 0 0 / 0.5);
  --lg-shadow-lg: 0 8px 16px -8px oklch(0% 0 0 / 0.5), 0 32px 80px -16px oklch(0% 0 0 / 0.7);

  /* Invert the system-base grey scale within able-claw in dark mode.
     The ABLEUI Chat Output Classes (.claw-h1 / .claw-body--editorial /
     .claw-sidebar / .claw-code / .claw-table / etc.) reference these
     primitives directly. Without this flip:
       .claw-h1 { color: var(--grey-900) }  →  #18191b on dark bg → invisible
       .claw-code { background: var(--grey-0) }  →  #fff on dark bg → white block
     We override INSIDE able-claw only, so other blueprints that use the
     greys as fixed primitives (icons, badges) are untouched. */
  --grey-0:    #0c0d0e;
  --grey-50:   #18191b;
  --grey-100:  #1d1c17;
  --grey-200:  #303236;
  --grey-300:  #3f3f46;
  --grey-400:  #505151;
  --grey-500:  #94a3b8;  /* muted body — slate-400, readable on dark bg */
  --grey-600:  #cbd5e1;
  --grey-700:  #e2e8f0;
  --grey-800:  #f0f1f2;
  --grey-900:  #f8fafc;  /* "darkest text" → flipped → near-white in dark */
  --grey-1000: #ffffff;
}

/* ─── Liquid Glass component overrides ─────────────────────────────── */

/* Grid container — wallpaper background + soft tinted border + bigger radius */
able-claw .aclaw-grid {
  background: var(--lg-wp-1);
  border: 1px solid var(--lg-line-soft);
  border-radius: var(--lg-r-md);
  box-shadow: var(--lg-shadow-sm);
}

/* Sidebar — frosted glass material */
able-claw .aclaw-sidebar {
  background: var(--lg-mat-regular);
  backdrop-filter: saturate(180%) blur(28px);
  -webkit-backdrop-filter: saturate(180%) blur(28px);
  border-right: 1px solid var(--lg-line-soft);
}

/* New conversation button — grad-user gradient with inner highlight */
able-claw .aclaw-new-chat {
  background: var(--lg-grad-user);
  color: var(--lg-user-ink);
  border-radius: var(--lg-r);
  font-weight: 600;
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / 0.22),
    0 2px 6px -2px oklch(52% 0.18 260 / 0.4),
    0 8px 20px -8px oklch(52% 0.18 260 / 0.4);
  transition: transform 0.08s, filter 0.15s;
}
able-claw .aclaw-new-chat:hover { filter: brightness(1.05); }
able-claw .aclaw-new-chat:active { transform: scale(0.98); }

/* Sidebar collapse / expand chevron — sq glass btn */
able-claw .aclaw-sidebar-collapse,
able-claw .aclaw-sidebar-expand,
able-claw .aclaw-rail-new-chat {
  background: var(--lg-mat-thin);
  border: 1px solid var(--lg-line-soft);
  border-radius: var(--lg-r);
  color: var(--lg-ink-2);
}
able-claw .aclaw-sidebar-collapse:hover,
able-claw .aclaw-sidebar-expand:hover,
able-claw .aclaw-rail-new-chat:hover {
  background: var(--lg-mat-hover);
  color: var(--lg-ink-1);
}

/* Search input — frosted */
able-claw .aclaw-search {
  background: var(--lg-mat-thin);
  border: 1px solid var(--lg-line-soft);
  border-radius: var(--lg-r);
  color: var(--lg-ink-1);
}
able-claw .aclaw-search:focus {
  background: var(--lg-mat-thick);
  border-color: color-mix(in oklch, var(--lg-sys-blue) 50%, transparent);
  box-shadow: 0 0 0 4px color-mix(in oklch, var(--lg-sys-blue) 16%, transparent);
}

/* Agent selector / TALKING TO card */
able-claw .aclaw-agent-selector {
  background: var(--lg-mat-thick);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border: 1px solid var(--lg-line-soft);
  border-radius: var(--lg-r);
  box-shadow: var(--lg-shadow-sm);
}
able-claw .aclaw-agent-selector:hover { background: var(--lg-mat-hover); }
able-claw .aclaw-agent-selector .aclaw-agent-avatar {
  background: var(--lg-grad-agent-avatar);
  box-shadow: inset 0 1px 0 oklch(100% 0 0 / 0.25), 0 2px 4px oklch(0% 0 0 / 0.12);
}

/* Conversation row in sidebar — active state is clearly distinct:
   accent-tinted background + 3px gradient left strip + accent ring +
   bold title color. Works in both light and dark because the accent
   (sys-blue → sys-purple) reads against both wallpapers.

   NOTE: NO outer box-shadow — the sidebar deliberately doesn't have
   overflow:hidden (agent popover needs to extend below the header),
   so an outer shadow would bleed past the sidebar border. We rely on
   bg tint + inset ring + left accent strip for visual distinction. */
able-claw .aclaw-conversation-row {
  border-radius: 12px;
  position: relative;
  transition: background 140ms, box-shadow 160ms;
}
able-claw .aclaw-conversation-row:hover { background: var(--lg-mat-thin); }
able-claw .aclaw-conversation-row.is-active {
  background: color-mix(in oklch, var(--lg-sys-blue) 14%, var(--lg-mat-thick));
  box-shadow:
    inset 0 0 0 1px color-mix(in oklch, var(--lg-sys-blue) 35%, transparent);
}
/* Left accent strip — subtle gradient bar that reads as the "selected
   indicator" without competing with the chat content. Contained within
   the row's bounds so no bleed past the sidebar edge. */
able-claw .aclaw-conversation-row.is-active::before {
  content: '';
  position: absolute;
  left: 4px; top: 50%;
  transform: translateY(-50%);
  width: 3px; height: 60%;
  border-radius: 999px;
  background: var(--lg-grad-user);
}
able-claw .aclaw-conversation-row.is-active .aclaw-conversation-title {
  color: var(--lg-ink-1);
  font-weight: 600;
}
able-claw .aclaw-conversation-row.is-active .aclaw-conversation-meta {
  color: color-mix(in oklch, var(--lg-sys-blue) 60%, var(--lg-ink-3));
}

/* Welcome surface — frosted intro card */
able-claw .aclaw-welcome {
  background: transparent;
}

/* Message bubbles — modernized: 22px radius, user gradient with glow,
   agent frosted glass */
able-claw .aclaw-message-bubble {
  border-radius: 22px;
  padding: 10px 14px;
  /* Bubble-level reading text (user messages + .aclaw-plainflow — anything NOT
     inside .claw-root). 16px / 1.6 matches the .claw-root base so prose reads
     the same size whichever lane rendered it. NOTE: this rule is (0,1,1) — it
     LOSES to any two-class '.aclaw-message-bubble .x' rule, which is why the
     @media bubble font rules that used to sit at (0,1,0) were dead code. */
  font-size: 16px;
  line-height: 1.6;
  letter-spacing: -0.012em;
  box-shadow: var(--lg-shadow-sm);
}
able-claw .aclaw-message--assistant .aclaw-message-bubble {
  background: var(--lg-mat-thick);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  color: var(--lg-ink-1);
  border: 1px solid var(--lg-line-soft);
}
able-claw .aclaw-message--user .aclaw-message-bubble {
  background: var(--lg-grad-user);
  color: var(--lg-user-ink);
  font-weight: 500;
  border: 0;
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / 0.18),
    0 2px 4px -2px oklch(52% 0.18 260 / 0.35),
    0 12px 28px -10px oklch(52% 0.18 260 / 0.3);
}

/* Channel divider — chip with mini colored icon square */
able-claw .aclaw-channel-divider {
  color: var(--lg-ink-3);
  margin: 16px 0 8px;
}
able-claw .aclaw-channel-divider::before,
able-claw .aclaw-channel-divider::after {
  background: linear-gradient(to right, transparent, var(--lg-line), transparent);
}
able-claw .aclaw-channel-divider-pill {
  background: var(--lg-mat-thick);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border: 1px solid var(--lg-line-soft);
  border-radius: var(--lg-r-pill);
  padding: 5px 12px 5px 6px;
  font-size: 11px;
  letter-spacing: 0.08em;
  color: var(--lg-ink-2);
  gap: 7px;
}
able-claw .aclaw-channel-divider-pill i {
  color: white;
  font-size: 9px;
  width: 18px; height: 18px;
  display: inline-grid; place-items: center;
  border-radius: 6px;
  background: var(--ableclaw-channel-default);
  box-shadow: inset 0 1px 0 oklch(100% 0 0 / 0.25);
}
able-claw .aclaw-channel-divider[data-channel="telegram"]  .aclaw-channel-divider-pill i { background: linear-gradient(135deg, oklch(74% 0.14 230), oklch(58% 0.18 240)); }
able-claw .aclaw-channel-divider[data-channel="slack"]     .aclaw-channel-divider-pill i { background: linear-gradient(135deg, oklch(70% 0.20 320), oklch(54% 0.22 330)); }
able-claw .aclaw-channel-divider[data-channel="email"]     .aclaw-channel-divider-pill i { background: linear-gradient(135deg, oklch(72% 0.06 250), oklch(50% 0.04 250)); }
able-claw .aclaw-channel-divider[data-channel="sms"]       .aclaw-channel-divider-pill i { background: linear-gradient(135deg, oklch(72% 0.18 145), oklch(54% 0.16 145)); }
able-claw .aclaw-channel-divider[data-channel="whatsapp"]  .aclaw-channel-divider-pill i { background: linear-gradient(135deg, oklch(72% 0.18 145), oklch(54% 0.16 145)); }
able-claw .aclaw-channel-divider[data-channel="web"]       .aclaw-channel-divider-pill i { background: linear-gradient(135deg, oklch(70% 0.04 230), oklch(48% 0.04 250)); }
able-claw .aclaw-channel-divider[data-channel="ableui"]    .aclaw-channel-divider-pill i { background: linear-gradient(135deg, oklch(40% 0.04 260), oklch(15% 0.02 260)); }

/* Composer — frosted glass, big radius, blue focus glow */
able-claw .aclaw-input-bar {
  /* 8 above the pill (the gap to the thread — the scroller ends flush), 10
     below it (the page's own bottom air is this plus the safe-area inset). */
  margin: 8px 16px 10px;
  /* This rule (0,1,1) out-specifies every plain .aclaw-input-bar padding rule
     in the file, so it is THE inset of the composer everywhere: 10 above the
     typed line (the textarea carries no vertical padding of its own), 8 under
     the control row. */
  padding: 10px 12px 8px;
  background: var(--lg-mat-regular);
  backdrop-filter: saturate(180%) blur(28px);
  -webkit-backdrop-filter: saturate(180%) blur(28px);
  border: 1px solid var(--lg-line-soft);
  border-radius: var(--lg-r-lg);
  box-shadow: var(--lg-shadow);
  transition: border-color 0.18s, box-shadow 0.18s;
}
able-claw .aclaw-input-bar:focus-within {
  border-color: color-mix(in oklch, var(--lg-sys-blue) 50%, transparent);
  box-shadow: var(--lg-shadow), 0 0 0 4px color-mix(in oklch, var(--lg-sys-blue) 16%, transparent);
}
able-claw .aclaw-upload {
  width: 38px; height: 38px;
  border-radius: 50%;
  background: transparent;
  color: var(--lg-ink-2);
}
able-claw .aclaw-upload:hover:not(:disabled) {
  background: var(--lg-mat-thin);
  color: var(--lg-ink-1);
}
able-claw .aclaw-input {
  background: transparent;
  color: var(--lg-ink-1);
  border: 0;
  /* NO vertical padding. The bar's own padding owns the composer's inset, and
     this rule (0,1,1) is what re-introduced the stacking the base .aclaw-input
     rule was written to prevent — including in THIS block, whose own
     .aclaw-input-bar comment three declarations up already claims "with the
     textarea's own vertical padding removed so the box does the spacing". It
     was not removed: 10px top + 10px bottom inflated a ONE-LINE textarea from
     28px to 44px, i.e. 20px of the composer's height at every width, spent on
     nothing (measured 390x664 build 2026-08-23: input bar 118px, of which the
     typed line claimed 44 for a 24px line).
     The 10px horizontal is the base rule's value too, and it is load-bearing:
     the attach glyph is centred in a 38px button, so it sits ~10px in — at 4px
     the placeholder started ~6px to the LEFT of the glyph rail below it. */
  padding: 0 10px;
  font-size: 15px;
}
able-claw .aclaw-input::placeholder { color: var(--lg-ink-3); }
/* The typed line is BORDERLESS — the composer container is the field now.
   template-system-base ships html able-claw .aclaw-input (0,1,2) giving the
   textarea its own border + background + radius, which was right when the
   composer was one row and the input needed to read as a box inside a bar. In
   the two-row layout that renders a box inside a box (visible on the phone,
   where the container is narrow enough to make both edges obvious). The
   .aclaw-input-bar qualifier is (0,2,1) — two classes beats one class + two
   elements — so this wins without touching the system theme other pages use. */
able-claw .aclaw-input-bar .aclaw-input {
  background: transparent;
  border: 0;
  border-radius: 0;
}
/* Placeholder legibility. The base rule's opacity:0.55 COMPOUNDED with an
   already-translucent ink colour — measured 1.88:1 on the dark composer, far
   under WCAG AA — so pin ONE deliberate value at full alpha instead of
   stacking two. The .aclaw-input-bar qualifier (0,2,1) is the same trick the
   mobile anti-zoom rule uses to out-specify the Liquid-Glass layer, and
   --text-muted is semantic so it re-binds in light mode. */
able-claw .aclaw-input-bar .aclaw-input::placeholder {
  color: var(--text-muted, #94a3b8);
  opacity: 1;
}
able-claw .aclaw-send {
  width: 38px; height: 38px; padding: 0;
  border-radius: 50%;
  background: var(--lg-grad-user);
  color: var(--lg-user-ink);
  display: grid; place-items: center;
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / 0.22),
    0 2px 6px -2px oklch(52% 0.18 260 / 0.4);
}
able-claw .aclaw-send:hover:not(:disabled) {
  filter: brightness(1.05);
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / 0.22),
    0 4px 10px -2px oklch(52% 0.18 260 / 0.5);
}
able-claw .aclaw-send:active:not(:disabled) { transform: scale(0.94); }
able-claw .aclaw-send span { display: none; }
able-claw .aclaw-send i { font-size: 14px; }
able-claw .aclaw-stop span { display: none; }
able-claw .aclaw-stop { width: 38px; height: 38px; padding: 0; border-radius: 50%; }

/* Bug #92 — global :focus-visible for every interactive control inside
   able-claw. Many of our specific button rules set outline:none or
   border:none for visual polish, which kills keyboard focus indication.
   This rule reintroduces a single consistent focus ring (only when
   keyboard navigation triggers it, not on mouse-click) so keyboard
   users can always see where they are. */
able-claw button:focus-visible,
able-claw [role="button"]:focus-visible,
able-claw a:focus-visible,
able-claw [data-conversation-id]:focus-visible {
  outline: 2px solid var(--ableclaw-accent, #6366f1);
  outline-offset: 2px;
  border-radius: 6px;
}
able-claw textarea:focus-visible,
able-claw input:focus-visible {
  /* Native inputs already get a focus ring; align with the same accent. */
  outline: 2px solid var(--ableclaw-accent, #6366f1);
  outline-offset: 1px;
}

/* Footer meta */
able-claw .aclaw-input-footer {
  color: var(--lg-ink-3);
  font-size: 11.5px;
}

/* Helper / launcher compact header */
able-claw[data-mode="helper"] .aclaw-helper-header,
able-claw[data-mode="launcher"] .aclaw-helper-header {
  border-bottom: 1px solid var(--lg-line-soft);
}
able-claw .aclaw-helper-avatar {
  background: var(--lg-grad-agent-avatar);
  box-shadow: inset 0 1px 0 oklch(100% 0 0 / 0.25), 0 2px 4px oklch(0% 0 0 / 0.12);
}
able-claw .aclaw-helper-name { color: var(--lg-ink-1); }
able-claw .aclaw-helper-sub  { color: var(--lg-ink-3); }
able-claw .aclaw-helper-action:hover {
  background: var(--lg-mat-thin);
}

/* Online dot mimic in agent selector header (decorative) */
able-claw .aclaw-helper-avatar::after {
  content: '';
  position: relative;
  display: block;
  width: 0; height: 0;
}

/* Launcher pill — keep the dark pill from the previous design as it
   matches the user mock; only refine shadow + status dot color. */
able-claw[data-mode="launcher"] .aclaw-launcher-pill {
  box-shadow: var(--lg-shadow);
}
able-claw[data-mode="launcher"] .aclaw-launcher-pill-status {
  background: var(--lg-sys-green);
  box-shadow: 0 0 0 3px color-mix(in oklch, var(--lg-sys-green) 22%, transparent);
}
able-claw[data-mode="launcher"] > .aclaw-grid {
  border-radius: var(--lg-r-md);
  box-shadow: var(--lg-shadow-lg);
}

/* Channel chip in main-chat divider — bigger spacing on top. No bottom
   padding: the composer's own top margin is the whole gap to the thread. */
able-claw .aclaw-messages { padding: 20px 32px 0; }
able-claw[data-mode="helper"] .aclaw-messages,
able-claw[data-mode="launcher"] .aclaw-messages { padding: 14px 18px 8px; }

/* ─── Selection pill + the message ⋯ menu (contextual block actions) ──── */
.aclaw-selpill {
  position: fixed; z-index: 10050; display: flex; gap: 4px; padding: 4px;
  border: 1px solid var(--border-default, rgba(0,0,0,.14));
  border-radius: 10px; background: var(--surface-card, #fff);
  box-shadow: 0 8px 24px rgba(0,0,0,.16);
}
.aclaw-selpill button {
  border: 0; background: transparent; color: var(--text-primary, #111);
  font: inherit; font-size: 13px; font-weight: 600; padding: 0 10px;
  min-height: 34px; border-radius: 7px; cursor: pointer; white-space: nowrap;
}
@media (pointer: coarse) { .aclaw-selpill button { min-height: 44px; } }
@media (hover: hover) and (pointer: fine) {
  .aclaw-selpill button:hover { background: var(--surface-hover, rgba(0,0,0,.05)); }
}
.aclaw-message-speak {
  border: 0; background: transparent; cursor: pointer; color: inherit;
  line-height: 1; padding: 2px 5px; border-radius: 6px; vertical-align: middle;
}
.aclaw-message-speak.is-tts-loading { opacity: 1 !important; animation: aclaw-tts-pulse 1s ease-in-out infinite; }
.aclaw-message-speak.is-tts-playing { opacity: 1 !important; color: var(--accent-text, var(--accent, #2563eb)); }
@keyframes aclaw-tts-pulse { 0%,100% { opacity: .35; } 50% { opacity: 1; } }
@media (hover: hover) and (pointer: fine) {
  .aclaw-message-speak { opacity: 0; pointer-events: none; }
  .aclaw-message:hover .aclaw-message-speak,
  .aclaw-message-speak:focus-visible,
  .aclaw-message-speak.is-tts-playing,
  .aclaw-message-speak.is-tts-loading { opacity: 0.55; pointer-events: auto; }
  .aclaw-message-speak:hover { opacity: 1; background: var(--surface-hover, rgba(0,0,0,.06)); }
}
.aclaw-message-more {
  border: 0; background: transparent; cursor: pointer; color: inherit;
  font-size: 14px; line-height: 1; padding: 2px 6px; border-radius: 6px;
}
@media (hover: hover) and (pointer: fine) {
  .aclaw-message-more { opacity: 0; pointer-events: none; }
  .aclaw-message:hover .aclaw-message-more,
  .aclaw-message-more:focus-visible { opacity: 0.55; pointer-events: auto; }
  .aclaw-message-more:hover { opacity: 1; background: var(--surface-hover, rgba(0,0,0,.06)); }
}
.aclaw-msgmenu {
  position: fixed; z-index: 10050; min-width: 170px; padding: 6px;
  border: 1px solid var(--border-default, rgba(0,0,0,.14));
  border-radius: 10px; background: var(--surface-card, #fff);
  box-shadow: 0 8px 24px rgba(0,0,0,.16); display: flex; flex-direction: column;
}
.aclaw-msgmenu button {
  border: 0; background: transparent; color: var(--text-primary, #111);
  font: inherit; font-size: 13px; text-align: left; padding: 9px 10px;
  min-height: 36px; border-radius: 7px; cursor: pointer;
}
@media (hover: hover) and (pointer: fine) {
  .aclaw-msgmenu button:hover { background: var(--surface-hover, rgba(0,0,0,.05)); }
}

/* ─── Welcome screen + shortcuts (Liquid Glass) ─────────────────────── */

able-claw .aclaw-welcome {
  padding: 32px 24px 16px;
}
able-claw .aclaw-welcome-icon {
  width: 84px; height: 84px;
  background: var(--lg-grad-agent-avatar);
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / 0.25),
    0 12px 32px -8px color-mix(in oklch, var(--lg-sys-purple) 50%, transparent);
}
able-claw .aclaw-welcome-icon svg {
  width: 38px; height: 38px;
  filter: drop-shadow(0 2px 4px oklch(0% 0 0 / 0.18));
}
able-claw .aclaw-welcome-headline-h1 {
  font-size: 32px; font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--lg-ink-1);
}
able-claw .aclaw-welcome-subtitle {
  font-size: 14.5px;
  color: var(--lg-ink-3);
  letter-spacing: -0.008em;
  opacity: 1;
  margin-top: 4px;
}

/* Categorized shortcut grid */
able-claw .aclaw-welcome-categories {
  gap: 20px;
  margin-top: 16px;
}
able-claw .aclaw-welcome-category-header {
  font-size: 11px;
  letter-spacing: 0.10em;
  font-weight: 700;
  gap: 8px;
  padding: 4px 4px 10px;
}
able-claw .aclaw-welcome-category-icon {
  width: 24px; height: 24px;
  border-radius: 7px;
  font-size: 13px;
  box-shadow: inset 0 1px 0 oklch(100% 0 0 / 0.25), 0 2px 6px -2px oklch(0% 0 0 / 0.12);
}

/* The shortcut card — frosted glass, line-soft border, 14px radius,
   soft shadow, hover lift + category-colored accent ring. */
able-claw .aclaw-shortcut {
  background: var(--lg-mat-thick);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border: 1px solid var(--lg-line-soft);
  border-radius: 14px;
  color: var(--lg-ink-1);
  padding: 14px;
  gap: 8px;
  box-shadow: var(--lg-shadow-sm);
  transition: transform 120ms, background 140ms, border-color 140ms, box-shadow 160ms;
  position: relative;
}
able-claw .aclaw-shortcut:hover {
  transform: translateY(-1px);
  background: var(--lg-mat-hover);
  border-color: var(--lg-line);
  box-shadow: var(--lg-shadow);
}
able-claw .aclaw-shortcut:active { transform: translateY(0); }
able-claw .aclaw-shortcut-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px;
  border-radius: 9px;
  background: var(--lg-mat-thin);
  border: 1px solid var(--lg-line-soft);
  font-size: 13px;
  opacity: 1;
  color: var(--lg-ink-2);
  flex-shrink: 0;
  box-shadow: inset 0 1px 0 oklch(100% 0 0 / 0.12);
}
able-claw .aclaw-shortcut-icon i { line-height: 1; }
able-claw .aclaw-shortcut-category {
  display: none;  /* category is in the column header already; redundant per card */
}
able-claw .aclaw-shortcut-text {
  font-size: 13.5px;
  line-height: 1.4;
  letter-spacing: -0.008em;
  color: var(--lg-ink-1);
}

/* Category accent palettes — color tints injected into card hover state +
   category header. Each category overrides its own --cat-accent so the
   shortcut icon backplate, header color, and hover shadow all align. */
able-claw .aclaw-welcome-category--analyze {
  --cat-accent: oklch(64% 0.16 145);            /* green */
  --cat-accent-tint: color-mix(in oklch, oklch(64% 0.16 145) 12%, transparent);
}
able-claw .aclaw-welcome-category--monitor {
  --cat-accent: oklch(60% 0.18 245);            /* blue */
  --cat-accent-tint: color-mix(in oklch, oklch(60% 0.18 245) 12%, transparent);
}
able-claw .aclaw-welcome-category--act {
  --cat-accent: oklch(72% 0.18 75);             /* amber */
  --cat-accent-tint: color-mix(in oklch, oklch(72% 0.18 75) 14%, transparent);
}
able-claw .aclaw-welcome-category--report {
  --cat-accent: oklch(64% 0.20 25);             /* red-orange */
  --cat-accent-tint: color-mix(in oklch, oklch(64% 0.20 25) 12%, transparent);
}

/* Apply per-category accent to header + shortcut icon + hover state */
able-claw .aclaw-welcome-category .aclaw-welcome-category-header {
  color: var(--cat-accent, var(--lg-ink-2));
}
able-claw .aclaw-welcome-category .aclaw-welcome-category-icon {
  background: var(--cat-accent-tint, var(--lg-mat-thin));
  color: var(--cat-accent, var(--lg-ink-2));
  border: 1px solid color-mix(in oklch, var(--cat-accent, var(--lg-ink-3)) 18%, transparent);
}
able-claw .aclaw-welcome-category .aclaw-shortcut .aclaw-shortcut-icon {
  background: var(--cat-accent-tint, var(--lg-mat-thin));
  color: var(--cat-accent, var(--lg-ink-2));
  border-color: color-mix(in oklch, var(--cat-accent, var(--lg-ink-3)) 14%, transparent);
}
able-claw .aclaw-welcome-category .aclaw-shortcut:hover {
  border-color: color-mix(in oklch, var(--cat-accent, var(--lg-line)) 40%, transparent);
  box-shadow: var(--lg-shadow-sm),
              0 0 0 3px color-mix(in oklch, var(--cat-accent, var(--lg-sys-blue)) 10%, transparent);
}

/* "or What can I help you with?" helper text */
able-claw .aclaw-welcome-helper {
  font-size: 13px;
  color: var(--lg-ink-3);
  margin-top: 12px;
  font-style: italic;
}

/* ─── Agent selector popover (dropdown) ─────────────────────────────── */
able-claw .aclaw-agent-popover {
  background: var(--lg-mat-thick);
  backdrop-filter: saturate(180%) blur(28px);
  -webkit-backdrop-filter: saturate(180%) blur(28px);
  border: 1px solid var(--lg-line-soft);
  border-radius: var(--lg-r);
  box-shadow: var(--lg-shadow);
  padding: 6px;
  z-index: 50;
  min-width: 240px;
}
able-claw .aclaw-agent-option {
  border-radius: 10px;
  padding: 8px 10px;
}
able-claw .aclaw-agent-option:hover {
  background: var(--lg-mat-hover);
}
able-claw .aclaw-agent-option.is-active {
  background: var(--lg-mat-thick);
  box-shadow: inset 0 0 0 1px var(--lg-line);
}
able-claw .aclaw-agent-option .aclaw-agent-avatar {
  background: var(--lg-grad-agent-avatar);
  box-shadow: inset 0 1px 0 oklch(100% 0 0 / 0.20);
}
able-claw .aclaw-agent-option .aclaw-agent-name { color: var(--lg-ink-1); }
able-claw .aclaw-agent-option .aclaw-agent-tagline { color: var(--lg-ink-3); }

/* ─── Stop button — visible square icon on red circle ──────────────── */
able-claw .aclaw-stop {
  /* Center the icon explicitly — inline-flex inherits justify-content
     flex-start by default, which left-justified the FA glyph inside the
     38px circle. align-items already covers vertical centering. */
  display: inline-flex !important;
  align-items: center;
  justify-content: center;
  gap: 0;
  padding: 0;
  background: oklch(60% 0.20 25);  /* sys-red */
  color: white;
  box-shadow:
    inset 0 1px 0 oklch(100% 0 0 / 0.18),
    0 2px 6px -2px oklch(60% 0.20 25 / 0.4);
}
able-claw .aclaw-stop:hover {
  background: oklch(54% 0.22 25);
}
able-claw .aclaw-stop i {
  font-size: 12px;
  color: white;
  display: inline-block;
  line-height: 1;
}

/* ─── Typing indicator (3 animated dots + live phase/timer) for empty assistant bubble ── */
able-claw .aclaw-message-bubble--pending {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  min-width: 56px;
  padding: 12px 16px;
}
/* The live phase verb + elapsed clock beside the dots (iOS WorkingIndicator parity). */
able-claw .aclaw-typing-status {
  font-size: 13px;
  line-height: 1.3;
  color: var(--text-muted, #6b7280);
  animation: aclaw-status-blink 1.8s ease-in-out infinite;
}
able-claw .aclaw-typing-status .aclaw-status-clock { animation: none; }
@media (prefers-reduced-motion: reduce) { able-claw .aclaw-typing-status { animation: none; } }
able-claw .aclaw-typing {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  line-height: 1;
}
able-claw .aclaw-typing > span {
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--lg-ink-3);
  opacity: 0.5;
  animation: aclaw-typing-bounce 1.2s infinite ease-in-out;
}
able-claw .aclaw-typing > span:nth-child(2) { animation-delay: 0.15s; }
able-claw .aclaw-typing > span:nth-child(3) { animation-delay: 0.30s; }
@keyframes aclaw-typing-bounce {
  0%, 80%, 100% { transform: translateY(0); opacity: 0.4; }
  40%           { transform: translateY(-3px); opacity: 0.95; }
}
/* Reasoning phase (the reasoning_state "start" event) — the model's native thinking window.
   COLOR ONLY: no size, no spacing, no new node, so the swap to the reply cannot shift
   layout. The point is that "the provider is generating" and "the provider has sent us
   nothing" stopped looking identical — measured at up to 230s, and 196s on a live
   fryaway turn. Both tokens are semantic, so this flips correctly in dark mode. */
able-claw[data-reasoning="true"] .aclaw-typing > span { background: var(--accent, #1b2a4a); }
able-claw[data-reasoning="true"] .aclaw-typing-status { color: var(--text-secondary, #3f3f46); }

/* Error bubble (stream / HTTP failure) — danger-tinted card with retry
   affordance. Replaces the failed assistant turn in-place. */
able-claw .aclaw-message-bubble--error {
  background: color-mix(in srgb, #b91c1c 8%, var(--lg-mat-thick));
  border: 1px solid color-mix(in srgb, #b91c1c 28%, transparent);
  color: var(--lg-ink-1);
}
able-claw .aclaw-error {
  display: flex; align-items: flex-start; gap: 10px;
}
able-claw .aclaw-error > .fa-circle-exclamation {
  color: #b91c1c;
  font-size: 16px;
  margin-top: 1px;
  flex-shrink: 0;
}
able-claw .aclaw-error-body { flex: 1; min-width: 0; }
able-claw .aclaw-error-title {
  font-weight: 600; font-size: 13px;
  color: #b91c1c;
  margin-bottom: 2px;
}
able-claw .aclaw-error-msg {
  font-size: 13px;
  color: var(--lg-ink-2);
  line-height: 1.45;
  margin-bottom: 8px;
  word-wrap: break-word;
}
able-claw .aclaw-error-retry {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 12px;
  border-radius: 999px;
  background: var(--lg-grad-user);
  color: var(--lg-user-ink);
  border: 0;
  font-size: 12px; font-weight: 600;
  cursor: pointer;
  transition: filter 120ms, transform 120ms;
}
able-claw .aclaw-error-retry:hover { filter: brightness(1.05); }
able-claw .aclaw-error-retry:active { transform: scale(0.97); }
able-claw .aclaw-error-retry i { font-size: 11px; }
/* TURN RESUME — the re-run affordance an interrupted turn earns. It sits in the
   message meta line beside the "interrupted" badge (not inside an error bubble),
   because the turn's partial output is real content the user keeps. */
able-claw .aclaw-turn-retry {
  padding: 1px 8px;
  font-size: 9.5px;
  letter-spacing: 0.04em; text-transform: uppercase;
  margin-left: 6px;
  vertical-align: middle;
}
able-claw .aclaw-turn-retry i { font-size: 9px; }
able-claw .aclaw-interrupted-empty { margin: 0; opacity: 0.75; font-style: italic; }

/* ═══════════════════════════════════════════════════════════════════════════
 * Browser compat fallbacks
 *
 * oklch() and color-mix() are Chrome 111+ / Safari 15.4+ / Firefox 113+
 * (May 2023). For older browsers, fall back to neutral hex tokens that
 * approximate the iOS Liquid Glass palette. Without these fallbacks the
 * page renders with "unset" values — flash of unstyled content + broken
 * layout colors.
 *
 * The @supports query reverses the logic: if the browser does NOT
 * understand oklch(), apply hex overrides. Modern browsers ignore the
 * block; older ones use the hex defaults.
 * ═══════════════════════════════════════════════════════════════════════ */

@supports not (color: oklch(0% 0 0)) {
  able-claw {
    /* Light defaults — hex approximations of the oklch tokens */
    --lg-wp-1: #f3f5f9;
    --lg-wp-2: #e6ebf3;
    --lg-wp-3: #ebeef5;
    --lg-ink-1: #1f2937;
    --lg-ink-2: #475569;
    --lg-ink-3: #6b7280;
    --lg-ink-4: #9ca3af;
    --lg-mat-thick:   rgba(255, 255, 255, 0.75);
    --lg-mat-regular: rgba(255, 255, 255, 0.60);
    --lg-mat-thin:    rgba(255, 255, 255, 0.40);
    --lg-mat-ultra:   rgba(255, 255, 255, 0.22);
    --lg-mat-hover:   rgba(255, 255, 255, 0.85);
    --lg-line:        rgba(31, 41, 55, 0.09);
    --lg-line-soft:   rgba(31, 41, 55, 0.05);
    --lg-line-strong: rgba(31, 41, 55, 0.18);
    --lg-sys-blue:    #3b82f6;
    --lg-sys-blue-2:  #2563eb;
    --lg-sys-green:   #10b981;
    --lg-sys-purple:  #8b5cf6;
    --lg-grad-user: linear-gradient(135deg, #3b82f6, #6366f1);
    --lg-grad-agent-avatar: linear-gradient(135deg, #8b5cf6, #2563eb);
    --lg-user-ink: #ffffff;
  }
  [data-theme="dark"] able-claw {
    --lg-wp-1: #1a202f;
    --lg-wp-2: #1f2937;
    --lg-wp-3: #1e293b;
    --lg-ink-1: #f8fafc;
    --lg-ink-2: #cbd5e1;
    --lg-ink-3: #94a3b8;
    --lg-ink-4: #64748b;
    --lg-mat-thick:   rgba(40, 48, 60, 0.75);
    --lg-mat-regular: rgba(43, 51, 65, 0.60);
    --lg-mat-thin:    rgba(49, 58, 73, 0.42);
    --lg-mat-ultra:   rgba(55, 65, 81, 0.24);
    --lg-mat-hover:   rgba(52, 61, 76, 0.75);
    --lg-line:        rgba(255, 255, 255, 0.10);
    --lg-line-soft:   rgba(255, 255, 255, 0.06);
    --lg-line-strong: rgba(255, 255, 255, 0.22);
    --lg-sys-blue:    #60a5fa;
    --lg-sys-blue-2:  #3b82f6;
    --lg-sys-green:   #34d399;
    --lg-sys-purple:  #a78bfa;
    --lg-grad-user: linear-gradient(135deg, #60a5fa, #6366f1);
  }
}

/* =================================================================
 * ABLEUI Chat Output Classes — Token-efficient HTML for chat bubbles.
 * Scoped under .aclaw-message-bubble so they only apply to LLM output.
 * Sanitizer strips inline style="..."; classes survive.
 * Wave-2 retokenization (C-spec §2.3): semantic accents ride the PLATFORM
 * tokens — s=var(--success) · i=var(--info) · w=var(--warning) ·
 * d=var(--danger) · b=var(--accent) — washes are color-mix over transparent,
 * gradient partners are color-mix toward white/black, and the deliberately
 * DARK editorial surfaces (hero / card--dark) derive from var(--info) over
 * black. Dark mode is BY CONSTRUCTION: tokens re-bind, greys invert via the
 * able-claw dark block; the only fixed hues left are the avatar identity
 * tones (oklch — decorative, mode-independent like badges) + true neutrals.
 * Tones via data-tone="s|i|w|d|b|n" — short attrs save tokens on
 * multiplied components (pills, cells).
 * ================================================================= */

/* READING SIZE — 16px at EVERY width (desktop is the primary reading surface;
   the reference bar is ChatGPT web: 16px / ~1.7 in a ~48rem column). This rule
   is (0,2,0) so it beats the (0,1,1) 'able-claw .aclaw-message-bubble' bubble
   rule below/above it — the ladder underneath (muted / eyebrow / chips / table)
   is sized RELATIVE to this 16px base, so raise them together, never alone. */
.aclaw-message-bubble .claw-root {
  display: grid; gap: 14px;
  font-family: var(--font-sans, system-ui, sans-serif);
  color: var(--text-primary, inherit);
  line-height: 1.65; font-size: 16px;
  animation: claw-fade-in 0.3s ease-out;
}
.aclaw-message-bubble .claw-root--editorial {
  display: grid; gap: 24px;
  font-family: var(--font-serif, Georgia, serif);
  color: var(--text-primary, inherit);
  line-height: 1.7; font-size: 15px;
  animation: claw-fade-in 0.4s ease-out;
}
/* width-law-v1 companion (same disease as .aclaw-chat, one level deeper):
   these are single-column grids whose column track was IMPLICIT (auto-sized)
   — a wide table's min-content inflated the track past the bubble (measured:
   a 381px track inside a 315px root on a 375px phone → the grid card bled
   36px past the viewport). minmax(0,1fr) pins the track to the container so
   the card fills the bubble and the table scrolls inside its own
   overflow-x wrap instead of bleeding. */
.aclaw-message-bubble .claw-root,
.aclaw-message-bubble .claw-root--editorial,
.aclaw-message-bubble .claw-section { grid-template-columns: minmax(0, 1fr); }

.aclaw-message-bubble .claw-h1 {
  margin: 0;
  font-family: var(--font-serif, Georgia, serif);
  font-size: 36px; font-weight: 800;
  color: var(--grey-900, inherit);
  letter-spacing: -1px; line-height: 1.05;
}
.aclaw-message-bubble .claw-h2 {
  margin: 0;
  font-size: 22px; font-weight: 700;
  color: var(--grey-900, inherit);
  letter-spacing: -0.4px; line-height: 1.15;
}
.aclaw-message-bubble .claw-h3 {
  margin: 0;
  font-size: 16px; font-weight: 600;
  color: var(--grey-900, inherit);
  line-height: 1.3;
}
.aclaw-message-bubble .claw-h4 {
  margin: 0;
  font-size: 14px; font-weight: 600;
  color: var(--grey-900, inherit);
}

.aclaw-message-bubble .claw-eyebrow {
  margin: 0;
  font-size: 12px; font-weight: 700;
  letter-spacing: 1.6px; text-transform: uppercase;
  color: var(--accent-text, var(--accent));
}
.aclaw-message-bubble .claw-eyebrow[data-tone="s"] { color: var(--success); }
.aclaw-message-bubble .claw-eyebrow[data-tone="i"] { color: var(--info); }
.aclaw-message-bubble .claw-eyebrow[data-tone="w"] { color: var(--warning); }
.aclaw-message-bubble .claw-eyebrow[data-tone="d"] { color: var(--danger); }
.aclaw-message-bubble .claw-eyebrow[data-tone="n"] { color: var(--grey-500, #66686b); }
.aclaw-message-bubble .claw-eyebrow--live::before {
  content: "";
  display: inline-block;
  width: 6px; height: 6px;
  border-radius: 50%;
  background: currentColor;
  margin-right: 6px;
  vertical-align: 1px;
  animation: claw-pulse 2s infinite;
}

.aclaw-message-bubble .claw-body {
  margin: 0;
  color: var(--text-primary, inherit);
  line-height: 1.65;
}
.aclaw-message-bubble .claw-body--editorial {
  margin: 0;
  font-family: var(--font-serif, Georgia, serif);
  font-size: 15px; line-height: 1.7;
  color: var(--grey-800, inherit);
}
.aclaw-message-bubble .claw-body--muted {
  margin: 0;
  font-size: 14px;
  color: var(--grey-500, #66686b);
}

.aclaw-message-bubble .claw-dropcap {
  float: left;
  font-family: var(--font-serif, Georgia, serif);
  font-size: 56px; font-weight: 900;
  line-height: 0.9;
  padding: 6px 10px 0 0;
  color: var(--accent-text, var(--accent));
}

.aclaw-message-bubble .claw-pullquote {
  margin: 8px 0;
  padding: 16px 24px;
  border-left: 3px solid var(--accent);
  background: linear-gradient(90deg, color-mix(in srgb, var(--accent) 6%, transparent), transparent);
  font-family: var(--font-serif, Georgia, serif);
  font-style: italic;
  font-size: 18px; line-height: 1.5;
  color: var(--grey-900, inherit);
}
.aclaw-message-bubble .claw-pullquote cite {
  display: block; margin-top: 8px;
  font-family: var(--font-sans, system-ui, sans-serif);
  font-style: normal;
  font-size: 12px;
  color: var(--grey-500, #66686b);
  letter-spacing: 0.5px;
}

.aclaw-message-bubble .claw-ul {
  margin: 0; padding-left: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.aclaw-message-bubble .claw-ul > li {
  position: relative;
  padding-left: 26px;
  line-height: 1.55;
}
.aclaw-message-bubble .claw-ul > li::before {
  content: "→";
  position: absolute;
  left: 0; top: 0;
  color: var(--accent-text, var(--accent));
  font-weight: 700;
  font-size: 16px;
  line-height: 1.55;
}
.aclaw-message-bubble .claw-ol {
  margin: 0; padding-left: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 12px;
  counter-reset: step;
}
.aclaw-message-bubble .claw-ol > li {
  position: relative;
  padding-left: 40px;
  min-height: 26px;
  counter-increment: step;
  line-height: 1.55;
}
.aclaw-message-bubble .claw-ol > li::before {
  content: counter(step);
  position: absolute;
  left: 0; top: 0;
  display: grid; place-items: center;
  width: 26px; height: 26px;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--accent), color-mix(in srgb, var(--accent) 75%, white));
  color: var(--on-accent, #fff);
  font-size: 12px; font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.aclaw-message-bubble .claw-2col {
  display: grid;
  grid-template-columns: 2fr 1fr;
  gap: 24px;
  align-items: start;
}
.aclaw-message-bubble .claw-sidebar {
  display: grid; gap: 8px;
  padding: 16px;
  background: var(--grey-100, rgba(127,127,127,0.06));
  border-radius: 8px;
  border: 1px solid var(--grey-200, rgba(127,127,127,0.16));
}
.aclaw-message-bubble .claw-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 12px;
}
.aclaw-message-bubble .claw-hr {
  border: 0;
  border-top: 1px solid var(--grey-300, rgba(127,127,127,0.20));
  margin: 4px 0;
}
.aclaw-message-bubble .claw-hr--gradient {
  border: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--grey-300, rgba(127,127,127,0.32)) 20%, var(--grey-300, rgba(127,127,127,0.32)) 80%, transparent);
  margin: 16px 0;
}
.aclaw-message-bubble .claw-section { display: grid; gap: 14px; }

.aclaw-message-bubble .claw-kpis {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 12px;
}
.aclaw-message-bubble .claw-kpi {
  display: grid; gap: 4px;
  padding: 16px 18px;
  background: var(--grey-0, #fff);
  border: 1px solid var(--grey-300, rgba(127,127,127,0.20));
  border-radius: 10px;
  border-top: 3px solid var(--accent);
  box-shadow: 0 1px 3px rgba(0,0,0,0.04);
}
.aclaw-message-bubble .claw-kpi[data-tone="s"] { border-top-color: var(--success); }
.aclaw-message-bubble .claw-kpi[data-tone="i"] { border-top-color: var(--info); }
.aclaw-message-bubble .claw-kpi[data-tone="w"] { border-top-color: var(--warning); }
.aclaw-message-bubble .claw-kpi[data-tone="d"] { border-top-color: var(--danger); }
.aclaw-message-bubble .claw-kpi[data-tone="b"] { border-top-color: var(--accent); }
/* When the KPI itself is a danger/success metric, the VALUE inherits the tone color.
   The label stays muted (it's a label, not the metric).
   For neutral tone with semantic value coloring, use claw-kpi__value[data-tone=...] directly. */
.aclaw-message-bubble .claw-kpi[data-tone="d"] .claw-kpi__value { color: var(--danger); }
.aclaw-message-bubble .claw-kpi[data-tone="s"] .claw-kpi__value { color: var(--success); }
.aclaw-message-bubble .claw-kpi[data-tone="w"] .claw-kpi__value { color: var(--warning); }
.aclaw-message-bubble .claw-kpi__value[data-tone="d"] { color: var(--danger); }
.aclaw-message-bubble .claw-kpi__value[data-tone="s"] { color: var(--success); }
.aclaw-message-bubble .claw-kpi__value[data-tone="w"] { color: var(--warning); }
.aclaw-message-bubble .claw-kpi__label {
  font-size: 10px; font-weight: 700;
  letter-spacing: 1.4px; text-transform: uppercase;
  color: var(--grey-500, #66686b);
}
.aclaw-message-bubble .claw-kpi__value {
  font-size: 26px; font-weight: 800;
  color: var(--grey-900, inherit);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.5px; line-height: 1.1;
}
.aclaw-message-bubble .claw-kpi__delta {
  font-size: 11px; font-weight: 600;
  display: inline-flex; align-items: center; gap: 4px;
  color: var(--grey-500, #66686b);
}
.aclaw-message-bubble .claw-kpi__delta[data-dir="up"]   { color: var(--success); }
.aclaw-message-bubble .claw-kpi__delta[data-dir="down"] { color: var(--danger); }
.aclaw-message-bubble .claw-kpi__delta[data-dir="up"]::before   { content: "↑ "; font-size: 14px; line-height: 1; }
.aclaw-message-bubble .claw-kpi__delta[data-dir="down"]::before { content: "↓ "; font-size: 14px; line-height: 1; }

/* Hero — a DELIBERATELY dark editorial surface in both modes; the deep navy
   derives from the info token over black (no raw hex), white text stays. */
.aclaw-message-bubble .claw-hero {
  display: grid; gap: 6px;
  padding: 28px 32px;
  background: linear-gradient(135deg, color-mix(in srgb, var(--info) 14%, black) 0%, color-mix(in srgb, var(--info) 60%, black) 100%);
  border-radius: 16px;
  color: #fff;
  position: relative; overflow: hidden;
}
.aclaw-message-bubble .claw-hero .claw-kpi__label { color: rgba(255,255,255,0.6); }
.aclaw-message-bubble .claw-hero__value {
  font-family: var(--font-serif, Georgia, serif);
  font-size: 48px; font-weight: 800;
  color: #fff;
  font-variant-numeric: tabular-nums;
  letter-spacing: -1.4px; line-height: 1;
}
.aclaw-message-bubble .claw-hero .claw-kpi__delta { color: rgba(255,255,255,0.75); }
.aclaw-message-bubble .claw-hero .claw-kpi__delta[data-dir="up"]   { color: color-mix(in srgb, var(--success) 55%, white); }
.aclaw-message-bubble .claw-hero .claw-kpi__delta[data-dir="down"] { color: color-mix(in srgb, var(--danger) 45%, white); }

.aclaw-message-bubble .claw-pill {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 9px;
  font-size: 11px; font-weight: 600;
  line-height: 1.4;
  border-radius: 12px;
  background: var(--grey-200, rgba(127,127,127,0.12));
  color: var(--grey-700, inherit);
  border: 1px solid var(--grey-300, rgba(127,127,127,0.20));
}
/* A pill INSIDE running prose must fit the line box (P2, 2026-08-03 — Recovr
   conv f88203cb, caught by the visual-review pass and confirmed by measurement).
   The standalone pill is 23.4px tall (11px x 1.4 line-height + 3px padding-block
   + 1px border, both sides) while body prose runs a 21.7px line box (14px x
   1.55). Inline in a sentence it therefore OUTGREW its line: the paragraph's
   line rects measured [17, 17, …, 23.4, 13, …, 23.4, 13, …] — every line
   carrying a pill jumped ~2px and fragmented, so the leading visibly stuttered
   mid-paragraph ("…it sits at [OK] today", "[TEMPORARILY ON HOLD] with a $0
   credit limit"). That is the same incoherence class as the callout measure:
   type furniture disturbing the reading rhythm.
   Only the padding-block shrinks (3px → 1px → 19.4px total, comfortably inside
   21.7px). Font-size, weight, radius, wash and border are untouched, so the pill
   still reads as a pill; baseline alignment is kept because the pill sits ON the
   text baseline. Scope is prose only (p / li) — a pill in a table cell, a card
   row or a standalone strip keeps its full 23.4px presence. */
.aclaw-message-bubble :is(p, li) .claw-pill { padding-block: 1px; }
/* Toned pills — token WASHES (not fixed pastels), so light and dark both
   derive from the semantic token; the hand-maintained dark twins are GONE. */
.aclaw-message-bubble .claw-pill[data-tone="s"] { background: color-mix(in srgb, var(--success) 10%, transparent); color: var(--success); border-color: color-mix(in srgb, var(--success) 34%, transparent); }
.aclaw-message-bubble .claw-pill[data-tone="i"] { background: color-mix(in srgb, var(--info) 10%, transparent); color: var(--info); border-color: color-mix(in srgb, var(--info) 34%, transparent); }
.aclaw-message-bubble .claw-pill[data-tone="w"] { background: color-mix(in srgb, var(--warning) 10%, transparent); color: var(--warning); border-color: color-mix(in srgb, var(--warning) 34%, transparent); }
.aclaw-message-bubble .claw-pill[data-tone="d"] { background: color-mix(in srgb, var(--danger) 10%, transparent); color: var(--danger); border-color: color-mix(in srgb, var(--danger) 34%, transparent); }
.aclaw-message-bubble .claw-pill[data-tone="b"] { background: color-mix(in srgb, var(--accent) 10%, transparent); color: var(--accent-text, var(--accent)); border-color: color-mix(in srgb, var(--accent) 34%, transparent); }
.aclaw-message-bubble .claw-pill__dot {
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--grey-500, #66686b);
}
.aclaw-message-bubble .claw-pill[data-tone="s"] .claw-pill__dot { background: var(--success); }
.aclaw-message-bubble .claw-pill[data-tone="i"] .claw-pill__dot { background: var(--info); }
.aclaw-message-bubble .claw-pill[data-tone="w"] .claw-pill__dot { background: var(--warning); }
.aclaw-message-bubble .claw-pill[data-tone="d"] .claw-pill__dot { background: var(--danger); }
.aclaw-message-bubble .claw-pill[data-tone="b"] .claw-pill__dot { background: var(--accent); }
.aclaw-message-bubble .claw-pill[data-live] .claw-pill__dot { animation: claw-pulse 2s infinite; }

.aclaw-message-bubble .claw-tbl {
  overflow-x: auto;
  border: 1px solid var(--grey-300, rgba(127,127,127,0.20));
  border-radius: 10px;
  box-shadow: 0 1px 3px rgba(0,0,0,0.04);
}
/* PAN, don't crush. A bare width:100% let a 6-column table compress to ~42px
   columns inside the .claw-tbl scroller (measured at 375px). max-content sizes
   the table to its CONTENT and min-width:100% still fills the bubble when the
   content is narrower — so narrow tables look unchanged and wide ones scroll
   inside .claw-tbl instead of shrinking their own text to nothing. */
.aclaw-message-bubble .claw-tbl table {
  width: max-content;
  min-width: 100%;
  border-collapse: collapse;
  font-size: 14px;
}
.aclaw-message-bubble .claw-tbl thead tr {
  background: var(--grey-100, rgba(127,127,127,0.06));
}
.aclaw-message-bubble .claw-th {
  padding: 11px 14px;
  text-align: left;
  font-size: 12px; font-weight: 700;
  letter-spacing: 1.2px; text-transform: uppercase;
  color: var(--grey-700, inherit);
  border-bottom: 1px solid var(--grey-300, rgba(127,127,127,0.20));
}
.aclaw-message-bubble .claw-th[data-num] { text-align: right; font-variant-numeric: tabular-nums; }
.aclaw-message-bubble .claw-td {
  padding: 11px 14px;
  color: var(--grey-900, inherit);
  border-bottom: 1px solid var(--grey-200, rgba(127,127,127,0.12));
  vertical-align: middle;
}
.aclaw-message-bubble .claw-tbl tbody tr:last-child .claw-td { border-bottom: 0; }
.aclaw-message-bubble .claw-td[data-c="num"]     { text-align: right; font-variant-numeric: tabular-nums; font-weight: 600; }
.aclaw-message-bubble .claw-td[data-c="money"]   { text-align: right; font-variant-numeric: tabular-nums; font-weight: 600; }
.aclaw-message-bubble .claw-td[data-c="muted"]   { color: var(--grey-500, #66686b); }
.aclaw-message-bubble .claw-td[data-c="loss"]    { color: var(--danger); font-weight: 600; text-align: right; font-variant-numeric: tabular-nums; }
.aclaw-message-bubble .claw-td[data-c="benefit"] { color: var(--success); font-weight: 600; text-align: right; font-variant-numeric: tabular-nums; }
.aclaw-message-bubble .claw-td[data-c="id"] {
  font-family: var(--font-mono, Menlo, monospace);
  font-size: 11px;
  color: var(--grey-500, #66686b);
}

/* The card is its own grid, and an IMPLICIT track is min-content-sized — a long
   unbroken token (an id, a URL) inflated the track past the card, and
   .claw-card--accent's overflow:hidden then CROPPED it silently (measured:
   scrollWidth 111 vs clientWidth 95 — 16px of content simply gone). Pin the
   track to the container and let text WRAP; nothing is ever clipped. Same cure
   as the minmax(0,1fr) on .claw-root/.claw-section above. */
.aclaw-message-bubble .claw-card {
  display: grid; gap: 8px;
  grid-template-columns: minmax(0, 1fr);
  min-width: 0;
  overflow-wrap: anywhere;
  padding: 16px;
  background: var(--grey-0, #fff);
  border: 1px solid var(--grey-300, rgba(127,127,127,0.20));
  border-radius: 10px;
  box-shadow: 0 1px 3px rgba(0,0,0,0.04);
}
.aclaw-message-bubble .claw-card__head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 8px;
}
/* Grid/flex children default to min-width:auto — without this the head's title
   refuses to shrink and pushes the trailing cell out of the card. */
.aclaw-message-bubble .claw-card__head > * { min-width: 0; }
.aclaw-message-bubble .claw-card--accent {
  position: relative; overflow: hidden;
  padding-top: 19px;
  border-top: 3px solid var(--accent);
}
.aclaw-message-bubble .claw-card--accent[data-tone="s"] { border-top-color: var(--success); }
.aclaw-message-bubble .claw-card--accent[data-tone="i"] { border-top-color: var(--info); }
.aclaw-message-bubble .claw-card--accent[data-tone="w"] { border-top-color: var(--warning); }
.aclaw-message-bubble .claw-card--accent[data-tone="d"] { border-top-color: var(--danger); }
.aclaw-message-bubble .claw-card--accent[data-tone="b"] { border-top-color: var(--accent); }
/* card--dark — a DELIBERATELY dark editorial surface in both modes; the deep
   navy derives from the info token over black (no raw hex). */
.aclaw-message-bubble .claw-card--dark {
  display: grid; gap: 12px;
  padding: 24px;
  background: linear-gradient(135deg, color-mix(in srgb, var(--info) 8%, black) 0%, color-mix(in srgb, var(--info) 60%, black) 100%);
  border-radius: 14px;
  color: #fff;
  position: relative; overflow: hidden;
}
.aclaw-message-bubble .claw-card--dark .claw-eyebrow { color: rgba(255,255,255,0.6); }
.aclaw-message-bubble .claw-card--dark .claw-h3,
.aclaw-message-bubble .claw-card--dark .claw-h4 { color: #fff; }
.aclaw-message-bubble .claw-card--dark .claw-kpi__label { color: rgba(255,255,255,0.5); }
.aclaw-message-bubble .claw-card--dark .claw-kpi__value { color: #fff; font-size: 18px; }
.aclaw-message-bubble .claw-card--dark .claw-body,
.aclaw-message-bubble .claw-card--dark .claw-body--muted { color: rgba(255,255,255,0.85); }

.aclaw-message-bubble .claw-ring {
  position: relative;
  width: 140px; height: 140px;
  border-radius: 50%;
  background: conic-gradient(var(--accent) 0% calc(var(--p, 0) * 1%), var(--grey-200, rgba(127,127,127,0.16)) calc(var(--p, 0) * 1%) 100%);
  display: grid; place-items: center;
}
.aclaw-message-bubble .claw-ring[data-tone="s"] { background: conic-gradient(var(--success) 0% calc(var(--p, 0) * 1%), var(--grey-200, rgba(127,127,127,0.16)) calc(var(--p, 0) * 1%) 100%); }
.aclaw-message-bubble .claw-ring[data-tone="w"] { background: conic-gradient(var(--warning) 0% calc(var(--p, 0) * 1%), var(--grey-200, rgba(127,127,127,0.16)) calc(var(--p, 0) * 1%) 100%); }
.aclaw-message-bubble .claw-ring[data-tone="d"] { background: conic-gradient(var(--danger) 0% calc(var(--p, 0) * 1%), var(--grey-200, rgba(127,127,127,0.16)) calc(var(--p, 0) * 1%) 100%); }
.aclaw-message-bubble .claw-ring__c {
  position: absolute;
  inset: 12px;
  background: var(--grey-0, #fff);
  border-radius: 50%;
  display: grid; place-items: center;
  text-align: center;
}

.aclaw-message-bubble .claw-bar {
  height: 8px;
  background: var(--grey-200, rgba(127,127,127,0.16));
  border-radius: 4px;
  overflow: hidden;
}
.aclaw-message-bubble .claw-bar__fill {
  height: 100%;
  background: linear-gradient(90deg, var(--success), color-mix(in srgb, var(--success) 55%, white));
  border-radius: 4px;
}
.aclaw-message-bubble .claw-bar__fill[data-tone="i"] { background: linear-gradient(90deg, var(--info), color-mix(in srgb, var(--info) 50%, white)); }
.aclaw-message-bubble .claw-bar__fill[data-tone="w"] { background: linear-gradient(90deg, var(--warning), color-mix(in srgb, var(--warning) 60%, white)); }
.aclaw-message-bubble .claw-bar__fill[data-tone="d"] { background: linear-gradient(90deg, var(--danger), color-mix(in srgb, var(--danger) 50%, white)); }
.aclaw-message-bubble .claw-bar__fill[data-tone="b"] { background: linear-gradient(90deg, var(--accent), color-mix(in srgb, var(--accent) 70%, white)); }

.aclaw-message-bubble .claw-stages {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  gap: 4px;
}
.aclaw-message-bubble .claw-stage {
  height: 6px;
  border-radius: 3px;
  background: var(--grey-200, rgba(127,127,127,0.16));
  border: 1px solid var(--grey-300, rgba(127,127,127,0.20));
  font-size: 0;
}
.aclaw-message-bubble .claw-stage[data-state="done"]    { background: var(--success); border-color: transparent; }
.aclaw-message-bubble .claw-stage[data-state="active"]  { background: linear-gradient(90deg, var(--warning), color-mix(in srgb, var(--warning) 60%, white)); border-color: transparent; animation: claw-shimmer 2s infinite; }
.aclaw-message-bubble .claw-stage[data-state="pending"] { background: var(--grey-200, rgba(127,127,127,0.16)); }

.aclaw-message-bubble .claw-spark { width: 100%; height: 32px; display: block; }
.aclaw-message-bubble .claw-donut { width: 120px; height: 120px; display: block; }

.aclaw-message-bubble .claw-tl {
  position: relative;
  padding-left: 32px;
  display: grid;
  gap: 18px;
}
.aclaw-message-bubble .claw-tl__line {
  position: absolute;
  left: 9px; top: 8px; bottom: 8px;
  width: 2px;
  background: linear-gradient(180deg, var(--success) 0%, var(--accent) 50%, var(--grey-300, rgba(127,127,127,0.32)) 100%);
  border-radius: 1px;
}
.aclaw-message-bubble .claw-tl > div { position: relative; display: grid; gap: 4px; }
.aclaw-message-bubble .claw-tl__dot {
  position: absolute;
  left: -29px; top: 4px;
  width: 12px; height: 12px;
  border-radius: 50%;
  background: var(--grey-0, #fff);
  border: 3px solid var(--grey-400, #bbbec3);
}
.aclaw-message-bubble .claw-tl__dot[data-state="done"]   { border-color: var(--success); box-shadow: 0 0 0 4px color-mix(in srgb, var(--success) 12%, transparent); }
.aclaw-message-bubble .claw-tl__dot[data-state="next"]   { border-color: var(--warning); box-shadow: 0 0 0 4px color-mix(in srgb, var(--warning) 12%, transparent); animation: claw-pulse 2s infinite; }
.aclaw-message-bubble .claw-tl__dot[data-state="active"] { border-color: var(--accent); box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 12%, transparent); }

/* w10/C10 — the callout family on SEMANTIC tokens (dark mode by construction:
   the tokens re-bind, the washes are color-mix over transparent, so no
   hand-maintained dark twin exists for callouts anymore). */
.aclaw-message-bubble .claw-callout {
  padding: 14px 18px;
  border-left: 3px solid var(--info);
  border-radius: 6px;
  background: linear-gradient(90deg, color-mix(in srgb, var(--info) 8%, transparent), transparent);
}
.aclaw-message-bubble .claw-callout > p { margin: 0; font-size: 13px; line-height: 1.55; color: var(--info); }
.aclaw-message-bubble .claw-callout[data-tone="s"]      { border-left-color: var(--success); background: linear-gradient(90deg, color-mix(in srgb, var(--success) 8%, transparent), transparent); }
.aclaw-message-bubble .claw-callout[data-tone="s"] > p  { color: var(--success); }
.aclaw-message-bubble .claw-callout[data-tone="w"]      { border-left-color: var(--warning); background: linear-gradient(90deg, color-mix(in srgb, var(--warning) 8%, transparent), transparent); }
.aclaw-message-bubble .claw-callout[data-tone="w"] > p  { color: var(--warning); }
.aclaw-message-bubble .claw-callout[data-tone="d"]      { border-left-color: var(--danger); background: linear-gradient(90deg, color-mix(in srgb, var(--danger) 8%, transparent), transparent); }
.aclaw-message-bubble .claw-callout[data-tone="d"] > p  { color: var(--danger); }

/* ─── Dark-theme catalog overrides ──────────────────────────────────────
 * EMPTY by construction (Wave 2, C-spec §2.3): every toned family above rides
 * the semantic tokens (which re-bind in dark) + color-mix washes over
 * transparent, and the neutral greys invert via the able-claw dark block —
 * so no hand-maintained dark twin remains. Add one back ONLY for deliberate
 * dark-tuning, never for parity. */

.aclaw-message-bubble .claw-suggestions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 4px;
}
.aclaw-message-bubble .claw-chip {
  padding: 7px 14px;
  font-size: 13px; font-weight: 600;
  color: var(--grey-900, inherit);
  background: var(--grey-0, #fff);
  border: 1px solid var(--grey-300, rgba(127,127,127,0.20));
  border-radius: 18px;
  cursor: pointer;
  box-shadow: 0 1px 2px rgba(0,0,0,0.04);
  font-family: inherit;
}
.aclaw-message-bubble .claw-chip:hover {
  background: var(--grey-100, rgba(127,127,127,0.06));
}

.aclaw-message-bubble .claw-person {
  display: grid;
  grid-template-columns: 48px 1fr;
  gap: 14px;
  align-items: center;
  padding: 14px;
  background: var(--grey-0, #fff);
  border: 1px solid var(--grey-300, rgba(127,127,127,0.20));
  border-radius: 10px;
}
/* Avatars — the default rides the brand accent; the five NAMED tones are
   decorative IDENTITY hues (a person keeps their color in both modes), so
   they are fixed oklch — mode-independent by design, like badges. */
.aclaw-message-bubble .claw-avatar {
  width: 48px; height: 48px;
  border-radius: 50%;
  display: grid; place-items: center;
  color: #fff;
  font-size: 16px; font-weight: 700;
  background: linear-gradient(135deg, var(--accent), color-mix(in srgb, var(--accent) 72%, white));
}
.aclaw-message-bubble .claw-avatar[data-tone="teal"]  { background: linear-gradient(135deg, oklch(70% 0.12 215), oklch(61% 0.11 220)); }
.aclaw-message-bubble .claw-avatar[data-tone="pink"]  { background: linear-gradient(135deg, oklch(66% 0.21 356), oklch(64% 0.21 16)); }
.aclaw-message-bubble .claw-avatar[data-tone="amber"] { background: linear-gradient(135deg, oklch(76% 0.16 70), oklch(70% 0.19 48)); }
.aclaw-message-bubble .claw-avatar[data-tone="green"] { background: linear-gradient(135deg, oklch(70% 0.15 163), oklch(60% 0.13 164)); }
.aclaw-message-bubble .claw-avatar[data-tone="slate"] { background: linear-gradient(135deg, oklch(55% 0.03 255), oklch(68% 0.03 255)); }

.aclaw-message-bubble .claw-compare {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
}

.aclaw-message-bubble .claw-link {
  color: var(--info);
  text-decoration: none;
  font-weight: 500;
  border-bottom: 1px solid color-mix(in srgb, var(--info) 30%, transparent);
}
.aclaw-message-bubble .claw-link:hover { border-bottom-color: var(--info); }
.aclaw-message-bubble .claw-btn {
  display: inline-block;
  padding: 10px 20px;
  font-size: 13px; font-weight: 600;
  border-radius: 8px;
  text-decoration: none;
  font-family: inherit;
  color: var(--on-accent, #fff);
  background: linear-gradient(135deg, var(--accent), color-mix(in srgb, var(--accent) 85%, black));
  box-shadow: 0 2px 4px color-mix(in srgb, var(--accent) 25%, transparent);
  border: 0;
}
.aclaw-message-bubble .claw-btn--sec {
  color: var(--grey-900, inherit);
  background: var(--grey-0, #fff);
  border: 1px solid var(--grey-300, rgba(127,127,127,0.20));
  box-shadow: none;
}

.aclaw-message-bubble .claw-code {
  font-family: var(--font-mono, Menlo, monospace);
  font-size: 13px;
  /* An inline code span carries the longest unbroken tokens the agent emits
     (ids, paths, URLs). Without this it is an un-shrinkable min-content island
     that widens whatever grid/flex track holds it. */
  overflow-wrap: anywhere;
  padding: 2px 7px;
  background: var(--grey-200, rgba(127,127,127,0.16));
  border-radius: 4px;
  color: var(--grey-800, inherit);
  font-variant-numeric: tabular-nums;
}

/* Universal inline tone — paint any <strong>/<em>/<span>/<code> with semantic color
   by setting data-tone="s|i|w|d|b". Use for in-prose loss/gain values, callout-able
   inline text, anywhere the agent used to inline-style color hex. The blueprint
   sanitizer strips inline style="color:...", so this attribute is the supported path. */
.aclaw-message-bubble strong[data-tone="s"],
.aclaw-message-bubble em[data-tone="s"],
.aclaw-message-bubble span[data-tone="s"],
.aclaw-message-bubble code[data-tone="s"] { color: var(--success); }
.aclaw-message-bubble strong[data-tone="i"],
.aclaw-message-bubble em[data-tone="i"],
.aclaw-message-bubble span[data-tone="i"],
.aclaw-message-bubble code[data-tone="i"] { color: var(--info); }
.aclaw-message-bubble strong[data-tone="w"],
.aclaw-message-bubble em[data-tone="w"],
.aclaw-message-bubble span[data-tone="w"],
.aclaw-message-bubble code[data-tone="w"] { color: var(--warning); }
.aclaw-message-bubble strong[data-tone="d"],
.aclaw-message-bubble em[data-tone="d"],
.aclaw-message-bubble span[data-tone="d"],
.aclaw-message-bubble code[data-tone="d"] { color: var(--danger); }
.aclaw-message-bubble strong[data-tone="b"],
.aclaw-message-bubble em[data-tone="b"],
.aclaw-message-bubble span[data-tone="b"],
.aclaw-message-bubble code[data-tone="b"] { color: var(--accent-text, var(--accent)); }
.aclaw-message-bubble strong[data-tone="n"],
.aclaw-message-bubble em[data-tone="n"],
.aclaw-message-bubble span[data-tone="n"],
.aclaw-message-bubble code[data-tone="n"] { color: var(--grey-500, #66686b); }

/* Tone modifier on body paragraphs — same shape as KPI/pill */
.aclaw-message-bubble .claw-body[data-tone="d"],
.aclaw-message-bubble .claw-body--muted[data-tone="d"] { color: var(--danger); }
.aclaw-message-bubble .claw-body[data-tone="s"],
.aclaw-message-bubble .claw-body--muted[data-tone="s"] { color: var(--success); }
.aclaw-message-bubble .claw-body[data-tone="w"],
.aclaw-message-bubble .claw-body--muted[data-tone="w"] { color: var(--warning); }


/* able-court-grid */

/* The sizing vars are bound to the ELEMENT as well as to .acg-host, and that
   is load-bearing for SSR: .acg-host is added by connectedCallback, so before
   the ~2.2MB bundle parses the class does not exist and every var here would
   be undefined. grid-template-columns: minmax(var(--acg-slot-w), 1fr) is then
   an INVALID declaration and is dropped whole, so the server-rendered grid
   collapses to content-sized rows and a single column -- measured 422px, then
   817px once the class lands: a 395px shift at hydration, i.e. exactly the CLS
   the SSR exists to remove. Same specificity trap as the *_CHROME_CSS rename:
   markup on screen before the bundle must be styled by selectors that match
   before the bundle. Identical values on both selectors, so nothing moves
   post-upgrade. */
able-court-grid, .acg-host{ --acg-label-w:60px; --acg-slot-w:30px; --acg-row-h:48px; --acg-lane-h:38px; --acg-strip-row-h:48px; display:block; }
@media (min-width:1280px){ able-court-grid, .acg-host{ --acg-slot-w:34px; --acg-row-h:52px; } }

/* ── mobile strip (base; hidden ≥768px) ── */
/* grid-template-columns EXPLICITO y clampado: un track auto-dimensionado crece
   hasta max-content, asi que con carriles la tira (705px con 4) desbordaba el
   telefono y el ancestro con overflow:hidden la RECORTABA — medido 2026-08-13.
   minmax(0,1fr) fuerza a la tira al ancho real del envoltorio y sus carriles se
   reparten ese ancho. overflow-x:auto es el cinturon: si un host no clampa su
   propio contenedor, el usuario obtiene SCROLL, nunca contenido invisible.
   (Sin acentos graves aqui dentro: este CSS vive en un template literal.) */
.acg-mobile{ display:grid; grid-template-columns:minmax(0, 1fr); gap:var(--space-3);
  min-width:0; overflow-x:auto; overscroll-behavior-x:contain; }
.acg-mobile-bar{ display:flex; align-items:center; gap:var(--space-3); }
.acg-ctl-label{ font-size:var(--font-sm); font-weight:600; color:var(--text-muted); }
.acg-row-select{ flex:1; min-height:var(--tap-target,44px); padding:var(--space-2) var(--space-3);
  font-size:max(16px,var(--font-base)); font-family:var(--font-sans); color:var(--text-primary);
  background:var(--surface-card); border:1px solid var(--border-default); border-radius:var(--radius-md); }
/* min-width:0 es LOAD-BEARING (medido 2026-08-13): la tira es item de un grid y
   el minimo automatico de un item es min-content, asi que con 4 carriles se
   plantaba en 705px dentro de un telefono de 390 y overflow:hidden recortaba los
   dos ultimos — minmax(0,1fr) en las columnas no alcanza si el CONTENEDOR no
   puede encoger. Con esto la tira baja al ancho real y los carriles se reparten. */
.acg-strip{ display:grid; grid-template-columns:76px 1fr; background:var(--surface-card);
  border:1px solid var(--border-default); border-radius:var(--radius-lg); overflow:hidden; min-width:0; }
.acg-strip-slot{ grid-column:1 / -1; display:flex; align-items:center; padding-left:var(--space-4);
  border-top:1px solid var(--border-subtle); cursor:pointer; }
.acg-strip-slot[data-hour="1"]{ border-top-color:var(--border-strong); }
.acg-strip-slot:first-child{ border-top:none; }
.acg-strip-time{ font-size:var(--font-xs); color:var(--text-muted); font-variant-numeric:tabular-nums; }

/* ── past / out-of-window slots: read-only + quiet hatch ── */
/* a COVERED slot (a live span sits on it) is inert — the 2px span-margin
   sliver must not offer "tap to book" under a block/booking/lesson
   (tenniswest FD bug 2026-07-30: aiming at the sliver booked a blocked court). */
.acg-slot[data-covered="true"], .acg-strip-slot[data-covered="true"]{ pointer-events:none; cursor:default; }
.acg-slot[data-past="true"], .acg-strip-slot[data-past="true"]{
  background-color:var(--surface-subtle);
  background-image:repeating-linear-gradient(135deg, rgba(100,116,139,0.07), rgba(100,116,139,0.07) 1px, transparent 1px, transparent 7px); }
/* a past slot is inert unless the host declared past-bookable (then it carries role=button) */
.acg-slot[data-past="true"]:not([role="button"]), .acg-strip-slot[data-past="true"]:not([role="button"]){ pointer-events:none; cursor:not-allowed; }

/* ── spans (bookings + lessons + settled) — shared desktop + mobile ── */
.acg-span{ position:relative; z-index:1; display:grid; align-content:center; gap:1px; margin:2px; padding:var(--space-1) var(--space-2);
  border-radius:var(--radius-md); box-shadow:var(--shadow-sm); cursor:pointer; overflow:hidden; min-width:0; }
.acg-span[data-kind="booking"][data-status="BOOKED"]{ background:var(--accent); color:var(--on-accent); }
/* the current user's OWN item (privacy pages) — accent + an inset "yours" ring */
.acg-span[data-mine="1"][data-status="BOOKED"]{ box-shadow:inset 0 0 0 1.5px color-mix(in srgb, var(--on-accent) 55%, transparent), var(--shadow-sm); }
/* someone ELSE'S booking (only when mine is explicitly 0) — a neutral, no-identity
   "taken" block, visually distinct from the user's own accent block */
.acg-span[data-kind="booking"][data-status="BOOKED"][data-mine="0"]{
  background:color-mix(in srgb, var(--text-muted) 16%, var(--surface-card)); color:var(--text-secondary);
  box-shadow:inset 0 0 0 1px var(--border-strong); }
/* lesson tint — retintable per app/theme via --acg-lesson-bg (default --accent-secondary) */
.acg-span[data-kind="lesson"]{ background:var(--acg-lesson-bg, var(--accent-secondary)); color:var(--on-accent);
  box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--on-accent) 35%, transparent), var(--shadow-sm); }
/* read-only spans (privacy / view-only) — no pointer, no click */
.acg-span[data-noclick]{ cursor:default; }
/* settled (cancelled / no-show) — a thin HISTORY strip pinned to the bottom of
   the lane. It must read as "this happened here" WITHOUT reading as occupancy:
   the free slot cell above it stays visible + tappable (rebooking the slot).
   surface-subtle-on-card was near-invisible; dashed border + secondary text +
   an inline CXL/NS marker keep it legible without competing with live blocks. */
.acg-span[data-status="CANCELLED"], .acg-span[data-status="NO_SHOW"]{ align-self:end;
  background:var(--surface-card); color:var(--text-secondary);
  box-shadow:none; border:1px dashed var(--border-strong);
  min-height:18px; align-content:center; opacity:1; }
.acg-span[data-status="CANCELLED"] .acg-span-name,
.acg-span[data-status="NO_SHOW"] .acg-span-name{ font-weight:600; text-decoration:line-through; text-decoration-color:color-mix(in srgb, currentColor 60%, transparent); }
.acg-span[data-status="CANCELLED"] .acg-span-name::before{ content:"CXL "; font-size:8px; font-weight:800; letter-spacing:0.06em; text-decoration:none; color:var(--danger); }
.acg-span[data-status="NO_SHOW"] .acg-span-name::before{ content:"NS "; font-size:8px; font-weight:800; letter-spacing:0.06em; text-decoration:none; color:var(--warning); }
.acg-span[data-status="CANCELLED"] .acg-span-time, .acg-span[data-status="NO_SHOW"] .acg-span-time,
.acg-span[data-status="CANCELLED"] .acg-span-tag, .acg-span[data-status="NO_SHOW"] .acg-span-tag{ display:none; }
.acg-span-name{ font-size:var(--font-xs); font-weight:700; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.acg-span-time{ font-size:10px; opacity:0.88; font-variant-numeric:tabular-nums; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.acg-span-tag{ font-size:9px; font-weight:700; letter-spacing:0.05em; text-transform:uppercase; opacity:0.78; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.acg-span--v{ grid-column:2; margin:3px var(--space-2) 3px 2px; }
.acg-span--v[data-status="CANCELLED"], .acg-span--v[data-status="NO_SHOW"]{ align-self:stretch; }
/* ── event badges: solid sprite icons pinned top-right inside a span ── */
.acg-span-badges{ position:absolute; top:3px; right:4px; z-index:2; display:flex; gap:2px; color:inherit; pointer-events:none; }
.acg-span-badges .ic{ font-size:12px; }

/* ── desktop grid (≥768px) ── */
.acg-grid{ display:none; }
@media (min-width:768px){
  .acg-mobile{ display:none; }
  .acg-grid{ display:block; position:relative; overflow-x:auto; overflow-y:visible;
    background:var(--surface-card); border:1px solid var(--border-default); border-radius:var(--radius-lg); scrollbar-width:auto; }
  .acg-row{ display:grid; grid-template-rows:var(--acg-row-h); }
  .acg-row--head{ grid-template-rows:30px; border-bottom:1px solid var(--border-strong); position:sticky; top:0; z-index:3; }
  /* label + slots span EVERY lane (grid-row:1/-1 is identical to 1 on a
     single-track row, so the lane split costs nothing when there is none). */
  .acg-corner, .acg-row-label{ grid-column:1; grid-row:1 / -1; position:sticky; left:0; z-index:2; display:flex; align-items:center;
    padding-left:var(--space-2); background:var(--surface-subtle); border-right:1px solid var(--border-strong);
    font-size:var(--font-xs); font-weight:700; color:var(--text-secondary); }
  .acg-corner{ z-index:4; }
  .acg-row-label{ gap:var(--space-1); overflow:hidden; }
  .acg-row-code{ font-variant-numeric:tabular-nums; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .acg-row-badge{ flex:none; font-size:8px; font-weight:800; letter-spacing:0.04em; text-transform:uppercase;
    padding:1px 4px; border-radius:var(--radius-full); background:var(--accent-soft); color:var(--accent-text, var(--accent)); }
  /* ── row dots: group swatches after the badge (page-colored per token) ── */
  .acg-row-dots{ flex:none; display:inline-flex; align-items:center; gap:3px; }
  .acg-dot{ width:7px; height:7px; border-radius:50%; background:var(--acg-dot, var(--text-muted)); }
  .acg-time-head{ grid-row:1; display:flex; align-items:center; border-left:1px solid var(--border-subtle); overflow:visible; position:relative; }
  .acg-time-head[data-hour="1"]{ border-left-color:var(--border-strong); }
  .acg-time-label{ font-size:10px; font-weight:600; color:var(--text-muted); padding-left:2px; white-space:nowrap; }
  .acg-row--res{ border-top:1px solid var(--border-subtle); }
  /* ── group header caption rows (opt-in via group-headers; desktop grid only) ── */
  .acg-row--group{ grid-template-columns:1fr; grid-template-rows:20px; border-top:1px solid var(--border-subtle); }
  .acg-group-label{ grid-column:1; grid-row:1; justify-self:start; position:sticky; left:0; z-index:2;
    display:inline-flex; align-items:center; padding:0 var(--space-2); background:var(--surface-subtle);
    border-left:3px solid var(--acg-group, var(--border-strong));
    font-size:10px; font-weight:800; letter-spacing:0.05em; text-transform:uppercase; color:var(--text-secondary); }
  .acg-group-fill{ grid-column:1; grid-row:1; background:var(--surface-subtle); }
  .acg-slot{ grid-row:1 / -1; border-left:1px solid var(--border-subtle); cursor:pointer; min-width:0; }
  .acg-slot[data-hour="1"]{ border-left-color:var(--border-default); }
  .acg-span{ grid-row:1; }
}

/* ── enhancement layer: now-line · crosshair · tooltip ── */
.acg-tip{ position:fixed; left:0; top:0; z-index:var(--z-tooltip,900); max-width:260px; padding:5px 9px;
  border-radius:var(--radius-sm); background:var(--surface-inverse); color:var(--text-inverse);
  font-size:var(--font-xs); font-weight:600; line-height:1.3; box-shadow:var(--shadow-md);
  pointer-events:none; opacity:0; transition:opacity 90ms var(--ease-enter, ease); }
.acg-tip.acg-tip--on{ opacity:1; }
.acg-slot.acg-col-hot:not([data-past="true"]), .acg-slot.acg-col-hot[data-past="true"][role="button"]{ background-color:var(--accent-soft); }
.acg-row-hot .acg-slot{ background-color:var(--accent-soft); }
.acg-time-head.acg-col-hot{ background-color:var(--accent-soft); }
.acg-time-head.acg-col-hot .acg-time-label{ color:var(--accent-text, var(--accent)); font-weight:700; }
.acg-row-hot .acg-row-label{ background:var(--accent-soft); }
.acg-row-hot .acg-row-label, .acg-row-hot .acg-row-label .acg-row-code{ color:var(--accent-text, var(--accent)); }
.acg-slot.acg-cell-hot:not([data-past="true"]), .acg-slot.acg-cell-hot[data-past="true"][role="button"]{ background-color:color-mix(in srgb, var(--accent) 18%, var(--surface-card)); box-shadow:inset 0 0 0 1.5px var(--accent); }
/* ── drag-to-select range: live highlight + no text selection while dragging ── */
.acg-slot.acg-slot--sel{ background-color:color-mix(in srgb, var(--accent) 16%, var(--surface-card));
  box-shadow:inset 0 1.5px 0 var(--accent), inset 0 -1.5px 0 var(--accent); }
.acg-slot.acg-slot--sel[data-sel-edge="start"]{ box-shadow:inset 1.5px 1.5px 0 var(--accent), inset 0 -1.5px 0 var(--accent); }
.acg-slot.acg-slot--sel[data-sel-edge="end"]{ box-shadow:inset -1.5px 1.5px 0 var(--accent), inset 0 -1.5px 0 var(--accent); }
.acg-slot.acg-slot--sel[data-sel-edge="both"]{ box-shadow:inset 0 0 0 1.5px var(--accent); }
.acg-host.acg-dragging{ user-select:none; -webkit-user-select:none; }
.acg-host.acg-dragging .acg-slot{ cursor:ew-resize; }
.acg-now{ position:absolute; top:0; width:2px; background:var(--danger); z-index:1; pointer-events:none; display:none; }
.acg-now-dot{ position:absolute; top:31px; left:50%; transform:translate(-50%,-50%); width:9px; height:9px; border-radius:50%; background:var(--danger); box-shadow:0 0 0 2px var(--surface-card); }
.acg-now-lab{ position:absolute; top:36px; left:50%; transform:translateX(-50%); font-size:9px; font-weight:700; letter-spacing:0.04em; text-transform:uppercase; color:var(--on-danger); background:var(--danger); padding:1px 5px; border-radius:var(--radius-full); white-space:nowrap; }

/* ── empty state ── */
.acg-empty{ display:grid; gap:var(--space-2); justify-items:center; padding:var(--space-10) var(--space-4);
  background:var(--surface-card); border:1px dashed var(--border-default); border-radius:var(--radius-lg); }
.acg-empty-title{ font-size:var(--font-lg); font-weight:700; color:var(--text-primary); }
.acg-empty-msg{ font-size:var(--font-sm); color:var(--text-muted); }

@media (hover:hover) and (pointer:fine){
  .acg-slot:hover, .acg-strip-slot:hover{ background:var(--accent-soft); }
  .acg-span[data-kind="booking"][data-status="BOOKED"]:not([data-noclick]):hover{ background:var(--accent-hover); }
  .acg-span[data-kind="lesson"]:not([data-noclick]):hover{ background:color-mix(in srgb, var(--acg-lesson-bg, var(--accent-secondary)) 82%, #000); }
}


/* able-day-card */

/* ⚠ ACCENT-COLOURED TEXT USES --accent-text, NEVER --accent.
   Measured on the live build theme 2026-08-24: --accent is a FILL token and
   computes the SAME #2563eb in both modes, so accent text sits at 3.35:1 on the
   dark card ground — under AA. --accent-text re-binds (#2563eb / #A5B4FC) and
   measures 5.17 light / 8.68 dark. Because the TOKEN flips, no media query and no
   [data-theme] guard is needed here at all, and the tenant keeps its own colour
   instead of a hard-coded blue. Fills and borders keep --accent. */
able-day-card { display: block; max-width: 460px; margin: 8px 0; }
able-day-card .adyc {
  /* TONE (CALENDAR-REVAMP.md 2c): ten closed NAMES; the THEME owns the values.
     These are only fallbacks so the card is legible in a theme that has not
     declared --cal-tone-* yet. A native renderer maps the same ten names. */
  --cal-tone-primary:  var(--cal-tone-primary-x,  var(--accent));
  --cal-tone-accent:   var(--cal-tone-accent-x,   var(--accent));
  --cal-tone-info:     var(--cal-tone-info-x,     var(--info));
  --cal-tone-warn:     var(--cal-tone-warn-x,     var(--warning));
  --cal-tone-critical: var(--cal-tone-critical-x, var(--danger));
  --cal-tone-money:    var(--cal-tone-money-x,    var(--success));
  --cal-tone-travel:   var(--cal-tone-travel-x,   var(--info));
  --cal-tone-muted:    var(--cal-tone-muted-x,    var(--text-muted));
  --cal-tone-social:   var(--cal-tone-social-x,   var(--success));
  --cal-tone-neutral:  var(--cal-tone-neutral-x,  var(--text-muted));
  background: var(--surface-card, #fff);
  border: 1px solid var(--border-default, #e5e7eb);
  border-radius: var(--radius-xl, 14px);
  box-shadow: var(--shadow-sm, 0 1px 2px rgba(0,0,0,.06));
  padding: 12px 14px;
  font-family: var(--font-sans, sans-serif);
  color: var(--text-primary, #1d1c17);
}
able-day-card .adyc-eyebrow {
  font-size: 10px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase;
  color: var(--accent-text, var(--accent, #1b2a4a)); margin: 0 0 2px;
}
able-day-card .adyc-title {
  font-size: 17px; line-height: 1.2; font-weight: 700; margin: 0;
  color: var(--text-primary, #1d1c17);
}
able-day-card .adyc-summary { margin: 2px 0 0; font-size: 12px; color: var(--text-muted, #6b7280); }

/* the marker strip — all-day + deadlines */
able-day-card .adyc-markers {
  display: flex; flex-wrap: wrap; gap: 4px;
  margin: 9px 0 0;
  padding: 6px; border-radius: var(--radius-sm, 4px);
  background: var(--surface-subtle, #f8fafc);
  border-bottom: 1px solid var(--border-default, #e5e7eb);
}
able-day-card .adyc-chip {
  display: inline-flex; align-items: center; gap: 4px;
  border: 1px solid var(--border-subtle, #f1f5f9);
  border-left: 3px solid var(--adyc-c, var(--text-muted));
  border-radius: var(--radius-sm, 4px);
  background: var(--surface-subtle, #f8fafc);
  padding: 2px 6px;
  font-size: 11px; line-height: 1.3;
  color: var(--text-primary, #1d1c17);
  text-align: left; cursor: pointer; font-family: inherit;
  max-width: 100%;
}
able-day-card .adyc-chip-t { font-variant-numeric: tabular-nums; color: var(--text-muted, #6b7280); }
able-day-card .adyc-chip-n { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* the CLASS — carried by the row's own hue, so it needs weight, not a second colour */
able-day-card .adyc-chip-c {
  font-weight: 700; letter-spacing: 0.01em; white-space: nowrap;
  color: var(--adyc-c, var(--text-primary));
}
able-day-card .adyc-c {
  font-weight: 700; letter-spacing: 0.01em;
  color: var(--adyc-c, var(--text-primary)); margin-right: 5px;
}
/* chip/row + its source link, grouped so the PAIR wraps as one unit — a link that
   wrapped away from its own chip would read as belonging to the next one. */
able-day-card .adyc-chipw { display: inline-flex; align-items: stretch; max-width: 100%; }
able-day-card .adyc-chipw .adyc-chip { border-top-right-radius: 0; border-bottom-right-radius: 0; }
able-day-card .adyc-roww { display: flex; align-items: stretch; gap: 0; }
able-day-card .adyc-roww .adyc-row { flex: 1 1 auto; min-width: 0; }
able-day-card .adyc-src {
  display: inline-flex; align-items: center; justify-content: center;
  flex: 0 0 auto; min-width: 22px; padding: 0 5px;
  border: 1px solid var(--border-subtle, #f1f5f9); border-left: 0;
  border-radius: 0 var(--radius-sm, 4px) var(--radius-sm, 4px) 0;
  background: var(--surface-subtle, #f8fafc);
  font-size: 11px; line-height: 1; text-decoration: none;
  color: var(--adyc-c, var(--text-muted, #6b7280));
}
@media (hover: hover) and (pointer: fine) {
  able-day-card .adyc-src:hover { background: var(--surface-hover, #f1f5f9); }
}
able-day-card .adyc-src:focus-visible { outline: 2px solid var(--accent, #1b2a4a); outline-offset: 1px; }

/* the timed agenda */
able-day-card .adyc-list { margin: 9px 0 0; display: flex; flex-direction: column; gap: 1px; }

/* ── DONE + TYPE, the two axes the pages carry and this card did not ─────────────
   DONE: the calendar has kept a finished row ON its day since 2026-08-18, struck
   rather than removed, because a calendar that deletes what you completed cannot
   answer "did I do that". The card never carried the flag, so one HW read finished
   on /week and still-owed in chat — two halves of ONE view disagreeing about the
   most consequential bit on the row.
   TYPE: work_type has been on the wire all along and no renderer drew it. The mark
   is geometric, never a second hue: colour already means WHICH CLASS, and shape is
   what survives greyscale, which is what stops colour being the only signal. Drawn
   as a ::before on the title — the row is a grid, so a generated child of the ROW
   would become a third track; a child of the title cannot. */
able-day-card .adyc-row[data-done] { opacity: .55; }
able-day-card .adyc-row[data-done] .adyc-n { text-decoration: line-through; }
able-day-card .adyc-row[data-work]:not([data-work="class"]):not([data-work="event"]) .adyc-n::before {
  content: ''; display: inline-block; flex: none;
  width: 7px; height: 7px; margin-right: 5px;
  background: var(--adyc-c, currentColor); opacity: .9;
}
able-day-card .adyc-row[data-work="submit"] .adyc-n::before { border-radius: 50%; }
able-day-card .adyc-row[data-work="prep"] .adyc-n::before {
  border-radius: 50%; background: transparent;
  box-shadow: inset 0 0 0 2px var(--adyc-c, currentColor);
}
able-day-card .adyc-row[data-work="assess"] .adyc-n::before { transform: rotate(45deg); border-radius: 1px; }
able-day-card .adyc-row[data-work="admin"] .adyc-n::before {
  border-radius: 1px; background: transparent;
  box-shadow: inset 0 0 0 2px var(--adyc-c, currentColor);
}
able-day-card .adyc-row[data-work="chore"] .adyc-n::before { border-radius: 1px; }
able-day-card .adyc-row[data-work="engage"] .adyc-n::before {
  width: 3px; border-radius: 1px; box-shadow: 5px 0 0 0 var(--adyc-c, currentColor);
  margin-right: 9px;
}

able-day-card .adyc-row {
  display: grid; grid-template-columns: 68px 1fr; gap: 8px; align-items: baseline;
  width: 100%; border: 0; background: transparent;
  padding: 5px 4px; border-radius: var(--radius-sm, 4px);
  font-family: inherit; text-align: left; cursor: pointer;
  color: var(--text-primary, #1d1c17);
  border-left: 3px solid var(--adyc-c, var(--accent));
}
@media (hover: hover) and (pointer: fine) {
  able-day-card .adyc-row:hover, able-day-card .adyc-chip:hover { background: var(--surface-hover, #f1f5f9); }
}
able-day-card .adyc-row:focus-visible, able-day-card .adyc-chip:focus-visible {
  outline: 2px solid var(--accent); outline-offset: -2px;
}
able-day-card .adyc-time {
  font-size: 11px; font-variant-numeric: tabular-nums; color: var(--text-muted, #6b7280);
  line-height: 1.35;
  /* the range is ONE line — wrapped, its baseline stops agreeing with the title */
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
able-day-card .adyc-body { min-width: 0; }
able-day-card .adyc-n {
  font-size: 13px; font-weight: 600; line-height: 1.3;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
able-day-card .adyc-s {
  font-size: 11px; color: var(--text-muted, #6b7280); line-height: 1.3;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
able-day-card .adyc-more {
  margin: 6px 0 0; font-size: 11px; font-weight: 600;
  color: var(--text-muted, #6b7280);
}
able-day-card .adyc-nothing {
  margin: 10px 0 2px; font-size: 12px; color: var(--text-muted, #6b7280); font-style: italic;
}
able-day-card .adyc-open {
  display: inline-block; margin: 10px 0 0;
  font-size: 12px; font-weight: 600; color: var(--accent-text, var(--accent, #1b2a4a)); text-decoration: none;
}
able-day-card .adyc-open:hover { text-decoration: underline; }

/* 320px: the time column would squeeze the title to nothing, so it goes above */
@media (max-width: 380px) {
  able-day-card .adyc-row { grid-template-columns: 1fr; gap: 0; }
  able-day-card .adyc-time { line-height: 1.2; }
}




/* able-day-cell */

/* ⚠ ACCENT-COLOURED TEXT USES --accent-text, NEVER --accent.
   Measured on the live build theme 2026-08-24: --accent is a FILL token and
   computes the SAME #2563eb in both modes, so accent text sits at 3.35:1 on the
   dark card ground — under AA. --accent-text re-binds (#2563eb / #A5B4FC) and
   measures 5.17 light / 8.68 dark. Because the TOKEN flips, no media query and no
   [data-theme] guard is needed here at all, and the tenant keeps its own colour
   instead of a hard-coded blue. Fills and borders keep --accent. */
able-day-cell { display: block; }
able-day-cell .adc {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  width: 100%;
  min-height: var(--adc-min-h, 44px);
  padding: var(--space-1, 4px) 2px;
  border: 0;
  border-radius: var(--radius-md, 8px);
  background: transparent;
  color: var(--text-primary);
  font-family: inherit;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background var(--transition-fast, 120ms) ease;
}
able-day-cell .adc[disabled] { cursor: default; opacity: .45; }
able-day-cell .adc:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}
@media (hover: hover) and (pointer: fine) {
  able-day-cell .adc:not([disabled]):hover { background: var(--surface-hover); }
}

/* the digit */
able-day-cell .adc-num {
  font-size: var(--adc-num-size, 13px);
  line-height: 1.1;
  font-weight: 400;
}
.adc[data-weight="semibold"] .adc-num { font-weight: 600; }
.adc[data-weight="heavy"]    .adc-num { font-weight: 800; }
.adc[data-muted="true"]     .adc-num { color: var(--text-subtle); }

/* TODAY vs SELECTED are separable states — today is a ring, selected is a fill.
   A day can be both; the fill wins on background, the ring survives as a border. */
.adc[data-today="true"] .adc-num {
  color: var(--accent-text, var(--accent));
  font-weight: 800;
}
.adc[data-today="true"]::before {
  content: "";
  position: absolute;
  inset: 2px;
  border-radius: var(--radius-md, 8px);
  border: 1.5px solid var(--accent);
  pointer-events: none;
}
.adc[data-selected="true"] { background: var(--accent); }
.adc[data-selected="true"] .adc-num { color: var(--on-accent); }

/* The mark slot — ALWAYS reserved so nothing jitters when marks land. */
able-day-cell .adc-marks {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 2px;
  height: var(--adc-mark-h, 9px);
  min-height: var(--adc-mark-h, 9px);
}
able-day-cell .adc-bar {
  height: 3px;
  border-radius: 999px;
  background: var(--accent);
}
able-day-cell .adc-dot {
  width: 4px;
  height: 4px;
  border-radius: 50%;
}
able-day-cell .adc-dot--exam  { background: var(--danger); }
able-day-cell .adc-dot--hw    { background: var(--warning); }
able-day-cell .adc-dot--event { background: var(--info); }
able-day-cell .adc-dot--trip  { background: var(--success); }
able-day-cell .adc-dot--other { background: var(--text-muted); }
/* the opt-in count lane. --src-color is the PAGE's hook: a page that already
   colours a legend per source sets it on .adc-dot[data-src="N"] and the dot beside
   a day becomes the colour of the chip it came from. Unset, it falls back to the
   same --info the events dot uses, so the blueprint alone still looks right. */
able-day-cell .adc-dot--count { background: var(--src-color, var(--info)); }
/* on a selected (filled) cell the marks sit on the accent — lift them to the
   on-accent ink so they stay visible instead of vanishing into the fill */
.adc[data-selected="true"] .adc-bar,
.adc[data-selected="true"] .adc-dot { background: var(--on-accent); opacity: .9; }

@media (prefers-reduced-motion: reduce) {
  able-day-cell .adc { transition: none; }
}


/* able-day-panel */

able-day-panel { display: block; min-width: 0; }

/* THE PANEL IS AS TALL AS THE DAY IT SHOWS. A right-side offcanvas is pinned
   top-to-bottom by construction, so a Tuesday holding seven rows and a Saturday
   holding one both drew a full-height sheet — and on the quiet day that is a
   screen-height column of nothing under three short lists. Measured on the
   donor: drawer 900px, content 430.9px, 445.1px of it empty.

   Scoped by :has() rather than by a per-page ref, so it follows the panel into
   any drawer instead of being re-declared per page (which is how the two donor
   copies each ended up with their own ref-scoped duplicate of this rule). Every
   other offcanvas in the fleet keeps the full-height drawer it was written
   against. A long day still fills the ceiling and scrolls inside itself, so
   nothing becomes unreachable; the rounded bottom edge is what keeps a short
   day reading as a finished card rather than a panel someone cut off. */
able-offcanvas[data-position="right"]:has(able-day-panel) {
  bottom: auto; height: auto; max-height: 92dvh;
  border-bottom-left-radius: var(--radius-lg);
}

.adp { display: grid; gap: var(--space-3); padding: var(--space-4); min-width: 0; }
.adp[data-panels="3"] { container-type: inline-size; container-name: adp; }

.adp-head {
  display: grid; gap: 2px;
  padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--border-default);
}
.adp-dow {
  font-size: var(--font-xs); text-transform: uppercase; letter-spacing: 0.08em;
  color: var(--text-secondary);
}
.adp-date { font-size: var(--font-xl); font-weight: 700; color: var(--text-primary); }

/* align-items: start — THE PANELS SIZE TO THEIR CONTENT. Grid stretches items
   to the row by default, so three panels holding 4, 4 and 1 rows all measured
   the tallest and two of them ended in ~290px of nothing with a divider running
   down the whole of it. Rejected twice on the donor ("that white space below is
   wasted"). Each panel is now as tall as what it holds, the dividers end where
   the content does, and a one-row ALL-DAY column stops reading as a column that
   failed to load. */
.adp-cols { display: grid; grid-template-columns: 1fr; gap: var(--space-3); align-items: start; }

/* THE RUNGS ARE CONTAINER QUERIES ON THE DRAWER, never viewport media queries,
   and that is load-bearing: responsive="auto" flips this offcanvas to a bottom
   sheet under 480px, so the panel's width and the window's width stop agreeing
   exactly where the layout has to change. Asking the drawer how wide IT is is
   the only question with a stable answer. */
/* THE MIDDLE RUNG — two up, ALL-DAY spanning beneath. Measured on iPad
   portrait: the drawer is ~676px there, and a single column left ~610px of
   width printing one list, while three would have been ~210px each and
   truncated the titles that are the whole point. Two is the honest answer at
   that width, and TIMED beside DUE is the pairing the reader's question
   actually wants ("what is due, and am I free?"). ALL-DAY goes full-width
   beneath: it is one row on most days, so a third of the width would be mostly
   empty and a full-width strip costs almost no height. */
@container adp (min-width: 560px) {
  .adp-cols { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-4); }
  .adp-col[data-col="due"] {
    border-left: 1px solid var(--border-subtle); padding-left: var(--space-4);
  }
  .adp-col[data-col="allday"] {
    grid-column: 1 / -1; border-top: 1px solid var(--border-subtle); padding-top: var(--space-2);
  }
}
@container adp (min-width: 760px) {
  .adp-cols { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-4); }
  /* A rule BETWEEN the columns, not a border on each — three boxed panels read
     as three cards, and these are three parts of ONE day. */
  .adp-col + .adp-col {
    border-left: 1px solid var(--border-subtle); padding-left: var(--space-4);
  }
  /* Undo the middle rung: at three-up ALL-DAY is a column like the others. */
  .adp-col[data-col="allday"] { grid-column: auto; border-top: 0; padding-top: 0; }
}

.adp-col { min-width: 0; display: flex; flex-direction: column; gap: var(--space-1); }

/* THE FOOT — full width, under everything, at every rung. It is a change of
   TENSE from the columns above it (what happened, not what is owed), so it gets
   a rule rather than a gap: the same separation ableU's week draws between the
   hour grid and its landed strip. It sits OUTSIDE .adp-cols, so none of the
   container-query rungs above have to know it exists — which is what keeps the
   three-column tuning untouched for hosts that never author one. */
.adp-foot {
  border-top: 1px solid var(--border-default);
  padding-top: var(--space-3);
  margin-top: var(--space-1);
}
/* An empty panel is quieter but still THERE — "Nothing due" is an ANSWER, and a
   missing panel is a question about whether the surface forgot to look. */
.adp-col[data-empty="y"] { opacity: .62; }
.adp-colh {
  margin: 0; display: flex; align-items: center; gap: 6px;
  font-size: var(--font-xs); font-weight: 800;
  letter-spacing: .06em; text-transform: uppercase;
  color: var(--text-secondary); line-height: 1.1;
  padding-bottom: 5px; border-bottom: 1px solid var(--border-default);
}
/* The count is the panel's whole density signal — tabular so three panels'
   numbers line up rather than jittering by digit width. */
.adp-n {
  font-variant-numeric: tabular-nums; font-size: var(--font-xs); font-weight: 800;
  color: var(--text-secondary); background: var(--surface-subtle);
  border-radius: var(--radius-full); padding: 1px 6px; letter-spacing: 0;
}
/* No gap: how rows are SEPARATED is row anatomy, not frame. One donor divides
   them with a border-bottom per row, the other with a list gap; a gap declared
   here would add 4px to every bordered row. A page that wants gaps sets
   ".adp-list { gap: … }" in its own CSS. */
.adp-list { list-style: none; margin: 0; padding: 0; display: grid; min-width: 0; }
.adp-none { margin: 0; font-size: var(--font-sm); color: var(--text-muted); }

/* ══ DUE vs ALL-DAY: A DEADLINE IS A POINT, AN ALL-DAY IS AN EXTENT ══════════
   Side by side these two must be told apart by SHAPE, not hue — the colour on
   these rows is already spoken for (it is the row's own category / calendar,
   carried in --adp-accent), and re-tinting it to mean "due" would overwrite the
   one axis that says which thing a row belongs to. So the distinction runs on
   two channels, neither of them hue, and it survives greyscale and a
   colour-blind reader.

   It is also NOT A NEW VOCABULARY: it is the same distinction the week band
   itself draws (able-time-grid: .atg-w-banner is filled, .atg-w-due outlined),
   drawn one level finer because here the two populations sit side by side. A
   reader who learns it in one surface has learned it in the other. */

/* ALL-DAY is an EXTENT: filled, rounded, its accent rule running the FULL
   height of the row. 20%, not the 12% first shipped — an adversarial read of
   that render called it "sin suficiente claridad", and it was right: 12% over a
   white card is a tint you have to go looking for. */
.adp-col[data-col="allday"] .adp-row {
  background: color-mix(in srgb, var(--adp-accent, var(--border-default)) 20%, var(--surface-card));
  border-radius: 0 var(--radius-md) var(--radius-md) 0;
}
/* ⚠ THE TINT COSTS SECONDARY TEXT ITS HEADROOM — measured, and this is the fix.
   --text-muted sits ~4.83:1 on the plain card ground, i.e. ~0.33 above the bar,
   so ANY tint puts it under. Measured at 12% it fell to 4.03-4.19 across three
   of five categories in light mode. --text-secondary on the tinted ground
   instead, scoped to the ONE column that has a tint so the other two keep the
   type they were designed with. Softening the tint was the alternative and it
   is the wrong one: it pays for a text-colour problem by weakening the fill
   that carries the whole distinction. */
.adp-col[data-col="allday"] .adp-meta { color: var(--text-secondary); }

/* DUE is a POINT: no fill. The accent is withdrawn to a ring on the card
   ground, and the rule is replaced by a short END-STOP at the row's middle —
   a stem and a foot, together an upright tee. A full-height rule says "this
   runs"; a centred mark says "this is due HERE". 45%, not 26% — at 26% the ring
   resolved to roughly #E5E7EB and read as an ordinary container edge rather
   than a deliberate outline. */
.adp-col[data-col="due"] .adp-row {
  position: relative;
  border-left-color: transparent;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--adp-accent, var(--border-default)) 45%, transparent);
}
.adp-col[data-col="due"] .adp-row::before,
.adp-col[data-col="due"] .adp-row::after {
  content: ''; position: absolute; left: -3px; top: 50%;
  background: var(--adp-accent, var(--border-default));
}
.adp-col[data-col="due"] .adp-row::before {
  width: 3px; height: 18px; margin-top: -9px; border-radius: 0 2px 2px 0;
}
.adp-col[data-col="due"] .adp-row::after {
  width: 12px; height: 3px; margin-top: -1.5px; border-radius: 0 1.5px 1.5px 0;
}
/* ⚠ THE TEE HANGS OUTSIDE THE ROW BOX (left: -3px), which is deliberate: it
   keeps the mark off the row's own padding so a tenant's row content is never
   indented to make room for it. The clearance it needs is the COLUMN's, and
   every rung already has it — at one-up the .adp wrapper's own --space-4
   padding, at two- and three-up the column's --space-4 padding-left. Nothing
   clips it, so no row-level rule is owed. (The donor that drew this mark on its
   swatch DOT instead had to widen the row's internal gap to --space-3 or the
   foot touched the first letter; drawing it on the row avoids that entirely.) */
/* A DONE DEADLINE KEEPS ITS SHAPE. A strikethrough already says "finished";
   dropping the terminus with it would make a completed row read as an all-day
   one, which is the opposite of the distinction this section exists to draw. */
.adp-col[data-col="due"] .adp-row[data-done="y"]::before,
.adp-col[data-col="due"] .adp-row[data-done="y"]::after { opacity: .55; }


/* able-empty-state */

able-empty-state {
  display: block;
  width: 100%;
  box-sizing: border-box;
  padding: var(--space-9, 56px) var(--space-5, 24px);
  text-align: center;
  font-family: var(--font-ui, sans-serif);
}
/* The display:block above is an AUTHOR rule, and author always outranks the UA
   sheet's [hidden] { display: none } — so without this line an
   able-empty-state carrying the hidden attribute stays VISIBLE. 19 sibling
   blueprints already ship this guard; this one did not, and able-ableu/campus
   had to patch it in page CSS for its two script-toggled instances. Server
   rendering makes the gap worse rather than creating it: the host was already
   visible pre-upgrade (this chrome is render-blocking), but it was an empty
   padded box and it now paints the whole message. Higher specificity than the
   base rule, so source order is not load-bearing.
   NOTE no backticks anywhere in this CSS body — one would close the template
   literal and take the whole able-runtime.js bundle down with it. */
able-empty-state[hidden] { display: none; }
@media (min-width: 768px) {
  able-empty-state {
    padding: var(--space-11, 96px) var(--space-7, 40px);
  }
}

.aes-inner {
  max-width: 420px;
  margin-inline: auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3, 12px);
}

.aes-icon {
  width: 56px; height: 56px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 28px;
  color: var(--text-muted, #6B5F58);
  background: var(--surface-subtle, #F2EDE5);
  border-radius: 50%;
  margin-bottom: var(--space-2, 8px);
}
.aes-icon:empty { display: none; }
.aes-icon svg,
.aes-icon img {
  width: 28px;
  height: 28px;
  display: block;
}

.aes-title-wrap > * {
  font-family: var(--font-display, var(--font-sans, sans-serif));
  font-size: clamp(18px, 1.5vw, 22px);
  line-height: 1.3;
  font-weight: 500;
  letter-spacing: -0.01em;
  color: var(--text-primary, #0F0E0C);
  margin: 0;
}
.aes-title-wrap:empty { display: none; }

.aes-description-wrap > * {
  font-family: var(--font-body, var(--font-sans, sans-serif));
  font-size: 15px;
  line-height: 1.55;
  color: var(--text-muted, #6B5F58);
  margin: 0;
  max-width: 44ch;
}
.aes-description-wrap:empty { display: none; }

.aes-actions-wrap {
  margin-top: var(--space-3, 12px);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3, 12px);
  justify-content: center;
}
.aes-actions-wrap:empty { display: none; }
/* The slot="actions" wrapper is re-emitted intact (so the markup survives an
   innerHTML round-trip), but it must not become the single flex item — that
   would stack multiple CTAs instead of laying them out in a row with the gap
   above. display:contents keeps the buttons as .aes-actions-wrap's own flex
   children, so the layout is identical to unwrapping. */
.aes-actions-wrap > [slot="actions"] { display: contents; }


/* able-faq */

able-faq {
  display: block;
  width: 100%;
  box-sizing: border-box;
}

able-faq[data-variant="single-col"] {
  display: flex;
  flex-direction: column;
}
able-faq[data-variant="two-col"] {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0 var(--space-7, 40px);
}
@media (min-width: 768px) {
  able-faq[data-variant="two-col"] {
    grid-template-columns: 1fr 1fr;
  }
}

able-faq > details {
  border-bottom: 1px solid var(--surface-base, #E6DFD4);
  padding: var(--space-4, 16px) 0;
}
able-faq > details:first-of-type {
  border-top: 1px solid var(--surface-base, #E6DFD4);
}

able-faq > details > summary {
  font-family: var(--font-display, var(--font-sans, sans-serif));
  font-size: clamp(16px, 1.3vw, 19px);
  line-height: 1.4;
  font-weight: 500;
  letter-spacing: -0.01em;
  color: var(--text-primary, #0F0E0C);
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3, 12px);
  padding: var(--space-2, 8px) 0;
  min-height: 44px;
}
able-faq > details > summary::-webkit-details-marker { display: none; }
able-faq > details > summary::after {
  content: "+";
  font-family: var(--font-mono, monospace);
  font-size: 22px;
  font-weight: 400;
  color: var(--copper, #B36A2F);
  flex-shrink: 0;
  transition: transform 220ms ease;
  line-height: 1;
}
able-faq > details[open] > summary::after {
  transform: rotate(45deg);
}

able-faq > details > *:not(summary) {
  font-family: var(--font-body, var(--font-sans, sans-serif));
  font-size: 15px;
  line-height: 1.6;
  color: var(--surface-base, #2C2622);
  padding: var(--space-3, 12px) 0 var(--space-2, 8px) 0;
  margin: 0;
  max-width: 64ch;
}
able-faq > details > *:not(summary) a {
  color: var(--copper-deep, #8C4F1F);
  text-decoration: underline;
  text-decoration-color: var(--surface-base, #E6DFD4);
  text-underline-offset: 3px;
}


/* able-feature-grid */

able-feature-grid {
  display: grid;
  width: 100%;
  box-sizing: border-box;
  grid-template-columns: 1fr;
}

able-feature-grid[data-gap="tight"]   { gap: var(--space-4, 16px); }
able-feature-grid[data-gap="default"] { gap: var(--space-6, 32px); }
able-feature-grid[data-gap="loose"]   { gap: var(--space-9, 56px); }

@media (min-width: 640px) {
  able-feature-grid[data-cols="2"] { grid-template-columns: repeat(2, 1fr); }
  able-feature-grid[data-cols="3"] { grid-template-columns: repeat(2, 1fr); }
  able-feature-grid[data-cols="4"] { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 1024px) {
  able-feature-grid[data-cols="3"] { grid-template-columns: repeat(3, 1fr); }
  able-feature-grid[data-cols="4"] { grid-template-columns: repeat(4, 1fr); }
}

/* ─ child card variants ─ */
able-feature-grid > * {
  display: flex;
  flex-direction: column;
  gap: var(--space-3, 12px);
}

able-feature-grid[data-variant="flat"] > * {
  /* no chrome, content speaks for itself */
}
able-feature-grid[data-variant="bordered"] > * {
  padding: var(--space-5, 24px);
  border: 1px solid var(--surface-base, #E6DFD4);
  border-radius: var(--radius-md, 6px);
}
able-feature-grid[data-variant="card"] > * {
  padding: var(--space-5, 24px);
  background: var(--surface-card, #FCFBF7);
  border: 1px solid var(--surface-base, #E6DFD4);
  border-radius: var(--radius-md, 6px);
}
able-feature-grid[data-variant="icon"] > * {
  padding: 0;
}
able-feature-grid[data-variant="icon"] > * > svg:first-child,
able-feature-grid[data-variant="icon"] > * > img:first-child,
able-feature-grid[data-variant="icon"] > * > i:first-child {
  width: 32px;
  height: 32px;
  color: var(--copper, #B36A2F);
  margin-bottom: var(--space-2, 8px);
}

/* Auto-style typography inside cards if author doesn't override */
able-feature-grid > * > h3,
able-feature-grid > * > h4 {
  font-family: var(--font-display, var(--font-sans, sans-serif));
  font-size: clamp(18px, 1.4vw, 22px);
  line-height: 1.25;
  font-weight: 500;
  letter-spacing: -0.01em;
  margin: 0;
  color: var(--text-primary, #0F0E0C);
}
able-feature-grid > * > p {
  font-family: var(--font-body, var(--font-sans, sans-serif));
  font-size: 15px;
  line-height: 1.55;
  margin: 0;
  color: var(--surface-base, #2C2622);
}


/* able-kpi-card */

:where(able-kpi-card) { display: block; }

:where(.akpi) {
  display: flex;
  flex-direction: column;
  gap: var(--space-1, 4px);
  min-width: 0;
}

:where(.akpi-label) {
  font-size: var(--font-xs, 12px);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted, #6b7280);
}

:where(.akpi-value) {
  font-size: var(--font-2xl, 24px);
  font-weight: 700;
  line-height: 1.15;
  color: var(--text-primary, #1d1c17);
  /* A KPI is read in a column beside its neighbours — the digits have to line up. */
  font-variant-numeric: tabular-nums;
}

/* Tone: the only colour opinion, and only where the caller asked for one. */
:where(.akpi[data-tone="good"] .akpi-value) { color: var(--success, #15803d); }
:where(.akpi[data-tone="bad"] .akpi-value)  { color: var(--danger, #b91c1c); }


/* able-login */

able-login {
  display: block;
  width: 100%;
  justify-self: stretch;
  min-height: 100vh;
  background: var(--login-bg, var(--surface-base, #f5f7fa));
  color: var(--text-primary, #0f172a);
}
.alogin-shell {
  width: 100%;            /* explicit; was implicit-inherited */
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  box-sizing: border-box;
}
.alogin-card {
  width: 100%;
  /* clamp to keep mobile gap + cap desktop width; was 420px fixed */
  max-width: var(--login-card-max-width, min(440px, calc(100vw - 32px)));
  padding: var(--login-card-padding, clamp(20px, 4vw, 32px));
  background: var(--login-card-bg, var(--surface-card, #ffffff));
  border-radius: var(--login-card-radius, 12px);
  box-shadow: var(--login-card-shadow, 0 10px 40px rgba(0, 0, 0, 0.08));
  box-sizing: border-box;
}
.alogin-brand {
  display: flex;
  justify-content: center;
  margin-bottom: 16px;
}
.alogin-brand-img {
  /* viewport-aware logo sizing — was fixed 56px */
  max-height: clamp(48px, 8vw, 72px);
  max-width: 240px;
  height: auto;
  width: auto;
  display: block;
  object-fit: contain;
}
.alogin-title {
  margin: 0 0 24px 0;
  font-size: 20px;
  font-weight: 700;
  text-align: center;
  letter-spacing: 0.04em;
  color: var(--text-primary, #0f172a);
}
.alogin-form { display: flex; flex-direction: column; gap: 14px; }
.alogin-form.is-hidden { display: none; }
.alogin-label {
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 13px;
}
.alogin-label-text {
  font-weight: 600;
  color: var(--text-primary, #0f172a);
  opacity: 0.85;
}
.alogin-input {
  width: 100%;
  box-sizing: border-box;
  padding: 12px 14px;
  min-height: 44px;       /* WCAG 2.2 AA touch target — was 39px */
  border: 1px solid var(--login-input-border, var(--border-default, #cbd5e1));
  border-radius: var(--login-input-radius, 8px);
  /* 16px MINIMUM — anything smaller triggers iOS Safari's auto-zoom on focus,
   * which causes a jarring viewport jump on every login. */
  font-size: 16px;
  line-height: 1.4;
  background: var(--surface-card, #ffffff);
  color: var(--text-primary, #0f172a);
  transition: border-color 120ms, box-shadow 120ms;
}
.alogin-input:focus {
  outline: none;
  border-color: var(--login-button-bg, var(--accent, #2563eb));
  box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.15);
}
.alogin-input-otp {
  font-family: 'SF Mono', Menlo, Consolas, monospace;
  font-size: 24px;
  letter-spacing: 8px;
  text-align: center;
}
/* Login-verification step. Deliberately built from the SAME tokens as the rest
 * of the card (--login-*, --text-primary, --border-default) so every tenant's
 * existing overrides reach it with no per-app work: this blueprint ships to the
 * whole fleet and must look native in all of them. */
/* The inner <form> deliberately does NOT reuse .alogin-form: it is nested
 * inside the block that owns show/hide, and a tenant theme that puts spacing on
 * .alogin-form would apply it twice. */
.alogin-verify-form {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.alogin-verify-target {
  margin: 0 0 4px 0;
  font-size: 14px;
  font-weight: 600;
  line-height: 1.45;
  color: var(--text-primary, #0f172a);
  overflow-wrap: anywhere;   /* a long masked address must not widen the card */
}
.alogin-verify-note {
  margin: 0 0 6px 0;
  /* The blueprint's prose tier (.alogin-hint), not its metadata tier: this line
   * is what pre-empts the support call from the ~1-in-4 Outlook inbox-placement
   * miss, so it has to be read, not merely present. */
  font-size: 13px;
  line-height: 1.45;
  color: var(--text-primary, #0f172a);
  opacity: 0.8;
}
.alogin-verify-note.is-hidden { display: none; }
.alogin-verify-status {
  margin: 0;
  font-size: 12px;
  line-height: 1.45;
  min-height: 1em;           /* reserve the line so a status never shifts layout */
  color: var(--text-primary, #0f172a);
  opacity: 0.75;
}
.alogin-verify-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 2px;
}
/* The back link is a different KIND of action from the resend/switch row —
 * give it its own breathing room instead of stacking it flush underneath. */
[data-verification-root] .alogin-back { margin-top: 14px; }
.alogin-verify-actions .alogin-chip { flex: 1 1 auto; }
.alogin-chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 10px 12px;
  min-height: 44px;          /* WCAG 2.2 AA touch target */
  border: 1px solid var(--border-default, #cbd5e1);
  border-radius: var(--login-input-radius, 8px);
  background: var(--surface-card, #ffffff);
  color: var(--login-link-color, var(--accent, #2563eb));
  font-size: 13px;
  font-weight: 600;
  line-height: 1.3;
  text-align: center;
  cursor: pointer;
  transition: background-color 120ms, border-color 120ms;
}
/* Hover-gated: without the guard Android leaves the chip stuck in its hover
 * paint after a tap. (The older rules in this file predate the convention;
 * they are left alone rather than re-skinned fleet-wide from this change.) */
@media (hover: hover) and (pointer: fine) {
  .alogin-chip:hover:not(:disabled) {
    background: rgba(0, 0, 0, 0.03);
    border-color: var(--text-primary, #94a3b8);
  }
}
.alogin-chip:disabled {
  /* Not 0.55: the label IS the state ("Resend in 12s"), so it must stay
   * readable while dimmed. */
  opacity: 0.72;
  cursor: not-allowed;
  color: var(--text-primary, #0f172a);
}
able-login[theme-variant="dark"] .alogin-chip {
  background: #1e293b;
  border-color: #334155;
}
.alogin-passkey-prompt {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 16px;
  margin-top: 8px;
  background: var(--surface-card, #ffffff);
  border: 1px solid var(--border-default, #e2e8f0);
  border-radius: var(--login-input-radius, 8px);
}
.alogin-passkey-prompt.is-hidden { display: none; }
.alogin-passkey-prompt-title {
  margin: 0;
  font-size: 14px;
  font-weight: 600;
  color: var(--text-primary, #0f172a);
}
.alogin-passkey-prompt-body {
  margin: 0;
  font-size: 13px;
  line-height: 1.45;
  color: var(--text-primary, #0f172a);
  opacity: 0.75;
}
.alogin-passkey-prompt-actions {
  display: flex;
  gap: 8px;
  margin-top: 4px;
}
.alogin-passkey-prompt-actions .alogin-submit { flex: 1; margin: 0; }
.alogin-passkey-prompt-actions .alogin-back { flex: 1; margin: 0; min-height: 44px; }
.alogin-passkey {
  margin-top: 4px;
  padding: 12px 16px;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  border: 1px solid var(--border-default, #cbd5e1);
  border-radius: var(--login-input-radius, 8px);
  background: var(--surface-card, #ffffff);
  color: var(--text-primary, #0f172a);
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  transition: background-color 120ms, border-color 120ms;
}
.alogin-passkey:hover:not(:disabled) {
  background: rgba(0, 0, 0, 0.03);
  border-color: var(--text-primary, #94a3b8);
}
.alogin-passkey:disabled { opacity: 0.6; cursor: not-allowed; }
.alogin-passkey.is-primary {
  background: var(--login-button-bg, var(--accent, #2563eb));
  color: var(--login-button-color, #ffffff);
  border-color: transparent;
}
.alogin-passkey.is-primary:hover:not(:disabled) {
  background: var(--login-button-bg-hover, var(--accent-hover, #1d4ed8));
  border-color: transparent;
}
.alogin-passkey-fallback {
  display: block;
  margin-top: 6px;
  font-size: 12px;
  text-align: center;
  background: transparent;
  border: 0;
  cursor: pointer;
  color: var(--login-link-color, var(--accent, #2563eb));
}
.alogin-passkey-fallback:hover { text-decoration: underline; }
.alogin-remember {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  user-select: none;
  cursor: pointer;
}
.alogin-remember input[type="checkbox"] {
  margin: 0;
  cursor: pointer;
}
.alogin-submit {
  margin-top: 4px;
  padding: 12px 16px;
  min-height: 48px;       /* 48px on mobile feels right for thumb taps */
  border: 0;
  border-radius: var(--login-input-radius, 8px);
  background: var(--login-button-bg, var(--accent, #2563eb));
  color: var(--login-button-color, #ffffff);
  font-size: 15px;
  font-weight: 600;
  cursor: pointer;
  transition: background-color 120ms, transform 80ms;
}
.alogin-submit:hover:not(:disabled) {
  background: var(--login-button-bg-hover, var(--accent-hover, #1d4ed8));
}
.alogin-submit:active:not(:disabled) { transform: translateY(1px); }
.alogin-submit:disabled { opacity: 0.6; cursor: not-allowed; }
.alogin-back {
  margin-top: 4px;
  padding: 8px 12px;
  background: transparent;
  color: var(--login-link-color, var(--accent, #2563eb));
  border: 0;
  font-size: 13px;
  cursor: pointer;
  text-align: center;
}
.alogin-back:hover { text-decoration: underline; }
.alogin-hint {
  margin: 0 0 6px 0;
  font-size: 13px;
  line-height: 1.45;
  color: var(--text-primary, #0f172a);
  opacity: 0.8;
}
.alogin-hint.is-hidden { display: none; }
.alogin-consent {
  margin: 10px 0 0 0;
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--text-muted, #64748b);
}
.alogin-consent a {
  color: inherit;
  text-decoration: underline;
}
.alogin-consent-check {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin: 10px 0 0 0;
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--text-primary, #0f172a);
  cursor: pointer;
}
.alogin-consent-check input[type="checkbox"] {
  flex: 0 0 auto;
  margin-top: 1px;
  /* 18px + an explicit ring: the UNCHECKED control must read as an empty box at a
     glance (the 10DLC reviewer flags a control that is small or hard to parse). */
  width: 18px;
  height: 18px;
  outline: 1.5px solid var(--border-strong, #9aa3ad);
  outline-offset: -1.5px;
  border-radius: 4px;
  accent-color: var(--accent, #1b2a4a);
  /* The card is light regardless of the page scheme; without this, a page-level
     color-scheme: dark paints the UNCHECKED native box as a solid dark square —
     which reads as CHECKED. The consent artifact must be unambiguous. */
  color-scheme: light;
}
.alogin-qr {
  display: flex;
  justify-content: center;
  padding: 12px;
  background: #ffffff;
  border-radius: 8px;
  border: 1px solid var(--border-default, #e2e8f0);
}
.alogin-qr.is-hidden { display: none; }
.alogin-qr-img { width: 200px; height: 200px; display: block; }
.alogin-error {
  margin: 12px 0 0 0;
  padding: 8px 12px;
  font-size: 13px;
  border-radius: 6px;
  background: rgba(220, 38, 38, 0.08);
  color: var(--login-error-color, #dc2626);
  border: 1px solid rgba(220, 38, 38, 0.2);
  display: none;
}
.alogin-error.is-visible { display: block; }
.alogin-forgot {
  display: block;
  margin-top: 14px;
  font-size: 13px;
  text-align: center;
  color: var(--login-link-color, var(--accent, #2563eb));
  text-decoration: none;
}
.alogin-forgot:hover { text-decoration: underline; }
/* WS-6 — the code-only door. Same link vocabulary as "Forgot password?" — it
   sits directly under it as a tight PAIR of quiet alternative routes. No rule
   between them: the original hairline (border-top on the input-border token)
   rendered as a heavy full-width line on dark themes and stacked a THIRD
   divider between the forgot link and the OR separator (seen live on
   tenniswest 2026-08-21). The OR divider below already owns the separation
   from the SSO block; two links don't need chrome to read as a list. */
.alogin-passwordless {
  display: block;
  margin-top: 8px;
  font-size: 13px;
  text-align: center;
  color: var(--login-link-color, var(--accent, #2563eb));
  text-decoration: none;
}
.alogin-passwordless:hover { text-decoration: underline; }
.alogin-divider {
  display: flex;
  align-items: center;
  margin: 20px 0 16px 0;
  font-size: 12px;
  color: var(--text-primary, #0f172a);
  opacity: 0.6;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.alogin-divider::before, .alogin-divider::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--border-default, #e2e8f0);
}
.alogin-divider::before { margin-right: 12px; }
.alogin-divider::after  { margin-left: 12px; }
.alogin-alternatives { display: flex; flex-direction: column; gap: 8px; }
.alogin-alt-button {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 12px 14px;
  min-height: 44px;       /* WCAG 2.2 AA touch target */
  border: 1px solid var(--border-default, #cbd5e1);
  border-radius: var(--login-input-radius, 8px);
  background: var(--surface-card, #ffffff);
  color: var(--text-primary, #0f172a);
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  transition: background-color 120ms, border-color 120ms;
  text-decoration: none;
}
.alogin-alt-button:hover {
  background: rgba(0, 0, 0, 0.03);
  border-color: var(--text-primary, #94a3b8);
}
.alogin-alt-icon {
  display: inline-flex;
  width: 18px;
  height: 18px;
  align-items: center;
  justify-content: center;
  font-size: 16px;
}
.alogin-footer {
  margin-top: 20px;
  text-align: center;
  font-size: 12px;
  color: var(--text-primary, #64748b);
  opacity: 0.75;
}
.alogin-footer a {
  color: var(--login-link-color, var(--accent, #2563eb));
  text-decoration: none;
}
.alogin-footer a:hover { text-decoration: underline; }

/* Theme variant: dark */
able-login[theme-variant="dark"] {
  background: var(--login-bg, #0f172a);
  color: #e2e8f0;
}
able-login[theme-variant="dark"] .alogin-card {
  background: var(--login-card-bg, #1e293b);
}
able-login[theme-variant="dark"] .alogin-input {
  background: #0f172a;
  color: #e2e8f0;
  border-color: var(--login-input-border, #334155);
}
able-login[theme-variant="dark"] .alogin-alt-button {
  background: #1e293b;
  color: #e2e8f0;
  border-color: #334155;
}
able-login[theme-variant="dark"] .alogin-alt-button:hover {
  background: #283549;
}

/* Theme variant: brand-purple */
able-login[theme-variant="brand-purple"] {
  --login-button-bg: #7c3aed;
  --login-button-bg-hover: #6d28d9;
  --login-link-color: #7c3aed;
}

@media (max-width: 480px) {
  /* Mobile shell: hug the top with a comfortable gutter, let the card take
   * the available width minus 16px each side. */
  .alogin-shell { padding: 16px; align-items: flex-start; padding-top: 32px; }
  .alogin-card  {
    padding: 28px 20px;
    /* Override the desktop clamp so we always size to viewport-minus-gutter
     * on small phones (overrides the min(440px, calc(100vw - 32px)) above). */
    max-width: var(--login-card-max-width, calc(100vw - 32px));
  }
  .alogin-title { font-size: 18px; margin-bottom: 20px; }
}


/* able-matrix-grid */

able-matrix-grid { display: block; }
.amg-wrap {
  overflow: auto;
  scrollbar-width: auto;
  scrollbar-color: var(--slate-300, #cbd5e1) var(--slate-100, #f1f5f9);
}
.amg-wrap::-webkit-scrollbar { width: 14px; height: 14px; }
.amg-wrap::-webkit-scrollbar-track { background: var(--slate-100, #f1f5f9); }
.amg-wrap::-webkit-scrollbar-thumb {
  background: var(--slate-300, #cbd5e1); border-radius: 8px;
  border: 3px solid var(--slate-100, #f1f5f9); min-height: 60px; min-width: 60px;
}
.amg-wrap::-webkit-scrollbar-thumb:hover { background: var(--slate-400, #94a3b8); }
.amg-table {
  border-collapse: separate; border-spacing: 0; width: max-content; min-width: 100%;
  font-size: 10.5px; table-layout: fixed;
}
.amg-th, .amg-td {
  border-bottom: 1px solid var(--border-subtle, #f1f5f9);
  vertical-align: middle; white-space: nowrap;
}
/* sticky header (each row pinned to top) */
.amg-thead .amg-th {
  position: sticky; top: 0; z-index: 3;
  background: var(--surface-subtle, #f1f5f9);
}
/* lead column headers/cells */
.amg-lead {
  font-size: 10px; font-weight: 700;
  color: var(--text-muted, #64748b); text-align: left;
}
.amg-lead-num { text-align: center; }
.amg-td.amg-lead { font-weight: normal; color: var(--text-primary, #1e293b); padding: 8px 10px; }
.amg-th.amg-lead { padding: 8px 10px; }
/* sticky left columns (offsets applied inline via style="left:Npx") */
.amg-sticky { position: sticky; z-index: 2; background: var(--surface-card, #fff); }
.amg-thead .amg-sticky { z-index: 5; background: var(--surface-subtle, #f1f5f9); }
.amg-sticky-last { box-shadow: inset -1px 0 0 0 var(--border-default, #e2e8f0); }
.amg-tbody .amg-tr:hover .amg-sticky { background: var(--surface-hover, #f8fafc); }

/* sort affordance */
.amg-sort-btn {
  display: inline-flex; align-items: center; gap: 6px; width: 100%;
  background: transparent; border: 0; padding: 0; cursor: pointer;
  font: inherit; color: inherit; text-align: left; text-transform: uppercase;
  letter-spacing: 0.04em; font-weight: 700;
}
.amg-sort-btn:hover { color: var(--text-primary, #1e293b); }
/* sort glyph — identical to the shared payroll .pa-sort-ind (⇕ neutral, ↑/↓ active, blue) */
.amg-sort-ind::after {
  content: '⇕'; margin-left: 4px; font-size: 9px; opacity: 0.4;
  display: inline-block; vertical-align: middle;
}
.amg-sort-ind.is-asc::after  { content: '↑'; opacity: 1; color: var(--blue-600, #2563eb); }
.amg-sort-ind.is-desc::after { content: '↓'; opacity: 1; color: var(--blue-600, #2563eb); }

/* avatar + pill */
.amg-emp-cell { display: flex; align-items: center; gap: var(--space-2, 8px); min-width: 0; }
.amg-avatar {
  width: 28px; height: 28px; flex: none; border-radius: 50%;
  background: var(--blue-50, #eff6ff); color: var(--blue-700, #1d4ed8);
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 10px; font-weight: 700;
  text-transform: uppercase;
}
.amg-emp-name {
  min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  font-weight: 600; color: var(--text-primary, #1e293b);
  text-transform: capitalize;
}
.amg-pill {
  display: inline-block; padding: 2px 10px; border-radius: var(--radius-full, 999px);
  font-size: 10px; font-weight: 600;
  background: var(--slate-100, #f1f5f9); color: var(--text-muted, #475569);
}

/* day banner (matrix column header) */
.amg-day-banner {
  width: 60px; min-width: 60px; max-width: 60px; text-align: center;
  font-family: var(--font-mono, monospace); font-weight: 700;
  color: var(--text-primary, #1e293b); padding: 6px 4px 5px;
  border-left: 1px solid var(--border-default, #e2e8f0);
}
.amg-day-dow { display: block; font-size: 9.5px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-muted, #64748b); }
.amg-day-md { display: block; font-size: 10px; }
.amg-day-ld { display: flex; justify-content: space-around; font-size: 8.5px; color: var(--text-subtle, #94a3b8); margin-top: 2px; }

/* matrix body cell */
.amg-cell { width: 60px; min-width: 60px; max-width: 60px; text-align: center; padding: 5px 4px; border-left: 1px solid var(--border-subtle, #f1f5f9); }
.amg-cell-pair { display: flex; gap: 6px; align-items: center; justify-content: center; }
/* checkbox — identical to attendance .att-check: 16px, appearance:none, top=blue /
   bottom=indigo when checked, ✓ rendered via a masked checkmark SVG. */
.amg-check {
  -webkit-appearance: none; appearance: none; width: 16px; height: 16px; margin: 0; flex-shrink: 0;
  border-radius: var(--radius-sm, 4px); border: 1.5px solid var(--slate-300, #cbd5e1);
  background: var(--surface-card, #fff); cursor: pointer; outline: none;
  transition: all var(--transition-fast, .15s ease);
  display: inline-flex; align-items: center; justify-content: center; vertical-align: middle;
}
.amg-check:hover { border-color: var(--blue-400, #60a5fa); background: var(--blue-50, #eff6ff); }
.amg-check:focus-visible { box-shadow: 0 0 0 3px rgba(37,99,235,0.25); }
.amg-check:checked { box-shadow: 0 1px 2px 0 rgba(15, 23, 42, 0.16); }
.amg-check-top:checked { background: var(--blue-600, #2563eb); border-color: var(--blue-600, #2563eb); }
.amg-check-top:checked:hover { background: var(--blue-700, #1d4ed8); border-color: var(--blue-700, #1d4ed8); }
.amg-check-bottom:checked { background: var(--indigo-700, #4338ca); border-color: var(--indigo-700, #4338ca); }
.amg-check-bottom:checked:hover { background: var(--indigo-800, #3730a3); border-color: var(--indigo-800, #3730a3); }
.amg-check:checked::after {
  content: ''; width: 11px; height: 11px; background: var(--surface-card, #fff);
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'><polyline points='20 6 9 17 4 12'/></svg>") no-repeat center / contain;
          mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'><polyline points='20 6 9 17 4 12'/></svg>") no-repeat center / contain;
}
.amg-cell-input {
  width: 100%; box-sizing: border-box; text-align: right; padding: 4px 6px;
  border: 1px solid var(--border-default, #e2e8f0); border-radius: var(--radius-md, 6px);
  background: var(--surface-card, #fff); font-family: var(--font-mono, monospace);
  font-size: 10px;
}

/* week group bands — Wk1 blue, Wk2 green (the Non Tip Share convention) */
.amg-group-banner {
  text-align: center; font-size: 10px; font-weight: 700;
  letter-spacing: 0.06em; text-transform: uppercase; padding: 5px 4px;
  border-bottom: 1px solid var(--border-default, #e2e8f0);
}
.amg-group-0, .amg-day-banner.amg-group-0, .amg-cell.amg-group-0 { background: var(--blue-50, #eff6ff); }
.amg-group-1, .amg-day-banner.amg-group-1, .amg-cell.amg-group-1 { background: var(--emerald-50, #ecfdf5); }
/* The banner row carries the SAME band tint as the day-banner + cells below it —
   without an explicit background here, .amg-thead .amg-th's header bg (0,2,0)
   outranks the bare .amg-group-N (0,1,0) and the band shows a "cut": the WK label
   row reads as plain header while the rows under it are tinted (visible notch
   where a trailing summary column like Period Total begins). */
.amg-group-banner.amg-group-0 { color: var(--blue-700, #1d4ed8); background: var(--blue-50, #eff6ff); }
.amg-group-banner.amg-group-1 { color: var(--emerald-600, #059669); background: var(--emerald-50, #ecfdf5); }
/* week boundary divider on the first column of each new group */
.amg-group-divider { border-left: 2px solid var(--border-strong, #94a3b8) !important; }

/* ---- Today indicator — amber wash + left rail on today's column ----
   (0,3,0) compound selectors so today outranks the week-band tints
   (.amg-day-banner.amg-group-N / .amg-cell.amg-group-N at 0,2,0) regardless
   of declaration order. Mirrors the original hand-table attendance look. */
.amg-th.amg-day-banner.amg-day-today {
  background: var(--amber-50, #fffbeb);
  box-shadow: inset 3px 0 0 0 var(--amber-300, #fcd34d);
}
.amg-th.amg-day-banner.amg-day-today .amg-day-md { color: var(--amber-600, #d97706); }
.amg-td.amg-cell.amg-cell-today {
  background: rgba(245, 158, 11, 0.08);
  box-shadow: inset 3px 0 0 0 var(--amber-300, #fcd34d);
}

/* ---- Dark mode adaptation ----------------------------------------------------
   The blue-50/emerald-50 week tints + slate scrollbars/avatar are ABSOLUTE light
   primitives — they don't flip, so in dark mode the matrix renders near-white
   columns with near-white (invisible) date labels. The blueprint injects this
   stylesheet into document.head (global), so [data-theme='dark'] (on <html>)
   matches. Re-tint the week bands by mixing the hue into the FLIPPING surface
   token so the column reads as a subtle dark blue/green and the date labels stay
   legible. Light mode is untouched (these rules only apply under data-theme=dark). */
:root[data-theme='dark'] .amg-group-0,
:root[data-theme='dark'] .amg-day-banner.amg-group-0,
:root[data-theme='dark'] .amg-cell.amg-group-0 {
  background: color-mix(in srgb, var(--blue-500, #3b82f6) 20%, var(--surface-card, #1e293b));
}
:root[data-theme='dark'] .amg-group-1,
:root[data-theme='dark'] .amg-day-banner.amg-group-1,
:root[data-theme='dark'] .amg-cell.amg-group-1 {
  background: color-mix(in srgb, var(--emerald-500, #10b981) 20%, var(--surface-card, #1e293b));
}
:root[data-theme='dark'] .amg-group-banner.amg-group-0 { color: var(--blue-300, #93c5fd); }
:root[data-theme='dark'] .amg-group-banner.amg-group-1 { color: var(--emerald-300, #6ee7b7); }
/* scrollbar — light track/thumb → dark */
:root[data-theme='dark'] .amg-wrap { scrollbar-color: var(--slate-600, #475569) var(--slate-800, #1e293b); }
:root[data-theme='dark'] .amg-wrap::-webkit-scrollbar-track { background: var(--slate-800, #1e293b); }
:root[data-theme='dark'] .amg-wrap::-webkit-scrollbar-thumb { background: var(--slate-600, #475569); border-color: var(--slate-800, #1e293b); }
:root[data-theme='dark'] .amg-wrap::-webkit-scrollbar-thumb:hover { background: var(--slate-500, #64748b); }
/* avatar — pale blue chip → translucent blue that reads on the dark surface */
:root[data-theme='dark'] .amg-avatar {
  background: color-mix(in srgb, var(--blue-500, #3b82f6) 30%, var(--surface-card, #1e293b));
  color: var(--blue-200, #bfdbfe);
}
/* checkbox hover — pale blue flash → subtle dark blue */
:root[data-theme='dark'] .amg-check:hover {
  border-color: var(--blue-400, #60a5fa);
  background: color-mix(in srgb, var(--blue-500, #3b82f6) 24%, var(--surface-card, #1e293b));
}
/* today — amber-50 banner is an absolute light primitive; re-mix on the dark surface */
:root[data-theme='dark'] .amg-th.amg-day-banner.amg-day-today,
:root[data-theme='dark'] .amg-td.amg-cell.amg-cell-today {
  background: color-mix(in srgb, var(--amber-500, #f59e0b) 22%, var(--surface-card, #1e293b));
  box-shadow: inset 3px 0 0 0 var(--amber-500, #f59e0b);
}
:root[data-theme='dark'] .amg-th.amg-day-banner.amg-day-today .amg-day-md { color: var(--amber-300, #fcd34d); }

/* currency cell host (able-currency-input is display:inline by default → must
   be blockified to fill its cell + the inner .aci-input styled here so pages
   don't have to re-style it) */
.amg-cell-input-host { display: block; width: 100%; min-width: 0; }
.amg-cell-input-host .aci-input {
  width: 100%; box-sizing: border-box; text-align: right; padding: 3px 6px;
  border: 1px solid var(--border-default, #e2e8f0); border-radius: var(--radius-md, 6px);
  background: var(--surface-card, #fff); font-family: var(--font-mono, monospace);
  font-size: 10px;
}
/* currency-pair: stacked top/bottom inputs with a short leading label */
.amg-cell-pair-cur { display: flex; flex-direction: column; gap: 3px; }
.amg-cur-line { display: flex; align-items: center; gap: 4px; }
.amg-cur-lbl {
  font-size: 9px; font-weight: 600; width: 36px; flex-shrink: 0;
  color: var(--text-muted, #64748b); text-transform: uppercase; letter-spacing: 0.02em;
}
.amg-cur-line .amg-cell-input-host { flex: 1; }
/* read-only derived lines under the editable cell value (Adj / Host / Total) */
.amg-cell-derived-block {
  margin-top: 4px; padding-top: 4px; display: flex; flex-direction: column; gap: 2px;
  border-top: 1px dashed var(--border-default, #e2e8f0);
}
.amg-cell-derived { display: flex; justify-content: space-between; gap: 6px; font-size: 9.5px; font-family: var(--font-mono, monospace); }
.amg-cd-lbl { color: var(--text-subtle, #94a3b8); text-transform: uppercase; letter-spacing: 0.02em; }
.amg-cd-val { color: var(--text-primary, #1e293b); font-weight: 500; }
/* trailing summary columns (e.g. Period Total) — fixed width so they don't
   absorb all the table slack under table-layout:fixed (which would leave the
   day columns narrow + a big empty summary). Capping here lets the day columns
   stretch to fill the width. */
.amg-th.amg-summary-h { text-align: right; width: 124px; }
.amg-td.amg-summary {
  text-align: right; padding: 0 10px; white-space: nowrap; width: 124px;
  font-family: var(--font-mono, monospace); font-weight: 600;
  font-size: 10px; color: var(--text-primary, #1e293b);
}


/* able-month-card */

/* ⚠ ACCENT-COLOURED TEXT USES --accent-text, NEVER --accent.
   Measured on the live build theme 2026-08-24: --accent is a FILL token and
   computes the SAME #2563eb in both modes, so accent text sits at 3.35:1 on the
   dark card ground — under AA. --accent-text re-binds (#2563eb / #A5B4FC) and
   measures 5.17 light / 8.68 dark. Because the TOKEN flips, no media query and no
   [data-theme] guard is needed here at all, and the tenant keeps its own colour
   instead of a hard-coded blue. Fills and borders keep --accent. */
able-month-card { display: block; max-width: 380px; margin: 8px 0; }
able-month-card .amc {
  /* TONE (CALENDAR-REVAMP.md 2c): ten closed NAMES; the THEME owns the values.
     These are only fallbacks so the card is legible in a theme that has not
     declared --cal-tone-* yet. A native renderer maps the same ten names. */
  --cal-tone-primary:  var(--cal-tone-primary-x,  var(--accent));
  --cal-tone-accent:   var(--cal-tone-accent-x,   var(--accent));
  --cal-tone-info:     var(--cal-tone-info-x,     var(--info));
  --cal-tone-warn:     var(--cal-tone-warn-x,     var(--warning));
  --cal-tone-critical: var(--cal-tone-critical-x, var(--danger));
  --cal-tone-money:    var(--cal-tone-money-x,    var(--success));
  --cal-tone-travel:   var(--cal-tone-travel-x,   var(--info));
  --cal-tone-muted:    var(--cal-tone-muted-x,    var(--text-muted));
  --cal-tone-social:   var(--cal-tone-social-x,   var(--success));
  --cal-tone-neutral:  var(--cal-tone-neutral-x,  var(--text-muted));
  background: var(--surface-card, #fff);
  border: 1px solid var(--border-default, #e5e7eb);
  border-radius: var(--radius-xl, 14px);
  box-shadow: var(--shadow-sm, 0 1px 2px rgba(0,0,0,.06));
  padding: 12px 14px;
  font-family: var(--font-sans, sans-serif);
  color: var(--text-primary, #1d1c17);
}
able-month-card .amc-eyebrow {
  font-size: 10px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase;
  color: var(--accent-text, var(--accent, #1b2a4a)); margin: 0 0 2px;
}
able-month-card .amc-title {
  font-size: 17px; line-height: 1.2; font-weight: 700; margin: 0 0 8px;
}
able-month-card .amc-head {
  display: grid; grid-template-columns: repeat(7, 1fr); gap: 1px; padding-bottom: 3px;
}
able-month-card .amc-wd {
  text-align: center; font-size: 9px; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--text-muted, #6b7280);
}
/* Six rows ALWAYS — the card's height cannot jump between a 4-row and a 6-row
   month, which in a chat thread would reflow every bubble under it. */
able-month-card .amc-grid {
  display: grid; grid-template-columns: repeat(7, 1fr);
  grid-template-rows: repeat(6, minmax(30px, auto)); gap: 1px;
}
able-month-card .amc-cell {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 2px; border: 0; background: transparent; padding: 2px 0;
  border-radius: var(--radius-sm, 4px);
  font-family: inherit; color: var(--text-primary, #1d1c17);
  font-variant-numeric: tabular-nums; cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
able-month-card .amc-cell[data-muted="true"] .amc-num { color: var(--text-subtle, #9ca3af); }
able-month-card .amc-cell[data-heavy="true"] .amc-num { font-weight: 800; }
able-month-card .amc-cell[data-today="true"] .amc-num { color: var(--accent-text, var(--accent, #1b2a4a)); font-weight: 800; }
able-month-card .amc-cell[data-selected="true"] { background: var(--accent, #1b2a4a); }
able-month-card .amc-cell[data-selected="true"] .amc-num { color: var(--on-accent, #fff); }
able-month-card .amc-cell:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
@media (hover: hover) and (pointer: fine) {
  able-month-card .amc-cell:hover { background: var(--surface-hover, #f1f5f9); }
}
able-month-card .amc-num { font-size: 11px; line-height: 1.1; }
/* the mark slot is ALWAYS reserved, so a day gaining a mark cannot shift the grid */
able-month-card .amc-marks {
  display: flex; align-items: center; justify-content: center; gap: 2px;
  height: 8px; min-height: 8px;
}
able-month-card .amc-bar { height: 3px; border-radius: 999px; background: var(--cal-tone-primary); }
able-month-card .amc-dot { width: 4px; height: 4px; border-radius: 50%; background: var(--amc-c, var(--text-muted)); }
able-month-card .amc-cell[data-selected="true"] .amc-bar,
able-month-card .amc-cell[data-selected="true"] .amc-dot { background: var(--on-accent, #fff); opacity: .9; }
able-month-card .amc-sel { margin: 10px 0 0; padding-top: 8px;
  border-top: 1px solid var(--border-subtle, #f1f5f9);
  display: flex; flex-direction: column; gap: 2px; }
able-month-card .amc-sel-h {
  margin: 0 0 4px; font-size: 10px; font-weight: 700; letter-spacing: .1em;
  text-transform: uppercase; color: var(--text-muted, #6b7280); }
able-month-card .amc-sel-row {
  display: grid; grid-template-columns: 58px 1fr auto; gap: 6px; align-items: baseline;
  border-left: 3px solid var(--amc-c, var(--text-muted)); padding-left: 5px; }
/* the CLASS — carried by the row's own hue, so it needs weight, not a second colour */
able-month-card .amc-sel-c {
  font-weight: 700; letter-spacing: 0.01em;
  color: var(--amc-c, var(--text-primary)); margin-right: 5px; }
/* ⚠ ONE RULE PER SELECTOR. Two rules for one class at equal specificity is how the
   music FAB escaped its grid cell (able-claw-music.js .claw-ms-play, 2026-08-22) —
   the later declaration silently won and nothing reported it. So grid-column lives
   WITH each part's own properties, never in a second block below.

   PINNED COLUMNS, not append order: a row with no time used to slide its title into
   the 58px time column, so an all-day row's title started where every other row's
   time did. Pinning also keeps the link in column 3 when the time is absent. */
able-month-card .amc-sel-src {
  grid-column: 3;
  font-size: 11px; line-height: 1; text-decoration: none; padding: 0 2px;
  color: var(--amc-c, var(--text-muted, #6b7280)); }
able-month-card .amc-sel-src:focus-visible {
  outline: 2px solid var(--accent, #1b2a4a); outline-offset: 1px; }
able-month-card .amc-sel-t {
  grid-column: 1;
  font-size: 10px; font-variant-numeric: tabular-nums; color: var(--text-muted, #6b7280);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
able-month-card .amc-sel-n {
  grid-column: 2;
  font-size: 12px; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
able-month-card .amc-sel-more {
  margin: 4px 0 0; font-size: 11px; font-weight: 600; color: var(--text-muted, #6b7280); }
able-month-card .amc-summary { margin: 12px 0 0; font-size: 12px; color: var(--text-muted, #6b7280); }
able-month-card .amc-open {
  display: inline-block; margin: 10px 0 0;
  font-size: 12px; font-weight: 600; color: var(--accent-text, var(--accent, #1b2a4a)); text-decoration: none;
}
able-month-card .amc-open:hover { text-decoration: underline; }




/* able-month-grid */

able-month-grid { display: block; }
able-month-grid .amg-head {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 2px;
  padding: 0 0 var(--space-1, 4px);
}
able-month-grid .amg-wd {
  text-align: center;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--text-muted);
}
able-month-grid .amg-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 2px;
}
/* Six rows, always — the height cannot jump between months. */
able-month-grid .amg-grid { grid-template-rows: repeat(6, minmax(var(--amg-row-h, 44px), auto)); }
able-month-grid .amg-empty {
  padding: var(--space-6, 24px);
  text-align: center;
  color: var(--text-muted);
  font-size: 13px;
}


/* able-nav-labels */

able-nav-labels { display: none; }
.anl-bubble {
  position: fixed;
  top: 0; left: 0;
  z-index: 1000;
  padding: 6px 10px;
  border-radius: var(--radius-md, 6px);
  background: var(--surface-inverse, #18191b);
  color: var(--text-inverse, #fff);
  border: 1px solid var(--border-inverse, rgba(255,255,255,.1));
  box-shadow: var(--shadow-md, 0 4px 12px rgba(0,0,0,.18));
  font-family: var(--font-sans, system-ui, sans-serif);
  font-size: var(--font-sm, 13px);
  font-weight: 500;
  line-height: 1.3;
  white-space: nowrap;
  max-width: 260px;
  overflow: hidden;
  text-overflow: ellipsis;
  pointer-events: none;
  opacity: 0;
  transform: translate3d(0, 0, 0) scale(.97);
  transition: opacity 120ms ease, transform 120ms ease;
}
.anl-bubble.is-visible {
  opacity: 1;
  transform: translate3d(0, 0, 0) scale(1);
}
@media (prefers-reduced-motion: reduce) {
  .anl-bubble { transition: none; transform: none !important; }
  .anl-bubble.is-visible { transform: none !important; }
}


/* able-note-card */

/* Accent-coloured TEXT uses --accent-text, never --accent: --accent is a FILL token
   and can sit under AA on the dark card ground; --accent-text re-binds per mode.
   Fills and borders keep --accent. */
able-note-card { display: block; max-width: 460px; margin: 8px 0; }
/* The course edge is the chat cards' shared posture (able-day-card, able-week-card):
   a 3px left border in the course's own colour. */
able-note-card .antc {
  background: var(--surface-card, #fff);
  border: 1px solid var(--border-default, #e5e7eb);
  border-left: 3px solid var(--antc-c, var(--accent, #1b2a4a));
  border-radius: var(--radius-xl, 14px);
  box-shadow: var(--shadow-sm, 0 1px 2px rgba(0,0,0,.06));
  padding: 12px 14px;
  font-family: var(--font-sans, sans-serif);
  color: var(--text-primary, #1d1c17);
}
able-note-card .antc-eyebrow {
  font-size: 10px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase;
  color: var(--accent-text, var(--accent, #1b2a4a)); margin: 0 0 4px;
}
able-note-card .antc-main { display: grid; grid-template-columns: 1fr; gap: 10px; }
able-note-card .antc-main.has-thumb { grid-template-columns: 72px 1fr; }
able-note-card .antc-main.has-glyph { grid-template-columns: 44px 1fr; }
able-note-card .antc-thumb {
  width: 72px; height: 96px; border-radius: var(--radius-sm, 4px); overflow: hidden;
  background: var(--surface-subtle, #f8fafc); border: 1px solid var(--border-subtle, #f1f5f9);
}
able-note-card .antc-thumb img { display: block; width: 100%; height: 100%; object-fit: cover; }
/* The kind tile — the catalog glyph on a tint of the course colour. Emoji ignore
   the color property; the text-presentation symbols take the accent text token.
   No backticks anywhere in this literal: the first one ends it. */
able-note-card .antc-glyph {
  width: 44px; height: 44px; border-radius: var(--radius-md, 8px);
  display: grid; place-items: center;
  font-size: 22px; line-height: 1; font-weight: 600;
  background: color-mix(in srgb, var(--antc-c, var(--accent, #1b2a4a)) 12%, var(--surface-card, #fff));
  border: 1px solid var(--border-subtle, #f1f5f9);
  color: var(--accent-text, var(--accent, #1b2a4a));
  font-family: var(--font-sans, sans-serif), "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
}
able-note-card .antc-body { min-width: 0; }
able-note-card .antc-top {
  display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
}
able-note-card .antc-code {
  font-size: 11px; font-weight: 700; letter-spacing: .04em; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
  color: var(--text-secondary, #3f3f46);
}
able-note-card .antc-date {
  font-size: 11px; color: var(--text-muted, #6b7280);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
able-note-card .antc-title {
  font-size: 15px; line-height: 1.25; font-weight: 700; margin: 2px 0 0;
  color: var(--text-primary, #1d1c17); overflow-wrap: anywhere;
}
able-note-card .antc-meta {
  margin: 4px 0 0; font-size: 12px; color: var(--text-muted, #6b7280);
  font-variant-numeric: tabular-nums;
}
able-note-card .antc-meta-part { white-space: nowrap; }
able-note-card .antc-topic {
  display: inline-block; margin: 6px 0 0; padding: 1px 8px;
  border: 1px solid var(--border-default, #e5e7eb); border-radius: var(--radius-full, 999px);
  font-size: 11px; color: var(--text-secondary, #3f3f46);
}
able-note-card .antc-topic.is-suggested { border-style: dashed; color: var(--text-muted, #6b7280); }
able-note-card .antc-snippet {
  margin: 8px 0 0; font-size: 12.5px; line-height: 1.5;
  color: var(--text-secondary, #3f3f46); font-style: italic; overflow-wrap: anywhere;
}
/* The tap target is 44px tall; the box reaches into the card's bottom padding so
   the card keeps its height and the label its place. */
able-note-card .antc-open {
  display: inline-flex; align-items: center; min-height: 44px; margin: 4px 0 -10px;
  font-size: 12px; font-weight: 600;
  color: var(--accent-text, var(--accent, #1b2a4a)); text-decoration: none;
}
/* A skipped sheet's affordance reads as a state; a record's affordance is a label. */
able-note-card .antc-open.is-skipped,
able-note-card .antc-open--static { color: var(--text-muted, #6b7280); }
@media (hover: hover) and (pointer: fine) {
  able-note-card a.antc-open:hover { text-decoration: underline; }
}
able-note-card .antc-open:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--radius-sm, 4px);
}
/* The inline player rides inside the card's own chrome: a hairline above it, no
   second box, the full inner width for the controls. The card's counts line already
   carries the length and the control shows the total, so the head's duration badge
   stays hidden here. */
able-note-card able-audio .aclaw-audio {
  margin: 10px 0 0; padding: 10px 0 0; background: transparent; border: 0; border-radius: 0;
  border-top: 1px solid var(--border-subtle, #f1f5f9);
}
able-note-card able-audio .aclaw-audio__dur { display: none; }


/* able-notification-bell */

/* ── Colour contract (read before changing any colour here) ───────────────────
   FOREGROUND / GRAPHIC roles (text, icon stroke, link, the unread dot, a focus
   ring) MUST stay legible in BOTH themes. --accent (#1b2a4a) and
   --accent-secondary (#1d4ed8) do NOT re-bind in the dark theme, so either of
   them BARE on a dark surface lands around 2.4:1 — a WCAG failure. The
   theme-sanctioned chain for a brand-tinted foreground is
   var(--login-link-color, var(--accent, ...)): --login-link-color exists only
   in the dark block (#93c5fd) and falls through to --accent in light, exactly
   so a blueprint gets a visible-on-dark colour without every tenant having to
   redefine its brand. Informational graphics use --info (which DOES re-bind).
   SOLID FILL roles (the badge pill) keep the brand token and pair it with the
   matching --on-* token: the COUNT is what has to be readable, and white on
   #1d4ed8 is 6.70:1 in both themes. The pill's own edge against a dark shell is
   low-contrast by nature — that is what --anb-badge-ring is for (a host sets it
   to its chrome colour to halo the pill). Never a bare hex outside a var()
   fallback. */
able-notification-bell { display: inline-flex; }

/* The trigger — deliberately minimal. Host shells own the button chrome
   (background, size, colour) via their own scoped CSS; this only guarantees
   the badge has a positioning context and the icon is centred. */
.anb-btn {
  position: relative;
  display: inline-grid; place-items: center;
  width: 32px; height: 32px; padding: 0;
  border: 0; border-radius: 9px;
  background: transparent; color: currentColor;
  cursor: pointer;
}
.anb-btn svg { width: 18px; height: 18px; }

.anb-badge {
  position: absolute;
  top: var(--anb-badge-top, 2px); right: var(--anb-badge-right, 2px); z-index: 1;
  min-width: 16px; height: 16px; padding: 0 4px; border-radius: 999px;
  /* SOLID FILL — the brand token stays; the label rides its on-token. */
  background: var(--accent-secondary, var(--danger, #b91c1c));
  color: var(--on-accent-secondary, #fff);
  font-size: 9.5px; font-weight: 800; line-height: 16px; text-align: center;
  box-shadow: 0 0 0 2px var(--anb-badge-ring, transparent);
  pointer-events: none;
}
.anb-badge[hidden] { display: none; }

/* TOUCH: the trigger reaches the 44x44 floor (rule 29 §3.6). This belongs in the
   BLUEPRINT and not in each shell — the layout audit's own note on its
   tap-target check says a fix on platform blueprint chrome lands in the
   blueprint, and by the time it was measured the shells had solved it four
   different ways (ablecollect ships its own 44px .ah-bell .anb-btn, fleet sizes
   both of its triggers, tenniswest keeps a 30px box) while ableU's two bells sat
   at 32x32 on a phone. Those host rules are .x-bell .anb-btn, specificity
   (0,2,0) against this rule's (0,1,0), so every tenant that already made a
   deliberate choice keeps it and only the un-overridden triggers move. Scoped to
   pointer:coarse so the desktop square is untouched — the same idiom
   .aoff-close uses, and the same desktop-keeps-its-compaction ruling the ableU
   week header landed on (5491a29, 54dd7d7, 0fa4780).

   ⚠ THIS BLOCK MUST STAY BELOW .anb-badge. Both badge rules are (0,1,0), so the
   later one wins and a "tidier" move up next to .anb-btn silently kills the
   offset — which is exactly the bug this block shipped with on its first draft.
   The offset is the SAME 6px the box grew ((44-32)/2), which keeps the pill
   hugging the 18px glyph instead of drifting into the corner of a bigger button,
   and it moves the FALLBACK only: a host that tuned --anb-badge-top/right
   (fleet, pcp) still wins, because the var is read here exactly as above.

   ⚠ NO BACKTICKS ANYWHERE IN THIS LITERAL. This is a template literal: the first
   backtick ends it and the rest of the file parses as code, so the whole bundle
   dies for every tenant. bin/bundle-safety.mjs is what catches it; node --check
   does not. */
@media (pointer: coarse) {
  .anb-btn { width: 44px; height: 44px; }
  .anb-badge { top: var(--anb-badge-top, 8px); right: var(--anb-badge-right, 8px); }
}

/* ── The panel (singleton, appended to <body>) ─────────────────────────── */
.anb-panel[hidden] { display: none; }
.anb-panel {
  /* The bell IS a popover, so it rides the popover tier of the platform scale
     rather than a raw 900 (which is --z-tooltip, the top of the scale, and
     would let the panel paint over anything a shell layers above it). A shell
     with genuine fleet stacking needs sets --anb-z. */
  position: fixed; z-index: var(--anb-z, var(--z-popover, 800));
  width: var(--anb-panel-width, 400px); max-height: min(74dvh, 660px);
  display: flex; flex-direction: column;
  background: var(--surface-card, #fff);
  border: 1px solid var(--border-default, #e5e7eb);
  border-radius: 12px; overflow: hidden;
  box-shadow: var(--shadow-xl, 0 16px 48px rgba(15, 23, 42, .22));
}
.anb-h {
  display: flex; align-items: center; gap: 8px; padding: 11px 14px;
  border-bottom: 1px solid var(--border-subtle, #f1f5f9);
}
.anb-h b { font-size: 13.5px; font-weight: 700; color: var(--text-primary, #1d1c17); }
.anb-count {
  font-size: 10px; font-weight: 700; padding: 1px 7px; border-radius: 999px;
  background: color-mix(in srgb, var(--login-link-color, var(--accent, #1b2a4a)) 14%, var(--surface-card, #fff));
  color: var(--login-link-color, var(--accent, #1b2a4a));
}
.anb-count[hidden] { display: none; }
.anb-close {
  margin-left: auto; background: none; border: 0; cursor: pointer; padding: 4px;
  color: var(--text-muted, #6b7280); border-radius: 6px;
  display: grid; place-items: center;
}
.anb-list { overflow-y: auto; flex: 1 1 auto; }

.anb-item {
  display: block; position: relative;
  padding: 10px 12px 10px 14px; cursor: pointer;
  border-bottom: 1px solid var(--border-subtle, #f1f5f9);
}
.anb-item:last-child { border-bottom: 0; }

/* ── Row state (Facebook rows) ────────────────────────────────────────────
   The FACE is an <able-notification-card flat> (renderFeed), so the icon chip,
   the tones, the headline / eyebrow / snippet and the timestamp all live in that
   ONE element. This wrapper keeps only the row-level state the card has no
   business owning:
   · NOT-VISITED keeps a subtle brand tint (--unread) until the user CLICKS it
     (persisted across opens + reloads by the localStorage ledger + the server
     read stamp). The tint rides the dark-safe brand chain + color-mix against
     --surface-card, so it adapts to dark automatically; --anb-unread-bg is the
     shell knob (see Styling hooks).
   · VISITED drops the tint; the card it holds wears the read (dimmed) treatment
     instead (renderFeed passes the read attribute, styled by .anc--read there).
   · NEW-since-seen adds the transient .anb-dot ON TOP — a corner marker
     independent of visited (state 2, the pre-open seenAt logic). */
.anb-item--unread {
  background: var(--anb-unread-bg, color-mix(in srgb, var(--login-link-color, var(--accent, #1d4ed8)) 7%, var(--surface-card, #fff)));
}
.anb-dot {
  /* The "new since seen" dot — a transient GRAPHIC in the row's left gutter,
     beside the card's icon chip. It carries meaning on its own, so it follows the
     dark-safe foreground chain (recolourable via --anb-accent, itself dark-safe). */
  position: absolute; top: 22px; left: 5px;
  width: 7px; height: 7px; border-radius: 999px;
  background: var(--anb-accent, var(--login-link-color, var(--accent, #1b2a4a)));
}
.anb-x {
  position: absolute; right: 8px; bottom: 8px;
  background: none; border: 0; cursor: pointer;
  padding: 3px; border-radius: 6px; color: var(--text-muted, #6b7280); display: none;
}
/* Keyboard parity: pointer users get the X on :hover (below), but a
   keyboard-only user never generates a hover — without this rule a row could
   be focused and read out yet never dismissed. Unconditional (NOT inside the
   hover media query) precisely because it serves the no-hover case. */
.anb-item:focus-within .anb-x { display: grid; }
.anb-item:focus-visible,
.anb-x:focus-visible,
.anb-close:focus-visible {
  outline: 2px solid var(--login-link-color, var(--accent, #1b2a4a));
  outline-offset: -2px;
}
@media (hover: hover) and (pointer: fine) {
  .anb-item:hover { background: var(--surface-hover, #f8f8f9); }
  /* An unread row brightens its own tint on hover (11% instead of 7%) rather
     than flipping to the neutral surface-hover — more specific, so it wins. */
  .anb-item--unread:hover {
    background: var(--anb-unread-bg, color-mix(in srgb, var(--login-link-color, var(--accent, #1d4ed8)) 11%, var(--surface-card, #fff)));
  }
  .anb-item:hover .anb-x { display: grid; }
  .anb-x:hover {
    color: var(--danger, #b91c1c);
    background: color-mix(in srgb, var(--danger, #b91c1c) 10%, transparent);
  }
  .anb-close:hover { color: var(--text-primary, #1d1c17); background: var(--surface-hover, #f8f8f9); }
}
@media (hover: none) { .anb-x { display: grid; } }

.anb-empty { padding: 26px 16px; text-align: center; font-size: 12px; color: var(--text-muted, #6b7280); }
.anb-f {
  padding: 9px 14px; border-top: 1px solid var(--border-subtle, #f1f5f9);
  display: flex; justify-content: center;
}
.anb-f[hidden] { display: none; }
.anb-f a {
  font-size: 11.5px; font-weight: 600; text-decoration: none;
  color: var(--login-link-color, var(--accent, #1b2a4a));
}

/* ── Grouped rows (a run of consecutive same-kind feed items) ──────────────────
   A maximal run of >=2 feed items that share one kind (the domain event_key on the
   wire; 'dm' on the message arm) collapses into a .anb-group. It
   starts COLLAPSED: only the head (newest) member shows, and a full-width toggle
   strip below it carries a "x N" count + a chevron. Tapping the strip expands the
   run INLINE. Every member stays an ordinary .anb-item with its own dot / tint /
   dismiss / route (keyed by data-anb-i), so a single row behaves identically
   grouped or not — the group adds only the toggle chrome. The count text rides the
   always-dark-safe 10px chain (like .anb-count and the footer link — a brand tint
   on 10px text re-fails AA), never --anb-accent. */
.anb-group { display: block; position: relative; }
/* Collapsed: hide every member after the head. */
.anb-group[data-anb-collapsed] > .anb-item:not([data-anb-group-head]) { display: none; }
/* Collapsed, the head + the toggle share ONE divider (not two stacked hairlines). */
.anb-group[data-anb-collapsed] > .anb-item[data-anb-group-head] { border-bottom: 0; }

.anb-group-toggle {
  display: flex; align-items: center; justify-content: center; gap: 4px;
  width: 100%; padding: 7px 12px; border: 0;
  border-bottom: 1px solid var(--border-subtle, #f1f5f9);
  /* A faint brand band (not bare text) so the strip READS as a control distinct
     from the corner dismiss X — the count + chevron on the dark-safe brand chain. */
  background: color-mix(in srgb, var(--login-link-color, var(--accent, #1b2a4a)) 5%, var(--surface-card, #fff));
  cursor: pointer;
  color: var(--login-link-color, var(--accent, #1b2a4a));
  font-family: inherit; font-size: 10.5px; font-weight: 700; letter-spacing: .01em;
}
/* The touch tap target reaches the 44px floor (rule 29 §3.6) — the same coarse
   idiom .anb-btn uses; the desktop strip stays compact. */
@media (pointer: coarse) { .anb-group-toggle { min-height: 44px; } }
.anb-group-x { opacity: .75; margin-right: 1px; }
.anb-group-toggle svg { transition: transform var(--transition-base, .18s) var(--ease-out, ease); }
.anb-group:not([data-anb-collapsed]) .anb-group-toggle svg { transform: rotate(180deg); }
.anb-group-toggle:focus-visible {
  outline: 2px solid var(--login-link-color, var(--accent, #1b2a4a));
  outline-offset: -2px;
}
@media (hover: hover) and (pointer: fine) {
  .anb-group-toggle:hover {
    background: color-mix(in srgb, var(--login-link-color, var(--accent, #1b2a4a)) 11%, var(--surface-card, #fff));
  }
}
/* The last group in the list sheds its trailing divider — the footer owns the
   next border, matching .anb-item:last-child. */
.anb-list > .anb-group:last-child > .anb-group-toggle { border-bottom: 0; }

/* ── The inline feed (<able-notification-feed>) ────────────────────────────
   The panel's list without the popover: a bordered card, the same rows at the
   page's width, and a footer strip for the two list actions. */
able-notification-feed { display: block; }
.anb-feed {
  max-width: 980px;   /* a reading measure — a feed row is prose, not a grid row */
  background: var(--surface-card, #fff);
  border: 1px solid var(--border-default, #e5e7eb);
  border-radius: 12px; overflow: hidden;
}
.anb-feed .anb-list { overflow: visible; }
.anb-feed .anb-item { padding: 12px 14px 12px 16px; }
.anb-feed .anb-dot { top: 24px; left: 6px; }
.anb-feed-f {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 6px 8px; border-top: 1px solid var(--border-subtle, #f1f5f9);
}
.anb-feed-f[hidden] { display: none; }
.anb-feed-f button {
  font: inherit; font-size: 12px; font-weight: 600; cursor: pointer;
  padding: 6px 8px; border: 0; border-radius: 6px; background: none;
  color: var(--login-link-color, var(--accent, #1b2a4a));
}
.anb-feed-f button[hidden] { display: none; }
.anb-feed-f button:focus-visible {
  outline: 2px solid var(--login-link-color, var(--accent, #1b2a4a));
  outline-offset: -2px;
}
@media (hover: hover) and (pointer: fine) {
  .anb-feed-f button:hover { background: var(--surface-hover, #f8f8f9); }
}

/* Below 820px the CSS owns the geometry (the JS bails out of positioning) —
   !important beats the inline left/top a previous desktop open left behind. */
@media (max-width: 820px) {
  .anb-panel {
    left: 8px !important; right: 8px; width: auto;
    top: var(--anb-mobile-top, 62px) !important;
    max-height: calc(100dvh - var(--anb-mobile-top, 62px) - 16px);
  }
}


/* able-notification-card */

/* ── Colour contract (read before changing any colour here) — the able-notification-bell.js
   contract, verbatim, because these two are ONE face. FOREGROUND / GRAPHIC roles (icon
   stroke, the accent tint) MUST stay legible in BOTH themes: --accent (#1b2a4a) and
   --accent-secondary do NOT re-bind in dark, so either bare on a dark surface fails AA.
   The theme-sanctioned chain for a brand-tinted foreground is
   var(--login-link-color, var(--accent, ...)): --login-link-color exists only in the dark
   block and falls through to --accent in light. Informational graphics use --info (which
   DOES re-bind). Never a bare hex outside a var() fallback. NO BACKTICKS ANYWHERE IN THIS
   LITERAL — the first one ends it and the rest of the file parses as code, killing the whole
   bundle for every tenant (bin/bundle-safety.mjs catches it; node --check does not). */
able-notification-card { display: block; }

.anc {
  display: grid; grid-template-columns: auto 1fr auto; gap: 10px; align-items: start;
  text-decoration: none; color: var(--text-primary, #1d1c17);
  font-family: var(--font-sans, sans-serif);
  background: var(--surface-card, #fff);
  border: 1px solid var(--border-default, #e5e7eb);
  border-radius: var(--radius-xl, 14px);
  box-shadow: var(--shadow-sm, 0 1px 2px rgba(0,0,0,.06));
  padding: 12px 14px; margin: 8px 0; max-width: 460px;
}
/* The FLAT variant — a bell / list row. The mounting list item owns the padding,
   the divider, the tint and the click, so the card is pure face at full width. */
.anc--flat {
  border: 0; box-shadow: none; border-radius: 0; padding: 0; margin: 0;
  max-width: none; background: transparent;
}

.anc-ic {
  position: relative;
  width: 30px; height: 30px; border-radius: 999px;
  display: grid; place-items: center; margin-top: 1px;
  background: color-mix(in srgb, var(--info, #1d4ed8) 12%, var(--surface-card, #fff));
  color: var(--info, #1d4ed8);
}
.anc-ic svg { width: 15px; height: 15px; }
.anc-ic--accent {
  background: color-mix(in srgb, var(--login-link-color, var(--accent, #1b2a4a)) 14%, var(--surface-card, #fff));
  color: var(--login-link-color, var(--accent, #1b2a4a));
}
.anc-ic--info {
  background: color-mix(in srgb, var(--info, #1d4ed8) 12%, var(--surface-card, #fff));
  color: var(--info, #1d4ed8);
}
.anc-ic--warn {
  background: color-mix(in srgb, var(--warning, #b45309) 14%, var(--surface-card, #fff));
  color: var(--warning, #b45309);
}
.anc-ic--bad {
  background: color-mix(in srgb, var(--danger, #b91c1c) 12%, var(--surface-card, #fff));
  color: var(--danger, #b91c1c);
}
.anc-ic--good {
  background: color-mix(in srgb, var(--success, #15803d) 14%, var(--surface-card, #fff));
  color: var(--success, #15803d);
}
/* The resolved tick — a small check hugging the chip's lower-right, on the same
   success token the good tone uses, ringed by the card surface so it reads as a
   badge rather than a smear on the glyph. */
.anc-check {
  position: absolute; right: -3px; bottom: -3px;
  width: 14px; height: 14px; border-radius: 999px;
  display: grid; place-items: center;
  background: var(--success, #15803d); color: var(--on-success, #fff);
  box-shadow: 0 0 0 2px var(--surface-card, #fff);
}
.anc-check svg { width: 9px; height: 9px; }

.anc-main { min-width: 0; }
.anc-eyebrow {
  font-size: 9.5px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: var(--text-muted, #6b7280); display: block; margin-bottom: 1px;
}
.anc-title {
  font-size: 14px; line-height: 1.3; font-weight: 700; color: var(--text-primary, #1d1c17);
  overflow-wrap: anywhere;
}
.anc-body {
  margin-top: 2px; font-size: 12.5px; line-height: 1.5; color: var(--text-secondary, #3f3f46);
  overflow-wrap: anywhere;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.anc-time {
  font-size: 11px; color: var(--text-muted, #6b7280); white-space: nowrap; margin-top: 2px;
  font-variant-numeric: tabular-nums;
}

/* Dense type for the bell row (the FLAT variant), matching the panel's own scale. */
.anc--flat .anc-title { font-size: 11px; line-height: 1.35; }
.anc--flat .anc-body { font-size: 11.5px; -webkit-line-clamp: 2; }
.anc--flat .anc-time { font-size: 10px; }

/* READ / RESOLVED — the VISITED treatment: dim the parts, never a whole-card
   opacity (the timestamp must stay readable). Matches able-notification-bell's
   .anb-item--visited rules so a read row looks the same in both renders. */
.anc--read .anc-title, .anc--resolved .anc-title { color: var(--text-secondary, #3f3f46); font-weight: 600; }
.anc--read .anc-eyebrow, .anc--resolved .anc-eyebrow { opacity: .7; }
.anc--read .anc-body, .anc--resolved .anc-body { color: var(--text-muted, #6b7280); }
.anc--read .anc-ic, .anc--resolved .anc-ic { opacity: .55; }
.anc--resolved .anc-check { opacity: 1; }

/* Tap affordance — a real link only when the card carries a route. The FLAT
   (bell) variant never does: its mounting row owns the hover, the click and the
   navigation, so the card stays inert there. */
a.anc:not(.anc--flat) { cursor: pointer; }
@media (hover: hover) and (pointer: fine) {
  a.anc:not(.anc--flat):hover { background: var(--surface-hover, #f8f8f9); }
}
a.anc:focus-visible {
  outline: 2px solid var(--login-link-color, var(--accent, #1b2a4a)); outline-offset: 2px;
}


/* able-offcanvas */

able-offcanvas {
  position: fixed; top: 0; bottom: 0;
  z-index: 201;
  background: var(--surface-card, #141415);
  color: var(--text-primary, #e2e8f0);
  box-shadow: -10px 0 30px rgba(0, 0, 0, 0.4);
  transform: translateX(100%);
  transition: transform 240ms cubic-bezier(0.2, 0.8, 0.2, 1);
  overflow: hidden;
  flex-direction: column;
}
able-offcanvas[data-position="right"] { right: 0; transform: translateX(100%); }
able-offcanvas[data-position="left"]  { left: 0;  transform: translateX(-100%); }
able-offcanvas[data-position="top"] {
  top: 0; left: 0; right: 0; bottom: auto;
  transform: translateY(-100%);
  height: auto; max-height: 80vh;
}
able-offcanvas[data-position="bottom"] {
  top: auto; left: 0; right: 0; bottom: 0;
  transform: translateY(100%);
  height: auto; max-height: 80vh;
}
able-offcanvas.aoff--open[data-position="right"],
able-offcanvas.aoff--open[data-position="left"]   { transform: translateX(0); }
able-offcanvas.aoff--open[data-position="top"],
able-offcanvas.aoff--open[data-position="bottom"] { transform: translateY(0); }
.aoff-backdrop {
  position: fixed; inset: 0; z-index: 200;
  background: rgba(11, 15, 26, 0.55);
  opacity: 0; pointer-events: none;
  transition: opacity 220ms ease;
}
.aoff-backdrop--open { opacity: 1; pointer-events: auto; }
/* Background scroll-lock: while a panel is open (a backdrop is always shown) the
   page behind it must not scroll — a second scroll behind the drawer is the exact
   thing rule 22 forbids. The panel + backdrop are position:fixed, so locking the
   page host / body clips neither. The removed scrollbar's width is re-added as
   padding on the locked scroller (measured, 0 on overlay scrollbars) so the page
   never shifts under the fading backdrop. */
html.aoff-locked, html.aoff-locked body { overflow: hidden !important; }
html.aoff-locked .platform-page-host { overflow: hidden !important; }
/* Close: a NEUTRAL ghost icon button with a bold X, top-LEFT.
   ⚠ Was a solid danger-red square (#ef4444/#dc2626/#b91c1c) from the
   2026-06-10 restyle — the platform's DESTRUCTIVE colour on a plain dismiss
   control, on every tenant and every panel. Dismissing a panel destroys
   nothing, so it reads neutral and earns colour only on interaction.
   Semantic tokens only: they re-bind under dark mode, where the absolute
   --grey-N primitives (and any raw hex) do not. */
.aoff-close {
  position: absolute; top: 14px; left: 14px;
  width: 30px; height: 30px; border-radius: 8px;
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent; color: var(--text-muted);
  border: none;
  cursor: pointer; padding: 0;
  font-size: 14px; font-weight: 700; line-height: 1;
  z-index: 10;
  transition: background 120ms ease, color 120ms ease, transform 120ms ease;
}
/* Hover is mouse-only — an ungated :hover sticks after a tap on Android. */
@media (hover: hover) and (pointer: fine) {
  .aoff-close:hover {
    background: var(--surface-hover); color: var(--text-primary);
    transform: scale(1.05);
  }
}
.aoff-close:active { background: var(--surface-active); color: var(--text-primary); transform: scale(0.95); }
.aoff-close:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Touch: expand the hit area to 44px (30 + 7 + 7) without enlarging the button. */
@media (pointer: coarse) {
  .aoff-close::after { content: ''; position: absolute; inset: -7px; }
}
/* Breadcrumb bar — visible ONLY when the panel holds stacked levels. Flow
   child above the content slot (the host flexes column when open). The
   left padding clears the absolute close button. */
.aoff-crumbs {
  display: none;
  flex: 0 0 auto;
  align-items: center;
  gap: 2px;
  padding: 13px 16px 11px 56px;
  border-bottom: 1px solid var(--border-default, rgba(127, 127, 127, 0.25));
  background: var(--surface-raised, rgba(127, 127, 127, 0.06));
  font-size: 12.5px;
  line-height: 1.2;
  overflow-x: auto;
  white-space: nowrap;
  scrollbar-width: thin;
}
.aoff-crumbs--visible { display: flex; }
.aoff-crumb {
  flex: 0 0 auto;
  background: none; border: none;
  padding: 4px 6px; border-radius: 6px;
  font: inherit; color: var(--text-muted, #94a3b8);
  cursor: pointer;
  max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  transition: background 120ms ease, color 120ms ease;
}
@media (hover: hover) and (pointer: fine) {
  .aoff-crumb:hover {
    color: var(--text-primary, #e2e8f0);
    background: var(--surface-hover, rgba(127, 127, 127, 0.15));
  }
}
.aoff-crumb:focus-visible { outline: 2px solid var(--accent, #3b82f6); outline-offset: 1px; }
.aoff-crumb--current {
  color: var(--text-primary, #e2e8f0);
  font-weight: 600;
  cursor: default;
  max-width: 280px;
}
.aoff-crumb-sep { flex: 0 0 auto; color: var(--text-subtle, #64748b); opacity: 0.65; padding: 0 2px; }
a.aoff-crumb { text-decoration: none; display: inline-block; }
.aoff-crumb--root { color: var(--accent-text, var(--accent, #3b82f6)); }
span.aoff-crumb--root { cursor: default; }
/* A trail drawer shows its bar from the first level, so the bar is the band
   the close button (30px at top:14) sits in: 14 + 30 + 14. */
able-offcanvas[trail] .aoff-crumbs { min-height: 58px; padding-top: 14px; padding-bottom: 14px; }
able-offcanvas[trail][data-position="bottom"].aoff-responsive-flipped .aoff-crumbs { min-height: 72px; }
@media (pointer: coarse) {
  .aoff-crumb { min-height: 44px; display: inline-flex; align-items: center; }
}
/* WARNING — THIS padding: 16px IS DEAD ON EVERY DEPLOYED PAGE. Do not tune it
   and expect to see anything change. Verified 2026-08-24.

   The system theme declares the winning rule:
       able-apps-system/.../template-system-base/theme.css
       able-offcanvas.aoff > .aoff-content { padding: 24px 28px; }
   That is specificity (0,2,1) against this rule's (0,1,0), with no !important
   on either — so it wins on SPECIFICITY, and load order never enters into it
   (a nearby theme comment invokes load order; that is the right mechanism for
   .aoff-close, the wrong one here). The floor is unavoidable in production:
   system-base-theme's parent IS template-system-base, and themes.py appends the
   floor to any chain that misses it, so the only way to reach this rule is an
   offline fixture tree with no system clone.

   The real inset for content is therefore 24px top / 28px left. That matters
   because the close button is absolutely positioned at top:14 left:14, 30x30
   (44x44 on mobile, plus a -7px ::after hit-expander under pointer: coarse),
   at z-index 10 — so the button's box overlaps the content box's top-left
   corner by about 16x20 CSS px on desktop, and its coarse HIT AREA by 23x27.

   AND THAT IS NOT PURELY LATENT — see the ruling in the template-blueprints
   skill, section able-offcanvas / the close-button overlap. Read the ruling
   before "fixing" anything here: five apps already compensate in three
   mutually incompatible ways, so a blueprint-level inset would double for
   some and indent others.

   ── AUTHORING A PANEL-HOSTED PAGE: the geometry, and the two traps ──
   The button is TOP-LEFT and it GROWS: 30x30 on desktop, 44x44 once
   responsive="auto" flips the panel to a bottom sheet, from the SAME 14/14
   origin. With the coarse hit expander its reach is x 7..65, y 7..65 from the
   panel edge, against a content inset of only 24 top / 28 left. So a page
   whose content starts flush at the panel's top-left loses pixels, and the
   MOBILE case is the worst one.

   Trap 1 — the inset belongs to the PANEL, not the page. A page-scoped
   padding is wrong whenever the same page also renders somewhere without a
   close button (sunstate@rates rate-detail-rates-tab mounts both into a panel
   and as an able-tabs tab): only the panel knows the button is there.

   Trap 2 — a padding-top on .aoff-content SCROLLS AWAY. .aoff-content is the
   scroller while the button is anchored to the panel, so the reservation holds
   only at scrollTop 0 (measured: 0 shadowed px at rest, 756 at scrollTop 150).
   Reserve the band on the HOST instead — an absolutely-positioned child
   resolves against its ancestor's PADDING box, so top:14px still means 14px
   from the panel edge and the band moves the flow children, never the button.
   Zero the content's own top padding or the two stack.

   Survey before choosing the axis: on sunstate@ablecollect, 23 of the 27
   components that load into a responsive="auto" panel lost painted pixels to
   a mouse at 320px and 26 of 27 to the coarse hit area, but 4 already start
   their content far right of the button — so a blanket left gutter would have
   over-indented those to 126-212px on a 320px screen. The top band was the
   honest answer there. */
.aoff-content {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
  padding: 16px;
  box-sizing: border-box;
}
/* Stashed layers stay in the document (so reactive bindings + SSR state
   survive) but are fully hidden and inert. */
.aoff-layer[data-aoff-stashed] { display: none !important; }
/* Responsive: mobile bottom-sheet flip (responsive="auto" opt-in) */
able-offcanvas[data-position="bottom"].aoff-responsive-flipped {
  border-top-left-radius: 16px;
  border-top-right-radius: 16px;
  max-height: 90dvh;
  padding-bottom: max(0px, env(safe-area-inset-bottom));
}
able-offcanvas[data-position="bottom"].aoff-responsive-flipped .aoff-close {
  width: 44px;
  height: 44px;
  font-size: 22px;
}
/* Bottom-sheet: the enlarged 44px close button + the grab handle need more
   clearance than the desktop 56px. */
able-offcanvas[data-position="bottom"].aoff-responsive-flipped .aoff-crumbs {
  padding-left: 72px;
  padding-top: 16px;
}
/* The bottom-sheet grab handle is its OWN non-scrolling band at the top of the
   sheet: a flow child above .aoff-content, so the sheet's content starts below it
   and the handle never floats over scrolled content. Hidden on side drawers. The
   pill is a semantic token so it reads in light and dark (a white pill vanished on
   a light sheet). */
.aoff-grip { display: none; }
able-offcanvas[data-position="bottom"].aoff-responsive-flipped .aoff-grip {
  display: flex; flex: 0 0 auto; align-items: center; justify-content: center;
  height: 22px; background: var(--surface-card, #141415);
}
able-offcanvas[data-position="bottom"].aoff-responsive-flipped .aoff-grip::before {
  content: ''; width: 40px; height: 4px; border-radius: 2px;
  background: var(--border-strong, rgba(148, 163, 184, 0.7));
}
able-offcanvas[data-position="bottom"].aoff-responsive-flipped .aoff-content {
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
}


/* able-payment-method */

able-payment-method { display: block; }
.apm {
  border: 0; padding: 0; margin: 0;
  display: flex; flex-direction: column;
  gap: 10px;
}
.apm-legend {
  padding: 0; margin: 0 0 12px;
  font-size: 13px; font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-secondary, #3f3f46);
}
.apm-option {
  display: grid;
  grid-template-columns: auto 48px 1fr auto;
  align-items: center;
  gap: 12px;
  padding: 14px 16px;
  border: 1.5px solid var(--border-default, #e5e7eb);
  border-radius: 10px;
  cursor: pointer;
  background: var(--surface-base, #ffffff);
  transition: border-color 160ms ease, background 160ms ease;
  min-height: 64px;
}
.apm-option:hover {
  border-color: var(--accent, #1b2a4a);
  background: var(--surface-hover, #f8f8f9);
}
.apm-option[data-selected="true"] {
  border-color: var(--accent, #1b2a4a);
  background: var(--accent-soft, #dbeafe);
}
.apm-radio {
  appearance: none;
  width: 20px; height: 20px;
  border-radius: 50%;
  border: 2px solid var(--border-strong, #d6d8db);
  background: var(--surface-base, #ffffff);
  margin: 0;
  cursor: pointer;
  position: relative;
  flex-shrink: 0;
}
.apm-radio:checked {
  border-color: var(--accent, #1b2a4a);
}
.apm-radio:checked::after {
  content: '';
  position: absolute;
  inset: 3px;
  background: var(--accent, #1b2a4a);
  border-radius: 50%;
}
.apm-logo {
  display: inline-flex;
  align-items: center; justify-content: center;
  width: 48px; height: 32px;
  font-size: 12px; font-weight: 700;
  letter-spacing: 0.04em;
  background: var(--surface-subtle, #f1f5f9);
  color: var(--text-secondary, #3f3f46);
  border-radius: 6px;
  text-transform: uppercase;
  overflow: hidden;
}
.apm-logo--img { background: transparent; padding: 0; }
.apm-logo--img img, .apm-logo.apm-logo--img {
  object-fit: contain;
  max-width: 100%; max-height: 100%;
}
.apm-logo--mp { background: #009ee3; color: #fff; }
.apm-logo--oxxo { background: #e30613; color: #fff; }
.apm-logo--spei { background: #006341; color: #fff; }
.apm-logo--stripe { background: #635bff; color: #fff; }
.apm-logo--cc { background: #1d1d1f; color: #fff; }
.apm-logo--bank { background: #475569; color: #fff; }
.apm-logo--cash { background: #15803d; color: #fff; }
.apm-logo--apartado { background: #b45309; color: #fff; }
.apm-text {
  display: flex; flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.apm-label {
  font-size: 14px; font-weight: 600;
  color: var(--text-primary, #1d1c17);
}
.apm-desc {
  font-size: 12px;
  color: var(--text-muted, #6b7280);
  overflow: hidden; text-overflow: ellipsis;
}
.apm-badge {
  display: inline-flex;
  align-items: center;
  background: var(--success-soft, #dcfce7);
  color: var(--success, #15803d);
  font-size: 11px; font-weight: 600;
  padding: 2px 8px;
  border-radius: 10px;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  white-space: nowrap;
}
@media (max-width: 479px) {
  .apm-option {
    grid-template-columns: auto 40px 1fr;
    grid-template-areas:
      "radio logo text"
      ".     .    badge";
    padding: 12px;
    min-height: 56px;
  }
  .apm-radio { grid-area: radio; }
  .apm-logo { grid-area: logo; width: 40px; height: 28px; }
  .apm-text { grid-area: text; }
  .apm-badge {
    grid-area: badge;
    justify-self: end;
    margin-top: 4px;
  }
}


/* able-phone-input */

able-phone-input {
  display: block;
  position: relative;
  width: 100%;
  font-family: inherit;
}
able-phone-input .apin-row {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: stretch;
  gap: 0;
  width: 100%;
  box-sizing: border-box;
  border: 1px solid var(--border-default, #d6d8db);
  border-radius: var(--radius-md, 8px);
  background: var(--surface-card, #ffffff);
  overflow: hidden;
}
able-phone-input .apin-row:focus-within {
  border-color: var(--accent, #1b2a4a);
  box-shadow: 0 0 0 3px var(--accent-soft, rgba(27, 42, 74, 0.15));
}
able-phone-input .apin-cc {
  display: flex;
  align-items: center;
  gap: 6px;
  min-height: 44px;
  padding: 0 10px;
  margin: 0;
  border: 0;
  border-right: 1px solid var(--border-subtle, #f1f5f9);
  background: var(--surface-subtle, #f1f5f9);
  color: var(--text-primary, #1d1c17);
  font: inherit;
  font-size: var(--font-base, 16px);
  line-height: 1;
  cursor: pointer;
  white-space: nowrap;
}
able-phone-input .apin-cc:disabled { cursor: not-allowed; opacity: 0.6; }
able-phone-input .apin-cc:focus-visible {
  outline: 2px solid var(--accent, #1b2a4a);
  outline-offset: -2px;
}
able-phone-input .apin-flag { font-size: 1.15em; line-height: 1; }
able-phone-input .apin-dial { font-variant-numeric: tabular-nums; }
able-phone-input .apin-caret { opacity: 0.55; }
able-phone-input .apin-input {
  min-height: 44px;
  width: 100%;
  box-sizing: border-box;
  padding: 0 12px;
  border: 0;
  background: transparent;
  color: var(--text-primary, #1d1c17);
  font: inherit;
  /* 16px floor — anything smaller makes iOS Safari zoom on focus (rule 29). */
  font-size: max(16px, var(--font-base, 16px));
  font-variant-numeric: tabular-nums;
}
able-phone-input .apin-input:focus { outline: none; }
able-phone-input .apin-input::placeholder { color: var(--text-subtle, #9ca3af); }
able-phone-input .apin-input:disabled { cursor: not-allowed; opacity: 0.6; }
able-phone-input .apin-pop {
  position: absolute;
  z-index: var(--z-dropdown, 300);
  top: calc(100% + 4px);
  left: 0;
  width: min(320px, 100%);
  max-height: 280px;
  overflow-y: auto;
  margin: 0;
  padding: 4px;
  list-style: none;
  border: 1px solid var(--border-default, #d6d8db);
  border-radius: var(--radius-md, 8px);
  background: var(--surface-card, #ffffff);
  box-shadow: var(--shadow-lg, 0 12px 32px rgba(0, 0, 0, 0.18));
}
able-phone-input .apin-pop[hidden] { display: none; }
able-phone-input .apin-opt {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 44px;
  padding: 0 10px;
  border-radius: var(--radius-sm, 4px);
  color: var(--text-primary, #1d1c17);
  font-size: var(--font-base, 16px);
  cursor: pointer;
}
able-phone-input .apin-opt[aria-selected="true"] { font-weight: 600; }
able-phone-input .apin-opt.is-active,
able-phone-input .apin-opt:hover {
  background: var(--surface-hover, #f8f8f9);
}
able-phone-input .apin-opt-name {
  flex: 1 1 auto;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
able-phone-input .apin-opt-dial {
  color: var(--text-secondary, #3f3f46);
  font-variant-numeric: tabular-nums;
}
@media (prefers-reduced-motion: reduce) {
  able-phone-input * { transition: none !important; }
}


/* able-report-card */

/* ⚠ ACCENT-COLOURED TEXT USES --accent-text, NEVER --accent.
   Measured on the deployed ableU cascade 2026-08-23: --accent is a FILL token and
   computes the SAME #2563eb in BOTH modes, so accent text sat at 3.35:1 on the dark
   card ground and 2.84:1 on the dark pill ground — both under the 4.5:1 AA floor for
   small text. --accent-text is the type-only twin, re-binds (#2563eb / #A5B4FC), and
   measures 5.17 light / 8.68 dark on the card and 4.75 / 7.36 on the pill. Because the
   TOKEN flips, no media query and no [data-theme] guard is needed here at all, and the
   tenant keeps its own colour instead of a hard-coded blue. Fills and grounds keep
   --accent / --accent-soft. */
able-report-card { display: block; max-width: 560px; margin: 8px 0; }
able-report-card .arpc {
  background: var(--surface-card, #fff);
  border: 1px solid var(--border-default, #e5e7eb);
  border-radius: var(--radius-xl, 14px);
  box-shadow: var(--shadow-sm, 0 1px 2px rgba(0,0,0,.06));
  padding: 14px 16px;
  font-family: var(--font-sans, sans-serif);
  color: var(--text-primary, #1d1c17);
}
able-report-card .arpc-eyebrow {
  font-size: 10px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase;
  color: var(--accent-text, var(--accent, #1b2a4a)); margin: 0 0 2px;
}
able-report-card .arpc-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
}
able-report-card .arpc-title {
  font-family: var(--font-display, var(--font-serif, serif));
  font-size: 20px; line-height: 1.15; margin: 0; color: var(--text-primary, #1d1c17);
}
able-report-card .arpc-letter {
  font-size: 13px; font-weight: 700; line-height: 1; white-space: nowrap;
  border-radius: 999px; padding: 5px 11px;
  background: var(--surface-subtle, #f1f5f9); color: var(--text-secondary, #3f3f46);
}
able-report-card .arpc-letter[data-t="good"] { background: var(--success-soft, #dcfce7); color: var(--success, #15803d); }
able-report-card .arpc-letter[data-t="mid"]  { background: var(--warning-soft, #fef3c7); color: var(--warning-hover, #92400e); }
able-report-card .arpc-letter[data-t="low"]  { background: var(--danger-soft, #fee2e2); color: var(--danger, #b91c1c); }
able-report-card .arpc-code {
  font-family: var(--font-mono, monospace); font-size: 10px; font-weight: 600;
  color: var(--accent-text, var(--accent, #1b2a4a)); background: var(--accent-soft, #dbeafe);
  border-radius: 999px; padding: 1px 8px; white-space: nowrap;
  display: inline-block; margin: 4px 0 0;
}
able-report-card .arpc-grade { display: flex; align-items: baseline; gap: 7px; margin: 10px 0 2px; }
able-report-card .arpc-grade-num {
  font-size: 30px; font-weight: 700; line-height: 1;
  font-variant-numeric: tabular-nums; color: var(--text-primary, #1d1c17);
}
able-report-card .arpc-grade-cap {
  font-size: 10px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase;
  color: var(--text-muted, #6b7280);
}
able-report-card .arpc-none {
  font-size: 12px; color: var(--text-muted, #6b7280); margin: 8px 0 2px; font-style: italic;
}
able-report-card .arpc-comps { display: flex; gap: 6px; margin: 10px 0 2px; }
able-report-card .arpc-comp {
  flex: 1; text-align: center; border-radius: var(--radius-lg, 10px);
  background: var(--surface-subtle, #f1f5f9); padding: 6px 3px; display: grid; gap: 1px;
}
able-report-card .arpc-comp b {
  font-size: 15px; font-variant-numeric: tabular-nums; line-height: 1.1;
  color: var(--text-primary, #1d1c17);
}
able-report-card .arpc-comp[data-empty="1"] b { color: var(--text-subtle, #9ca3af); font-weight: 500; }
able-report-card .arpc-comp span {
  font-size: 9px; font-weight: 600; letter-spacing: .04em;
  color: var(--text-secondary, #3f3f46); overflow-wrap: anywhere;
}
able-report-card .arpc-comp i {
  font-size: 8px; font-style: normal; letter-spacing: .04em;
  color: var(--text-muted, #6b7280);
}
able-report-card .arpc-skills-head {
  font-size: 9px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase;
  color: var(--text-secondary, #3f3f46); margin: 14px 0 6px;
  padding-top: 10px; border-top: 1px solid var(--border-subtle, #f1f5f9);
}
able-report-card .arpc-skill { margin: 0 0 9px; }
able-report-card .arpc-skill-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
}
able-report-card .arpc-skill-name {
  font-size: 13px; font-weight: 600; color: var(--text-primary, #1d1c17);
  overflow-wrap: anywhere;
}
able-report-card .arpc-skill-pct {
  font-size: 12px; font-variant-numeric: tabular-nums; white-space: nowrap;
  color: var(--text-secondary, #3f3f46);
}
able-report-card .arpc-bar {
  height: 5px; border-radius: 999px; margin: 4px 0 2px; overflow: hidden;
  background: var(--surface-hover, #f0f1f2);
}
able-report-card .arpc-bar-fill { height: 100%; border-radius: 999px; background: var(--text-subtle, #9ca3af); }
able-report-card .arpc-bar-fill[data-t="strong"] { background: var(--success, #15803d); }
able-report-card .arpc-bar-fill[data-t="mid"]    { background: var(--warning, #b45309); }
able-report-card .arpc-bar-fill[data-t="weak"]   { background: var(--danger, #b91c1c); }
able-report-card .arpc-skill-meta { font-size: 11px; color: var(--text-muted, #6b7280); margin: 0; }
able-report-card .arpc-more { font-size: 11px; color: var(--text-muted, #6b7280); margin: 0; }
able-report-card .arpc-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  margin-top: 12px; padding-top: 8px; border-top: 1px solid var(--border-subtle, #f1f5f9);
}
able-report-card .arpc-renorm {
  font-size: 9px; letter-spacing: .03em; color: var(--text-muted, #6b7280); margin: 0;
}
able-report-card .arpc-open {
  font-size: 12px; font-weight: 650; color: var(--accent-text, var(--accent, #1b2a4a));
  text-decoration: none; white-space: nowrap; min-height: 24px;
}
@media (hover: hover) and (pointer: fine) {
  able-report-card .arpc-open:hover { text-decoration: underline; }
}


/* able-shell-nav */

/* ── STRUCTURAL — unconditional ───────────────────────────────────────────────
 * Only what the rail needs to be LAID OUT correctly at first paint. Nothing here
 * decides how it LOOKS.
 *
 * Cosmetic defaults used to live here unconditionally and they LEAKED: fryaway
 * declares no padding on its group band, so this file's .asn-group{padding:4px 0 1px}
 * applied and pushed every group down cumulatively (+4px at the top, +10px by the
 * fourth band — measured 2026-08-26). A tenant stylesheet only outranks a default
 * for the properties it actually names, so any cosmetic default here silently
 * becomes part of an app that never asked for it. The look now sits behind an
 * opt-in below.
 *
 * display:contents is the load-bearing one: the rail, the mobile bar and the scrim
 * have to be layout SIBLINGS of the page's <main> — ableU positions the rail fixed,
 * fryaway makes it a GRID CELL — and a host box between them breaks the second. */
able-shell-nav{display:contents;}
.asn-side{display:flex;flex-direction:column;min-height:0;}
.asn-brand{display:flex;align-items:center;}
.asn-brand-link{display:flex;align-items:center;text-decoration:none;flex:1;min-width:0;}
.asn-collapse{flex:none;display:flex;align-items:center;justify-content:center;cursor:pointer;}
.asn-nav{flex:1;overflow-y:auto;overflow-x:hidden;}
.asn-group-label{display:block;}
.asn-item{position:relative;display:flex;align-items:center;width:100%;box-sizing:border-box;
  text-decoration:none;white-space:nowrap;font-family:inherit;text-align:start;cursor:pointer;}
.asn-ico{flex:none;display:flex;align-items:center;justify-content:center;color:inherit;}
.asn-text{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;}
.asn-caret{flex:none;margin-left:auto;transition:transform .16s;}
.asn-disclosure[aria-expanded="true"] .asn-caret{transform:rotate(180deg);}
.asn-sub{display:flex;flex-direction:column;}
.asn-foot{display:flex;flex-direction:column;}
.asn-user{display:flex;align-items:center;}
.asn-user-name{flex:1 1 auto;min-width:0;cursor:pointer;text-align:start;font:inherit;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.asn-bell{flex:none;margin-left:auto;display:flex;align-items:center;}
.asn-mbar{display:none;}
.asn-scrim{display:none;}
[data-nav="collapsed"] .asn-text,[data-nav="collapsed"] .asn-group-label,
[data-nav="collapsed"] .asn-user-name{display:none;}
[data-nav-boot="1"] .asn-side{transition:none !important;}
@media (max-width:900px){
  .asn-mbar{display:flex;align-items:center;position:fixed;top:0;left:0;right:0;z-index:55;
    padding-top:env(safe-area-inset-top,0);}
  .asn-mbar-btn{width:var(--tap-target,44px);height:var(--tap-target,44px);border:none;
    background:transparent;color:inherit;cursor:pointer;display:flex;align-items:center;justify-content:center;}
  .asn-mbar-brand{flex:1;display:flex;align-items:center;min-height:var(--tap-target,44px);text-decoration:none;}
  .asn-mbar-bell{margin-left:auto;display:flex;align-items:center;}
  .asn-scrim{display:block;position:fixed;inset:0;z-index:58;opacity:0;pointer-events:none;transition:opacity .2s;}
  [data-mdrawer="open"] .asn-scrim{opacity:1;pointer-events:auto;}
  .asn-collapse{display:none;}
  [data-nav="collapsed"] .asn-text,[data-nav="collapsed"] .asn-group-label,
  [data-nav="collapsed"] .asn-user-name{display:revert;}
}
@media (prefers-reduced-motion:reduce){
  .asn-side,.asn-scrim,.asn-caret{transition:none;}
}

/* ── THE DEFAULT SKIN — opt in with <able-shell-nav skin="default"> ───────────
 * A brand-new app gets a usable, token-driven rail from one attribute. An app
 * that brings its own stylesheet (ableU, fryaway) never sets it and therefore
 * inherits NONE of this — which is the whole point: a default that can reach a
 * skinned app is not a default, it is a bug with a friendly name. */
able-shell-nav[skin="default"] .asn-brand{gap:10px;padding:13px 8px 16px 14px;}
able-shell-nav[skin="default"] .asn-brand-link{gap:10px;}
able-shell-nav[skin="default"] .asn-collapse{width:30px;height:30px;border:none;border-radius:8px;
  background:transparent;color:var(--text-secondary,#3f3f46);}
able-shell-nav[skin="default"] .asn-collapse svg{width:18px;height:18px;}
able-shell-nav[skin="default"] .asn-nav{padding:2px 8px 8px;}
able-shell-nav[skin="default"] .asn-group{padding:4px 0 1px;}
able-shell-nav[skin="default"] .asn-group-label{margin:0;padding:2px 10px 3px;font-size:10px;
  font-weight:700;letter-spacing:.13em;text-transform:uppercase;color:var(--text-muted,#6b7280);}
able-shell-nav[skin="default"] .asn-item{gap:10px;padding:5px 10px;margin:1px 0;border-radius:8px;
  color:var(--text-secondary,#3f3f46);font-size:13.5px;font-weight:600;background:transparent;border:none;}
able-shell-nav[skin="default"] .asn-item[aria-current="page"]{background:var(--accent-soft,#dbeafe);
  color:var(--accent,#1b2a4a);}
able-shell-nav[skin="default"] .asn-ico{width:20px;height:20px;}
able-shell-nav[skin="default"] .asn-ico svg{width:19px;height:19px;}
able-shell-nav[skin="default"] .asn-foot{padding:7px 10px;gap:3px;}
able-shell-nav[skin="default"] .asn-user{padding:3px 6px 2px;min-height:38px;}
able-shell-nav[skin="default"] .asn-user-name{margin-left:8px;padding:0;border:none;background:transparent;
  font-size:13.5px;font-weight:600;color:var(--text-secondary,#3f3f46);}
@media (max-width:900px){
  able-shell-nav[skin="default"] .asn-mbar{gap:10px;padding:0 10px;}
  able-shell-nav[skin="default"] .asn-mbar-btn{border-radius:9px;}
  able-shell-nav[skin="default"] .asn-mbar-btn svg{width:22px;height:22px;}
  able-shell-nav[skin="default"] .asn-mbar-brand{gap:8px;}
  able-shell-nav[skin="default"] .asn-scrim{background:rgba(0,0,0,.45);}
  able-shell-nav[skin="default"] .asn-item{min-height:var(--tap-target,44px);}
}


/* able-shipping-rate */

able-shipping-rate { display: block; }
.ashr {
  border: 0; padding: 0; margin: 0;
  display: flex; flex-direction: column;
  gap: 10px;
}
.ashr-legend {
  padding: 0; margin: 0 0 12px;
  font-size: 13px; font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-secondary, #3f3f46);
}
.ashr-option {
  display: grid;
  grid-template-columns: auto 48px 1fr auto;
  align-items: center;
  gap: 12px;
  padding: 14px 16px;
  border: 1.5px solid var(--border-default, #e5e7eb);
  border-radius: 10px;
  cursor: pointer;
  background: var(--surface-base, #ffffff);
  transition: border-color 160ms ease, background 160ms ease;
  min-height: 64px;
}
.ashr-option:hover {
  border-color: var(--accent, #1b2a4a);
  background: var(--surface-hover, #f8f8f9);
}
.ashr-option[data-selected="true"] {
  border-color: var(--accent, #1b2a4a);
  background: var(--accent-soft, #dbeafe);
}
.ashr-radio {
  appearance: none;
  width: 20px; height: 20px;
  border-radius: 50%;
  border: 2px solid var(--border-strong, #d6d8db);
  background: var(--surface-base, #ffffff);
  margin: 0;
  cursor: pointer;
  position: relative;
  flex-shrink: 0;
}
.ashr-radio:checked { border-color: var(--accent, #1b2a4a); }
.ashr-radio:checked::after {
  content: '';
  position: absolute; inset: 3px;
  background: var(--accent, #1b2a4a);
  border-radius: 50%;
}
.ashr-logo {
  display: inline-flex;
  align-items: center; justify-content: center;
  width: 48px; height: 32px;
  font-size: 11px; font-weight: 700;
  letter-spacing: 0.04em;
  background: var(--surface-subtle, #f1f5f9);
  color: var(--text-secondary, #3f3f46);
  border-radius: 6px;
  text-transform: uppercase;
}
.ashr-logo--img { background: transparent; padding: 0; }
.ashr-logo--estafeta { background: #ea0029; color: #fff; }
.ashr-logo--dhl { background: #fc0; color: #d40511; }
.ashr-logo--fedex { background: #4d148c; color: #ff6200; }
.ashr-logo--paquetexpress { background: #003e7e; color: #fff; }
.ashr-logo--99min { background: #ff5e2a; color: #fff; }
.ashr-logo--ups { background: #351c15; color: #ffb500; }
.ashr-logo--redpack { background: #c8102e; color: #fff; }
.ashr-logo--sendex { background: #173f5f; color: #fff; }
.ashr-logo--pickup { background: #15803d; color: #fff; }
.ashr-logo--correos { background: #006847; color: #fff; }
.ashr-text {
  display: flex; flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.ashr-carrier {
  font-size: 14px; font-weight: 600;
  color: var(--text-primary, #1d1c17);
}
.ashr-eta {
  font-size: 12px;
  color: var(--text-muted, #6b7280);
}
.ashr-badge {
  display: inline-flex;
  align-items: center;
  align-self: flex-start;
  margin-top: 2px;
  background: var(--info-soft, #dbeafe);
  color: var(--info, #1d4ed8);
  font-size: 11px; font-weight: 600;
  padding: 2px 8px;
  border-radius: 10px;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}
.ashr-price {
  font-size: 15px; font-weight: 600;
  color: var(--text-primary, #1d1c17);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.ashr-price--free {
  color: var(--success, #15803d);
  text-transform: none;
  font-weight: 700;
}
.ashr-empty {
  padding: 16px;
  text-align: center;
  color: var(--text-muted, #6b7280);
  font-size: 14px;
  margin: 0;
}
@media (max-width: 479px) {
  .ashr-option {
    grid-template-columns: auto 40px 1fr;
    grid-template-areas:
      "radio logo text"
      ".     .    price";
    padding: 12px;
    min-height: 56px;
  }
  .ashr-radio { grid-area: radio; }
  .ashr-logo { grid-area: logo; width: 40px; height: 28px; }
  .ashr-text { grid-area: text; }
  .ashr-price {
    grid-area: price;
    justify-self: end;
    margin-top: 4px;
  }
}


/* able-social-compose */

.asc { display: block; margin: 10px 0; padding: 0; border-radius: 14px;
  border: 1px solid var(--border-default, #dfe3e9);
  background: var(--surface-card, #fff); color: var(--text-primary, #1d1c17);
  overflow: hidden; }
.asc-head { display: flex; align-items: center; gap: 10px; padding: 13px 16px;
  border-bottom: 1px solid var(--border-subtle, #e9edf2); }
.asc-avatar { width: 34px; height: 34px; border-radius: 50%; overflow: hidden;
  flex: 0 0 auto; background: var(--surface-subtle, #eef1f6);
  display: flex; align-items: center; justify-content: center;
  font-size: 13px; font-weight: 700; color: var(--text-secondary, #3f3f46); }
.asc-avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
.asc-who { display: flex; flex-direction: column; min-width: 0; flex: 1 1 auto; }
.asc-name { font-size: 13.5px; font-weight: 700; overflow-wrap: anywhere; }
.asc-handle { font-size: 11.5px; color: var(--text-muted, #6b7280); overflow-wrap: anywhere; }
.asc-net { font-size: 10px; font-weight: 700; letter-spacing: 1.2px; text-transform: uppercase;
  color: var(--text-muted, #6b7280); flex: 0 0 auto; }
.asc-body { padding: 14px 16px; display: flex; flex-direction: column; gap: 14px; }
/* The frame HUGS the media instead of boxing it. A vertical clip (TikTok's whole
 * format) in a full-width 16:9 box is ~70% black bars, which reads as a broken
 * player rather than a preview — so the media keeps its own aspect (width:auto)
 * under a height cap and the frame centers on it. */
.asc-media { border-radius: 10px; overflow: hidden; line-height: 0;
  display: flex; justify-content: center;
  border: 1px solid var(--border-subtle, #e9edf2);
  background: var(--surface-subtle, #eef1f6); }
.asc-media video, .asc-media img { display: block; width: auto; max-width: 100%;
  max-height: 420px; background: #000; }
.asc-note { font-size: 11px; color: var(--text-muted, #6b7280); margin: 6px 0 0;
  overflow-wrap: anywhere; }
.asc-field { display: flex; flex-direction: column; gap: 6px; }
.asc-label { font-size: 10.5px; font-weight: 700; letter-spacing: 1px;
  text-transform: uppercase; color: var(--text-muted, #6b7280); }
.asc-req { color: var(--danger, #b91c1c); margin-left: 5px; letter-spacing: 0; }
.asc-cap { width: 100%; box-sizing: border-box; font: inherit; font-size: 13px;
  line-height: 1.55; padding: 10px 12px; border-radius: 10px; resize: vertical;
  min-height: 74px; color: var(--text-primary, #1d1c17);
  background: var(--surface-card, #fff);
  border: 1px solid var(--border-default, #dfe3e9); }
.asc-cap:focus-visible, .asc-select:focus-visible { outline: 2px solid var(--accent, #1b2a4a);
  outline-offset: 1px; }
.asc-count { font-size: 11px; color: var(--text-muted, #6b7280); align-self: flex-end; }
.asc-count[data-over="true"] { color: var(--danger, #b91c1c); font-weight: 700; }
.asc-select { width: 100%; box-sizing: border-box; font: inherit; font-size: 13px;
  padding: 9px 12px; border-radius: 10px; color: var(--text-primary, #1d1c17);
  background: var(--surface-card, #fff);
  border: 1px solid var(--border-default, #dfe3e9); }
.asc-toggles { display: flex; flex-wrap: wrap; gap: 8px; }
.asc-toggle { display: inline-flex; align-items: center; gap: 7px; cursor: pointer;
  font-size: 12.5px; padding: 7px 12px; border-radius: 999px;
  border: 1px solid var(--border-default, #dfe3e9);
  background: var(--surface-card, #fff); min-height: 38px; }
.asc-toggle input { margin: 0; width: 15px; height: 15px; accent-color: var(--accent, #1b2a4a); }
.asc-toggle[data-off="true"] { opacity: .5; cursor: not-allowed;
  background: var(--surface-subtle, #eef1f6); }
.asc-off { font-size: 10.5px; color: var(--text-muted, #6b7280); font-style: normal; }
.asc-switch { display: flex; align-items: flex-start; gap: 9px; cursor: pointer;
  font-size: 12.5px; line-height: 1.5; }
.asc-switch input { margin: 2px 0 0; width: 16px; height: 16px; flex: 0 0 auto;
  accent-color: var(--accent, #1b2a4a); }
.asc-switch b { font-weight: 700; }
.asc-sub { display: block; color: var(--text-secondary, #3f3f46); font-size: 11.5px; }
.asc-brands { display: flex; flex-direction: column; gap: 10px; margin-top: 11px;
  padding: 11px 13px; border-radius: 10px;
  background: var(--surface-subtle, #eef1f6);
  border: 1px solid var(--border-subtle, #e9edf2); }
.asc-result { display: block; font-size: 11.5px; margin-top: 3px; font-weight: 600;
  color: var(--accent, #1b2a4a); }
.asc-warn { margin: 0; font-size: 11.5px; font-weight: 600; color: var(--warning, #b45309); }
.asc-bad { margin: 0; font-size: 11.5px; font-weight: 600; color: var(--danger, #b91c1c); }
.asc-notice { margin: 0; padding: 10px 12px; border-radius: 10px; font-size: 11.5px;
  line-height: 1.5; color: var(--text-secondary, #3f3f46);
  background: var(--surface-subtle, #eef1f6);
  border-left: 3px solid var(--warning, #b45309); }
.asc-consent { margin: 0; font-size: 11.5px; line-height: 1.55;
  color: var(--text-secondary, #3f3f46); }
.asc-consent a { color: var(--accent, #1b2a4a); text-decoration: underline; }
.asc-actions { display: flex; flex-wrap: wrap; gap: 9px; align-items: center; }
.asc-go { font: inherit; font-size: 13px; font-weight: 700; padding: 10px 20px;
  border-radius: 10px; border: 0; cursor: pointer; min-height: 40px;
  color: var(--on-accent, #fff);
  background: linear-gradient(135deg, var(--accent, #1b2a4a),
    color-mix(in srgb, var(--accent, #1b2a4a) 82%, black)); }
.asc-go:disabled { opacity: .42; cursor: not-allowed; box-shadow: none; }
.asc-ghost { font: inherit; font-size: 12.5px; font-weight: 600; padding: 9px 15px;
  border-radius: 10px; cursor: pointer; min-height: 38px;
  border: 1px solid var(--border-default, #dfe3e9);
  background: var(--surface-card, #fff); color: var(--text-primary, #1d1c17); }
.asc-ghost:disabled { opacity: .5; cursor: not-allowed; }
.asc-link { display: inline-flex; align-items: center; text-decoration: none;
  font: inherit; font-size: 13px; font-weight: 700; padding: 10px 18px;
  border-radius: 10px; color: var(--on-accent, #fff); min-height: 40px;
  background: var(--accent, #1b2a4a); }
.asc-state { display: flex; align-items: flex-start; gap: 9px; padding: 12px 16px;
  border-top: 1px solid var(--border-subtle, #e9edf2); font-size: 12.5px; }
.asc-dot { width: 9px; height: 9px; border-radius: 50%; margin-top: 4px; flex: 0 0 auto;
  background: var(--warning, #b45309); }
.asc-dot[data-s="ok"] { background: var(--success, #15803d); }
.asc-dot[data-s="bad"] { background: var(--danger, #b91c1c); }
.asc-state-txt { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.asc-state-txt b { font-size: 13px; }
.asc-state-txt code, .asc-note code { font-family: var(--font-mono, ui-monospace, Menlo, monospace);
  font-size: 11px; overflow-wrap: anywhere; }
.asc-blocked { padding: 15px 16px; display: flex; flex-direction: column; gap: 11px;
  align-items: flex-start; font-size: 13px; line-height: 1.55; }
@media (max-width: 560px) {
  .asc-actions { flex-direction: column; align-items: stretch; }
  .asc-actions > * { width: 100%; justify-content: center; text-align: center; }
  .asc-toggle { flex: 1 1 100%; }
}
/* 44px is the iOS HIG floor for a TOUCH target (28-template-design §2.2). The desktop
 * heights above match this card's siblings in the claw; a finger gets the taller ones,
 * which is where a mis-tap on "Post" actually costs something. Keyed on the pointer, not
 * the width — a small window on a laptop is still a mouse. Inline consent links stay
 * prose: they sit in a sentence, and padding a link out to 44px there would break the
 * line it belongs to. */
@media (hover: none) and (pointer: coarse) {
  .asc-go, .asc-ghost, .asc-link, .asc-toggle { min-height: 44px; }
  .asc-select { min-height: 44px; padding-block: 11px; }
  .asc-switch input, .asc-toggle input { width: 18px; height: 18px; }
}
@media (prefers-reduced-motion: reduce) {
  .asc * { transition: none !important; animation: none !important; }
}


/* able-spinner */

able-spinner { display: contents; }
.asp-bar {
  position: fixed; top: 0; left: 0; right: 0; height: 3px;
  z-index: 1000; pointer-events: none;
  background: rgba(255, 255, 255, 0.08);
  overflow: hidden;
}
.asp-bar[hidden] { display: none !important; }
.asp-bar-fill {
  height: 100%; width: 30%;
  background: linear-gradient(90deg, transparent 0%, var(--accent-secondary, #3b82f6) 50%, transparent 100%);
  transform: translateX(-100%);
  animation: asp-slide 1.2s linear infinite;
}
@keyframes asp-slide {
  from { transform: translateX(-100%); }
  to   { transform: translateX(400%); }
}
.asp-overlay {
  position: fixed; inset: 0;
  background: rgba(11, 15, 26, 0.45);
  display: flex; align-items: center; justify-content: center;
  z-index: 999;
}
.asp-overlay[hidden] { display: none !important; }
.asp-spinner {
  width: 36px; height: 36px;
  border: 3px solid rgba(255, 255, 255, 0.16);
  border-top-color: var(--accent-secondary, #3b82f6);
  border-radius: 50%;
  animation: asp-spin 800ms linear infinite;
}
@keyframes asp-spin { to { transform: rotate(360deg); } }


/* able-stat-row */

able-stat-row {
  display: grid;
  width: 100%;
  box-sizing: border-box;
  grid-template-columns: 1fr;
  gap: var(--space-5, 24px);
}
@media (min-width: 640px) {
  able-stat-row[data-cols="2"] { grid-template-columns: repeat(2, 1fr); }
  able-stat-row[data-cols="3"] { grid-template-columns: repeat(3, 1fr); }
  able-stat-row[data-cols="4"] { grid-template-columns: repeat(2, 1fr); }
  able-stat-row[data-cols="5"] { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 1024px) {
  able-stat-row[data-cols="4"] { grid-template-columns: repeat(4, 1fr); }
  able-stat-row[data-cols="5"] { grid-template-columns: repeat(5, 1fr); }
}

able-stat-row[data-align="center"] > * { text-align: center; }

able-stat-row[data-variant="bordered"] > * {
  border-left: 2px solid var(--copper, #B36A2F);
  padding-left: var(--space-4, 16px);
}
@media (min-width: 640px) {
  able-stat-row[data-variant="bordered"][data-align="center"] > * {
    border-left: 0;
    border-top: 2px solid var(--copper, #B36A2F);
    padding-left: 0;
    padding-top: var(--space-3, 12px);
  }
}

able-stat-row > * > .stat-num,
able-stat-row > * > :first-child {
  font-family: var(--font-display, var(--font-sans, sans-serif));
  font-size: clamp(36px, 4.4vw, 56px);
  line-height: 1.05;
  font-weight: 500;
  letter-spacing: -0.02em;
  color: var(--text-primary, #0F0E0C);
  margin: 0 0 var(--space-1, 4px);
  font-feature-settings: 'tnum', 'lnum';
}
able-stat-row > * > .stat-label,
able-stat-row > * > :nth-child(2) {
  font-family: var(--font-ui, sans-serif);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted, #6B5F58);
  margin: 0;
}


/* able-status-pill */

able-status-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2, 8px);
  padding: 3px 10px 3px 8px;
  font-family: var(--font-ui, sans-serif);
  font-size: 12px;
  font-weight: 500;
  line-height: 1.4;
  border-radius: 999px;
  border: 1px solid transparent;
  vertical-align: middle;
  white-space: nowrap;
}

.asp-dot {
  width: 6px; height: 6px;
  border-radius: 50%;
  flex-shrink: 0;
  background: currentColor;
}
.asp-label {
  display: inline-block;
}
able-status-pill[data-dot="false"] .asp-dot { display: none; }
able-status-pill[data-dot="false"] { padding-left: 10px; }

able-status-pill[data-kind="success"] {
  background: var(--narrative-jade-soft, rgba(42, 152, 88, 0.12));
  color: var(--narrative-jade-deep, #1F6B40);
  border-color: var(--narrative-jade-line, rgba(42, 152, 88, 0.28));
}
able-status-pill[data-kind="warning"] {
  background: var(--narrative-amber-soft, rgba(214, 169, 52, 0.16));
  color: var(--narrative-amber-deep, #8C5418);
  border-color: var(--narrative-amber-line, rgba(214, 169, 52, 0.32));
}
able-status-pill[data-kind="error"] {
  background: var(--narrative-crimson-soft, rgba(220, 79, 55, 0.12));
  color: var(--narrative-crimson-deep, #B23E2E);
  border-color: var(--narrative-crimson-line, rgba(220, 79, 55, 0.28));
}
able-status-pill[data-kind="info"] {
  background: var(--narrative-cobalt-soft, rgba(58, 100, 168, 0.12));
  color: var(--narrative-cobalt-deep, #2A4880);
  border-color: var(--narrative-cobalt-line, rgba(58, 100, 168, 0.28));
}
able-status-pill[data-kind="neutral"] {
  background: var(--surface-subtle, #F2EDE5);
  color: var(--text-secondary, #2C2622);
  border-color: var(--border-strong, #E6DFD4);
}

/* Dark mode. The kind colors above use the --narrative-*-deep editorial
   tokens, which are SATURATED-but-DARK and do NOT re-bind in dark mode (rule
   27: narrative-* tokens keep fixed hex). On a dark page that leaves dark
   text on a dark soft fill — measured ~2.0:1 for success (WCAG AA fail). The
   theme tokens take precedence when a tenant theme defines a dark narrative
   palette; the literal fallbacks brighten the text so the pill stays legible
   on any dark theme that doesn't. Background/border keep the same low-alpha
   tints (they read fine over a dark surface); only the TEXT brightens. */
:root[data-theme="dark"] able-status-pill[data-kind="success"] {
  color: var(--narrative-jade-deep-on-dark, #5FD6A0);
}
:root[data-theme="dark"] able-status-pill[data-kind="warning"] {
  color: var(--narrative-amber-deep-on-dark, #F3C969);
}
:root[data-theme="dark"] able-status-pill[data-kind="error"] {
  color: var(--narrative-crimson-deep-on-dark, #F3917E);
}
:root[data-theme="dark"] able-status-pill[data-kind="info"] {
  color: var(--narrative-cobalt-deep-on-dark, #8FB8FF);
}


/* able-sunday-card */

/* ⚠ ACCENT-COLOURED TEXT USES --accent-text, NEVER --accent.
   Measured on the deployed ableU cascade 2026-08-23: --accent is a FILL token and
   computes the SAME #2563eb in BOTH modes, so accent text sat at 3.35:1 on the dark
   card ground and 2.84:1 on the dark pill ground — both under the 4.5:1 AA floor for
   small text. --accent-text is the type-only twin, re-binds (#2563eb / #A5B4FC), and
   measures 5.17 light / 8.68 dark on the card and 4.75 / 7.36 on the pill. Because the
   TOKEN flips, no media query and no [data-theme] guard is needed here at all, and the
   tenant keeps its own colour instead of a hard-coded blue. Fills and grounds keep
   --accent / --accent-soft.

   This REPLACES the earlier dark-only guard that painted .ascd-open + .ascd-code with
   var(--accent-on-dark, #7db3ff). That token is declared by no theme this card renders
   under, so the hard-coded blue was silently doing the work — overriding the tenant's
   brand — and it reached only 2 of the 4 accent strings, leaving .ascd-eyebrow and
   .ascd-dinner-frame at 3.35:1 / 2.84:1. The token flip covers all four and measures
   higher than the hex did (8.68 vs 8.04 on the card, 7.36 vs 6.83 on the pill). */
able-sunday-card { display: block; max-width: 560px; margin: 8px 0; }
able-sunday-card .ascd {
  background: var(--surface-card, #fff);
  border: 1px solid var(--border-default, #e5e7eb);
  border-radius: var(--radius-xl, 14px);
  box-shadow: var(--shadow-sm, 0 1px 2px rgba(0,0,0,.06));
  padding: 14px 16px;
  font-family: var(--font-sans, sans-serif);
  color: var(--text-primary, #1d1c17);
}
able-sunday-card .ascd-eyebrow {
  font-size: 10px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase;
  color: var(--accent-text, var(--accent, #1b2a4a)); margin: 0 0 2px;
}
able-sunday-card .ascd-title {
  font-family: var(--font-display, var(--font-serif, serif));
  font-size: 22px; line-height: 1.15; margin: 0; color: var(--text-primary, #1d1c17);
}
able-sunday-card .ascd-week { margin: 2px 0 10px; font-size: 12px; color: var(--text-muted, #6b7280); }
able-sunday-card .ascd-kid {
  font-family: var(--font-display, var(--font-serif, serif));
  font-size: 15px; margin: 10px 0 4px; color: var(--text-primary, #1d1c17);
}
able-sunday-card .ascd-class {
  border-top: 1px solid var(--border-subtle, #f1f5f9);
  padding: 10px 0 8px; margin-top: 8px;
}
able-sunday-card .ascd-class-head { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
able-sunday-card .ascd-code {
  font-family: var(--font-mono, monospace); font-size: 10px; font-weight: 600;
  color: var(--accent-text, var(--accent, #1b2a4a)); background: var(--accent-soft, #dbeafe);
  border-radius: 999px; padding: 1px 8px; white-space: nowrap;
}
able-sunday-card .ascd-course { font-size: 14px; font-weight: 650; margin: 0; }
able-sunday-card .ascd-counts { display: flex; gap: 6px; margin: 8px 0 6px; }
able-sunday-card .ascd-count {
  flex: 1; text-align: center; border-radius: var(--radius-lg, 10px); padding: 6px 4px;
  display: grid; gap: 0;
}
able-sunday-card .ascd-count b { font-size: 18px; font-variant-numeric: tabular-nums; line-height: 1.1; }
able-sunday-card .ascd-count span {
  font-size: 9px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
}
able-sunday-card .ascd-count[data-k="r"] { background: var(--success-soft, #dcfce7); color: var(--success, #15803d); }
able-sunday-card .ascd-count[data-k="p"] { background: var(--warning-soft, #fef3c7); color: var(--warning-hover, #92400e); }
able-sunday-card .ascd-count[data-k="w"] { background: var(--danger-soft, #fee2e2); color: var(--danger, #b91c1c); }
able-sunday-card .ascd-meta { font-size: 12px; color: var(--text-secondary, #3f3f46); margin: 0 0 2px; }
able-sunday-card .ascd-topics { font-size: 12px; color: var(--text-muted, #6b7280); margin: 0; }
able-sunday-card .ascd-quiet { font-size: 12px; color: var(--text-muted, #6b7280); font-style: italic; margin: 6px 0 0; }
able-sunday-card .ascd-hard {
  margin: 8px 0 0; padding: 8px 10px;
  border-left: 3px solid var(--warning, #b45309);
  background: var(--surface-subtle, #f1f5f9);
  border-radius: 0 var(--radius-md, 8px) var(--radius-md, 8px) 0;
}
able-sunday-card .ascd-hard-head {
  font-size: 9px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: var(--warning-hover, #92400e); margin: 0 0 3px;
}
able-sunday-card .ascd-hard-q {
  font-family: var(--font-serif, serif); font-size: 13px; line-height: 1.4;
  margin: 0 0 4px; color: var(--text-primary, #1d1c17);
}
able-sunday-card .ascd-hard-a, able-sunday-card .ascd-hard-fix {
  font-size: 12px; line-height: 1.45; margin: 0 0 2px;
  color: var(--text-secondary, #3f3f46); overflow-wrap: anywhere;
}
able-sunday-card .ascd-hard-fix { color: var(--text-primary, #1d1c17); }
able-sunday-card .ascd-k {
  font-weight: 700; font-size: 9px; letter-spacing: .05em; text-transform: uppercase;
  color: var(--text-secondary, #3f3f46); margin-right: 3px;
}
able-sunday-card .ascd-dinner {
  margin: 8px 0 0; padding: 8px 12px;
  background: var(--accent-soft, #dbeafe); border-radius: var(--radius-md, 8px);
}
able-sunday-card .ascd-dinner-frame {
  font-size: 9px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: var(--accent-text, var(--accent, #1b2a4a)); margin: 0 0 2px;
}
able-sunday-card .ascd-dinner-q {
  font-family: var(--font-display, var(--font-serif, serif));
  font-size: 15px; line-height: 1.3; margin: 0; color: var(--text-primary, #1d1c17);
}
able-sunday-card .ascd-next { font-size: 12px; color: var(--text-secondary, #3f3f46); margin: 8px 0 0; }
able-sunday-card .ascd-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  margin-top: 12px; padding-top: 4px; border-top: 1px solid var(--border-subtle, #f1f5f9);
}
able-sunday-card .ascd-principle {
  font-size: 9px; letter-spacing: .05em; text-transform: uppercase;
  color: var(--text-secondary, #3f3f46); margin: 0;
}
able-sunday-card .ascd-open {
  font-size: 12px; font-weight: 650; color: var(--accent-text, var(--accent, #1b2a4a));
  text-decoration: none; white-space: nowrap;
  /* The card's ONE affordance, read on a phone: the tap target is the 44px floor
     (rule 28 §2.2), built from padding so the label keeps its size. Measured at
     89×24 before this — the link looked right and could not be reliably hit. */
  display: inline-flex; align-items: center; min-height: 44px; padding-left: 12px;
}
@media (hover: hover) and (pointer: fine) {
  able-sunday-card .ascd-open:hover { text-decoration: underline; }
}


/* able-tabs */

able-tabs { display: block; }
.atabs[data-orientation="horizontal"] {
  display: block;
}
.atabs-bar {
  display: flex;
  gap: 8px;
  padding: 0 16px;
  border-bottom: 1px solid var(--border-default, #e5e7eb);
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scroll-snap-type: x mandatory;
  scrollbar-width: thin;
}
.atabs-bar[data-orientation="vertical"] {
  flex-direction: column;
  border-bottom: 0;
  border-right: 1px solid var(--border-default, #e5e7eb);
  overflow-x: visible;
  scroll-snap-type: none;
}
.atabs-tab {
  flex: 0 0 auto;
  padding: 12px 16px;
  min-height: 44px;
  border: 0;
  background: transparent;
  color: inherit;
  cursor: pointer;
  white-space: nowrap;
  border-bottom: 2px solid transparent;
  scroll-snap-align: start;
  font-size: 14px;
  font-weight: 500;
  transition: border-color 200ms, color 200ms, background-color 120ms;
}
.atabs-tab:focus-visible {
  outline: 2px solid var(--accent-secondary, #1d4ed8);
  outline-offset: -2px;
}
.atabs-tab.is-active {
  border-bottom-color: var(--accent-secondary, #1d4ed8);
  color: var(--accent-secondary-text, var(--accent-secondary, #1d4ed8));
}
.atabs-bar[data-orientation="vertical"] .atabs-tab {
  border-bottom: 0;
  border-right: 2px solid transparent;
  text-align: left;
}
.atabs-bar[data-orientation="vertical"] .atabs-tab.is-active {
  border-right-color: var(--accent-secondary, #1d4ed8);
  border-bottom-color: transparent;
}
.atabs-count { margin-left: 4px; color: var(--text-muted, #6b7280); font-weight: 500; }
.atabs-panel { padding: 16px; }
@media (hover: hover) {
  .atabs-tab:hover {
    background: rgba(0, 0, 0, 0.04);
  }
}
@media (max-width: 479px) {
  .atabs-bar { gap: 4px; padding: 0 12px; }
  .atabs-tab { padding: 10px 14px; font-size: 13px; }
}


/* able-test */

able-test { display: block; container-type: inline-size; margin: 6px 0; font-family: var(--font-sans, sans-serif); color: var(--text-primary, #17191d); --atst-inset: 24px; }
/* Toned surfaces are the tone mixed into the card surface, so a graded row reads in both modes
   (a light-mode pastel under dark-mode text is the failure this rules out). */
able-test {
  --atst-ok-bg: color-mix(in oklab, var(--success, #15884a) 16%, var(--surface-card, #fff));
  --atst-bad-bg: color-mix(in oklab, var(--danger, #d1343b) 14%, var(--surface-card, #fff));
  --atst-warn-bg: color-mix(in oklab, var(--warning, #b45309) 18%, var(--surface-card, #fff));
  --atst-sel-bg: color-mix(in oklab, var(--accent, #1a5cff) 12%, var(--surface-card, #fff));
}
/* The card is one framed sheet, on a page and in chat alike; its bands carry their own inset. */
able-test .atst-card {
  background: var(--surface-card, #fff);
  border: 1px solid var(--border-default, #e6e4de);
  border-radius: 16px; overflow: clip;
  box-shadow: 0 1px 2px rgba(23,25,29,.04), 0 12px 32px -16px rgba(23,25,29,.12);
  padding: 0; position: relative; box-sizing: border-box;
}
able-test[data-demoted] .atst-card { padding: 8px var(--atst-inset); }
able-test .atst-sr {
  position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap;
}
able-test [hidden] { display: none !important; }
able-test svg.ic { width: 1em; height: 1em; flex: none; }
able-test .atst-opt .ic { width: 16px; height: 16px; }
able-test .atst-hd { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; min-width: 0; padding: 12px var(--atst-inset) 8px; }
able-test .atst-hd-eyebrow {
  order: 1; flex: none; margin: 0; font-size: 11px; font-weight: 800; line-height: 20px; letter-spacing: .14em; text-transform: uppercase;
  color: var(--accent-text, var(--accent, #1a5cff));
}
able-test .atst-hd-sp { order: 2; flex: 1; }
able-test .atst-hd-skill {
  order: 4; flex: none; margin-left: auto; max-width: 45%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; box-sizing: border-box;
  font-size: 12px; font-weight: 600; line-height: 16px; color: var(--text-secondary, #5b6069);
  background: var(--surface-subtle, #f3f2ee); border-radius: 999px; padding: 4px 10px;
}
able-test .atst-hd .atst-pill { order: 4; flex: none; margin-left: auto; }
/* The item face and the paper keep their header to one row at every width: the title ellipsizes beside the eyebrow. */
able-test .atst-hd[data-phase="item"], able-test .atst-hd[data-phase="paper"] { flex-wrap: nowrap; }
able-test .atst-hd[data-phase="item"] .atst-hd-sp, able-test .atst-hd[data-phase="paper"] .atst-hd-sp { display: none; }
able-test .atst-hd[data-phase="item"] .atst-hd-title, able-test .atst-hd[data-phase="paper"] .atst-hd-title { order: 2; flex: 1 1 auto; display: block; white-space: nowrap; text-overflow: ellipsis; }
able-test .atst-hd-count { order: 4; flex: none; margin-left: auto; font-size: 12px; font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; color: var(--text-secondary, #5b6069); }
able-test .atst-hd-timer { order: 4; flex: none; font-family: var(--font-mono, monospace); font-variant-numeric: tabular-nums; font-size: 13px; font-weight: 700; white-space: nowrap; }
able-test .atst-hd-timer[data-warn] { color: var(--danger, #d1343b); }
able-test .atst-warnbar { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-top: 0; margin-bottom: 10px; padding: 8px 12px; border-radius: 10px; background: var(--warning-soft, #fef3c7); color: var(--warning-hover, #92400e); font-size: 13px; }
able-test .atst-hd[data-phase="single"] { padding-bottom: 14px; border-bottom: 1px solid var(--border-subtle, #eeece7); }
able-test .atst-lead { margin: 0; padding: 0 var(--atst-inset) 12px; font-size: 15px; line-height: 1.5; color: var(--text-primary, #17191d); overflow-wrap: anywhere; text-wrap: pretty; }
able-test .atst-body { position: relative; min-width: 0; }
/* The chat card keeps ONE height for every question of a set, so nothing under it moves: the
   height most of the set's questions fit in (reserveBodyHeight), capped to the screen; a longer
   question, its options and its feedback scroll inside the card. */
able-test .atst-body {
  display: flex; flex-direction: column; gap: 12px; padding: 14px var(--atst-inset) 12px; box-sizing: border-box;
  height: var(--atst-card-h, min(420px, 62dvh)); overflow: auto; overscroll-behavior: contain; scrollbar-gutter: stable;
}
able-test .atst-body > * { flex: none; }
/* The card fades its bottom edge while more of the question hides below the fold. */
able-test .atst-body[data-more-b] { -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 28px), transparent); mask-image: linear-gradient(to bottom, #000 calc(100% - 28px), transparent); }
able-test .atst-body--probe { position: absolute; top: 0; left: 0; visibility: hidden; pointer-events: none; height: auto; overflow: visible; }
/* The paper: every question on one sheet, numbered, a rule between them; a verdict takes the
   room its words need and no zone is reserved under a question — the sheet scrolls. */
able-test .atst-body--paper { gap: 0; }
able-test .atst-paper-item { padding: 14px 0 12px; border-top: 1px solid var(--border-subtle, #eeece7); }
able-test .atst-paper-item:first-child, able-test .atst-sec-title + .atst-paper-item { border-top: 0; padding-top: 0; }
able-test .atst-body--paper .atst-sec-title { margin: 6px 0 8px; }
able-test .atst-paper-acts { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
able-test .atst-paper-item .atst-verdict + .atst-paper-acts { margin-top: 8px; }
able-test .atst-stem-n {
  flex: none; margin-top: 1px; min-width: 24px; height: 24px; padding: 0 6px; box-sizing: border-box; display: inline-flex; align-items: center; justify-content: center;
  border-radius: 6px; background: var(--accent-soft, #eef3ff); color: var(--accent-text, var(--accent, #1a5cff));
  font-family: var(--font-mono, monospace); font-size: 12px; font-weight: 700; line-height: 1;
}
/* The feedback zone is reserved from the first paint and pins to the body's bottom edge: a
   verdict on a question taller than the body shows over the last options, the stem never
   scrolls away and the body height stands. */
able-test .atst-fb { min-height: var(--atst-fb-h, 76px); margin-top: 0; position: sticky; bottom: 0; z-index: 1; }
able-test .atst-fb:not(:empty) { background: var(--surface-card, #fff); box-shadow: 0 -8px 12px -10px rgba(0,0,0,.35); max-height: 60%; overflow: auto; }
/* Empty it keeps its reserved height and its pin but must not sit over the last option and eat
   the tap; filled (a verdict with Redo / Try another) it takes clicks again. */
able-test .atst-fb:empty { pointer-events: none; }
able-test .atst-body[data-more-b]:has(.atst-verdict) { -webkit-mask-image: none; mask-image: none; }
/* A paper's zones flow with their questions: nothing pins, nothing is reserved. */
able-test .atst-paper-item .atst-fb, able-test .atst-paper-item .atst-fb:not(:empty) { position: static; min-height: 0; margin-top: 10px; max-height: none; overflow: visible; box-shadow: none; }
/* The full-screen walk scrolls as a whole: the feedback takes the room its words need and reserves none. */
able-test .atst-fb .atst-verdict { margin-top: 0; }
able-test .atst-fb .atst-error { margin-top: 0; }
able-test .atst-qscroll { max-height: 320px; overflow: auto; overscroll-behavior: contain; }
able-test .atst-face { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; margin: 8px 0 0; }
able-test .atst-face-num { font-size: 22px; font-weight: 700; line-height: 1; font-variant-numeric: tabular-nums; }
able-test .atst-face-cap { font-size: 12px; color: var(--text-secondary, #3f3f46); }
able-test .atst-drawer {
  margin: 0; padding: 8px 10px; border: 1px dashed var(--danger, #b91c1c); border-radius: var(--radius-md, 8px);
  background: var(--atst-bad-bg); color: var(--text-primary, #1d1c17); font-size: 13px; line-height: 1.45;
}
able-test .atst-drawer-head { margin: 0; color: var(--danger, #b91c1c); }
able-test .atst-drawer-line { margin: 2px 0 0; }
able-test .atst-drawer .atst-why { color: var(--text-primary, #1d1c17); }
able-test .atst-drawer-acts { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
able-test .atst-drawer-acts .atst-btn { flex: 1 1 calc(50% - 3px); }
able-test .atst-lbl { font-weight: 600; color: var(--text-secondary, #3f3f46); margin-right: 2px; }
able-test .atst-rubric-quote { margin: 6px 0 0; font-size: 12px; font-style: italic; color: var(--text-muted, #6b7280); }
able-test .atst-missed-toggle { margin-top: 6px; }
able-test .atst-review-stem .atst-md, able-test .atst-q-stem .atst-md { min-width: 0; overflow: hidden; }
able-test .atst-q-stem .atst-md p { margin: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
able-test .atst-review-stem .atst-md p { margin: 0; }
able-test .atst-hd-title {
  order: 5; flex: 1 1 100%; min-width: 0; margin: 0;
  font-family: var(--font-display, var(--font-sans, sans-serif)); font-size: 16px; font-weight: 700; letter-spacing: -.01em; line-height: 20px;
  color: var(--text-primary, #1d1c17); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
able-test .atst-meta {
  display: flex; flex-wrap: wrap; gap: 4px 10px; align-items: center;
  font-size: 12px; color: var(--text-muted, #6b7280); margin: 6px 0 0;
}
able-test .atst-pill {
  font-size: 11px; font-weight: 700; line-height: 1; white-space: nowrap;
  border-radius: 999px; padding: 5px 9px;
  background: var(--surface-subtle, #f1f5f9); color: var(--text-secondary, #3f3f46);
}
able-test .atst-pill[data-t="pass"] { background: var(--success-soft, #dcfce7); color: var(--success, #15803d); }
able-test .atst-pill[data-t="fail"] { background: var(--danger-soft, #fee2e2); color: var(--danger, #b91c1c); }
able-test .atst-pill[data-t="accent"] { background: var(--accent-soft, #dbeafe); color: var(--accent-text, var(--accent, #1b2a4a)); }
/* The step row: one segment per question stretched across the row, the count beside it, a
   divider under it. Too many segments for the row scroll sideways (the strip fades the edge
   more hide behind); the results face wraps them instead. */
able-test .atst-steprow { display: flex; align-items: center; gap: 12px; padding: 0 var(--atst-inset) 10px; border-bottom: 1px solid var(--border-subtle, #eeece7); }
able-test .atst-steps {
  position: relative; flex: 1; display: flex; flex-wrap: nowrap; gap: 5px; margin: 0 -4px; padding: 4px; list-style: none; min-width: 0;
  overflow-x: auto; overflow-y: hidden; overscroll-behavior-x: contain; scrollbar-width: none; -webkit-overflow-scrolling: touch; scroll-snap-type: x proximity;
}
able-test .atst-steps::-webkit-scrollbar { display: none; }
able-test .atst-steps[data-more-r] { -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent); mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent); }
able-test .atst-steps[data-more-l] { -webkit-mask-image: linear-gradient(to right, transparent, #000 28px); mask-image: linear-gradient(to right, transparent, #000 28px); }
able-test .atst-steps[data-more-l][data-more-r] { -webkit-mask-image: linear-gradient(to right, transparent, #000 28px, #000 calc(100% - 28px), transparent); mask-image: linear-gradient(to right, transparent, #000 28px, #000 calc(100% - 28px), transparent); }
able-test .atst-steps--wrap { flex-wrap: wrap; overflow: visible; scroll-snap-type: none; gap: 5px; margin: 4px 0 10px; padding: 0; }
able-test .atst-steps--wrap .atst-step { flex: 0 0 auto; min-width: 30px; padding: 0 8px; }
able-test .atst-steps-label { flex: none; font-family: var(--font-mono, monospace); font-size: 12px; font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; color: var(--text-secondary, #5b6069); }
able-test .atst-step {
  position: relative; flex: 1 0 24px; scroll-snap-align: center; display: inline-flex; align-items: center; justify-content: center; gap: 4px;
  min-width: 24px; height: 22px; padding: 0 6px; box-sizing: border-box; border-radius: 6px; margin: 0;
  border: 1.5px solid var(--surface-subtle, #f3f2ee); background: var(--surface-subtle, #f3f2ee); color: var(--text-muted, #a3a8b1);
  font-family: var(--font-mono, monospace); font-size: 11px; font-weight: 700; font-variant-numeric: tabular-nums; line-height: 1; cursor: default;
}
able-test .atst-step::before { content: ''; position: absolute; inset: -6px -3px; }
able-test button.atst-step { cursor: pointer; }
able-test .atst-step[data-state="answered"] { background: var(--accent-soft, #eef3ff); border-color: var(--accent-soft, #eef3ff); color: var(--accent-text, var(--accent, #1a5cff)); }
able-test .atst-step[data-state="current"] { background: var(--surface-card, #fff); border-color: var(--accent, #1a5cff); color: var(--accent, #1a5cff); }
able-test .atst-step[data-state="right"] { background: var(--success-soft, #e6f4ec); border-color: var(--success-soft, #e6f4ec); color: var(--success, #15884a); }
able-test .atst-step[data-state="wrong"] { background: var(--danger-soft, #fbe9ea); border-color: var(--danger-soft, #fbe9ea); color: var(--danger, #d1343b); }
able-test .atst-step[data-state="repaired"] { background: var(--success-soft, #e6f4ec); border-color: var(--success, #15884a); border-style: dashed; color: var(--success, #15884a); }
/* The question on screen carries its tone's full border. */
able-test .atst-step[aria-current="step"][data-state="right"] { border-color: var(--success, #15884a); }
able-test .atst-step[aria-current="step"][data-state="wrong"] { border-color: var(--danger, #d1343b); }
able-test .atst-step[aria-current="step"][data-state="answered"] { border-color: var(--accent, #1a5cff); }
able-test .atst-step[data-state="right"]::after, able-test .atst-step[data-state="repaired"]::after { content: '✓'; font-size: 9px; }
able-test .atst-step[data-state="wrong"]::after { content: '✕'; font-size: 9px; }
able-test .atst-step[data-flag]::after { content: ''; position: absolute; top: -3px; right: -3px; width: 8px; height: 8px; border-radius: 50%; background: var(--warning, #b45309); }
able-test .atst-step:focus-visible { outline: 2px solid var(--accent, #1a5cff); outline-offset: 2px; }
able-test .atst-btn {
  min-height: 40px; min-width: 40px; padding: 0 18px; box-sizing: border-box;
  border-radius: 10px; border: 1.5px solid var(--border-default, #e6e4de);
  background: var(--surface-card, #fff); color: var(--text-primary, #17191d);
  font: inherit; font-size: 14px; font-weight: 800; line-height: 1.2; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
}
able-test .atst-btn--primary {
  background: var(--accent, #1a5cff); color: var(--on-accent, #fff); border-color: transparent;
  box-shadow: 0 6px 16px -8px var(--accent, #1a5cff);
}
able-test .atst-btn--quiet { border-color: transparent; background: transparent; color: var(--accent-text, var(--accent, #1a5cff)); padding: 0 6px; min-width: 0; font-size: 13px; font-weight: 700; }
able-test .atst-btn--danger { color: var(--danger, #d1343b); }
able-test .atst-btn:disabled { cursor: not-allowed; opacity: .4; box-shadow: none; }
able-test .atst-btn:focus-visible,
able-test .atst-opt:focus-within,
able-test .atst-input:focus-visible,
able-test .atst-navbtn:focus-visible {
  outline: 2px solid var(--accent, #1a5cff); outline-offset: 2px;
}
able-test .atst-btn .ic { width: 1em; height: 1em; }
/* The footer band: the set's doors on the left, the key legend and the primary on the right —
   one row in every state, so the composer under the card never moves when a grade changes
   the buttons; a narrow card shows the short labels. */
able-test .atst-actions {
  display: flex; flex-wrap: nowrap; gap: 16px; align-items: center; min-width: 0; min-height: 40px;
  padding: 10px var(--atst-inset) 12px; border-top: 1px solid var(--border-subtle, #eeece7);
  background: color-mix(in oklab, var(--surface-subtle, #f3f2ee) 45%, var(--surface-card, #fff));
}
/* The footer sticks to the scroller's bottom edge while the card is on screen, so Next and Check never need a scroll. */
able-test .atst-actions { position: sticky; bottom: 0; z-index: 2; box-shadow: 0 -1px 0 var(--border-subtle, #eeece7); }
able-test .atst-actions .atst-spacer { display: block; flex: 1 0 0; }
able-test .atst-actions .atst-btn { flex: 0 1 auto; }
able-test .atst-actions .atst-btn > span,
able-test .atst-actions .atst-hint { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
able-test .atst-keyhint { flex: none; font-family: var(--font-mono, monospace); font-size: 12px; font-weight: 600; white-space: nowrap; color: var(--text-muted, #8a8f98); }
able-test .atst-btn-s { display: none; }
/* A one-row chip strip — the reasons under Skip / Remove, the flags under a stem — scrolls
   sideways rather than wrapping, so the row it stands in keeps its height. */
able-test .atst-chips {
  display: flex; flex: 1 1 auto; align-items: center; gap: 6px; min-width: 0; padding: 2px 0;
  overflow-x: auto; overflow-y: hidden; scrollbar-width: none; -webkit-overflow-scrolling: touch;
}
able-test .atst-chips::-webkit-scrollbar { display: none; }
able-test .atst-chips .atst-chip { flex: none; min-height: 36px; padding: 0 12px; font-size: 13px; font-weight: 600; white-space: nowrap; }
able-test .atst-chips .atst-chip--go { border-color: var(--accent, #1a5cff); color: var(--accent-text, var(--accent, #1a5cff)); }
able-test .atst-chips-label { flex: none; font-size: 12px; font-weight: 700; white-space: nowrap; color: var(--text-secondary, #5b6069); }
able-test .atst-actions .atst-chips { margin: -2px 0; }
able-test .atst-body > .atst-chips { margin-top: -4px; }
/* The flag on the stem row: a quiet icon with a 44-px hit area; filled once the question is flagged. */
able-test .atst-flagbtn {
  position: relative; flex: none; width: 28px; height: 28px; margin: 0 -4px 0 0; padding: 0; border: 0; border-radius: 50%;
  background: transparent; color: var(--text-subtle, #b5b9c2); cursor: pointer; display: inline-flex; align-items: center; justify-content: center; font-size: 13px;
}
able-test .atst-flagbtn::before { content: ''; position: absolute; inset: -8px; }
able-test .atst-flagbtn[data-on] { color: var(--warning, #b45309); background: var(--atst-warn-bg); }
able-test .atst-flagbtn:focus-visible { outline: 2px solid var(--accent, #1a5cff); outline-offset: 2px; }
able-test .atst-item { border: 0; margin: 0; padding: 0; min-width: 0; }
able-test .atst-stem {
  font-size: 18px; line-height: 1.45; font-weight: 600; letter-spacing: -.01em; margin: 0 0 12px; padding: 0; color: var(--text-primary, #17191d);
  overflow-wrap: anywhere; text-wrap: pretty;
}
able-test .atst-stem:focus { outline: none; }
able-test .atst-stem-row { display: flex; align-items: flex-start; gap: 10px; }
able-test .atst-stem-row .atst-md { flex: 1; min-width: 0; }
able-test .atst-stem-ic { flex: none; margin-top: 2px; width: 24px; height: 24px; display: inline-flex; align-items: center; justify-content: center; border-radius: 50%; background: var(--surface-subtle, #f3f2ee); color: var(--text-secondary, #5b6069); font-size: 13px; }
able-test .atst-md p { margin: 0 0 6px; }
able-test .atst-md p:last-child { margin-bottom: 0; }
able-test .atst-md ul, able-test .atst-md ol { margin: 0 0 6px; padding-left: 22px; }
able-test .atst-md code {
  font-family: var(--font-mono, monospace); font-size: .92em;
  background: var(--surface-subtle, #f1f5f9); border-radius: 4px; padding: 1px 5px;
}
able-test .atst-md img { max-width: 100%; height: auto; border-radius: var(--radius-md, 8px); display: block; margin: 8px 0; }
/* A run of "label  amount" lines in a stem (an accounting sheet's figures) reads as one ledger. */
able-test .atst-figs { border-collapse: collapse; margin: 4px 0 6px; max-width: 100%; font-size: 14px; line-height: 1.25; }
able-test .atst-figs td { padding: 1px 0; border-top: 1px solid var(--border-subtle, #f1f5f9); vertical-align: baseline; }
able-test .atst-figs tr:first-child td { border-top: 0; }
able-test .atst-figs .atst-figs-n { text-align: right; padding-left: 20px; font-variant-numeric: tabular-nums; white-space: nowrap; }
/* A markdown pipe table in a stem (an accounting sheet, a comparison) reads as a real grid. */
able-test .atst-md-table { border-collapse: collapse; width: 100%; margin: 4px 0 6px; font-size: 14px; line-height: 1.3; }
able-test .atst-md-table th, able-test .atst-md-table td { padding: 4px 8px; border: 1px solid var(--border-subtle, #f1f5f9); text-align: left; vertical-align: baseline; }
able-test .atst-md-table thead th { font-weight: 700; color: var(--text-secondary, #5b6069); background: var(--surface-subtle, #f3f2ee); border-bottom: 1px solid var(--border-default, #e6e4de); }
able-test .atst-md-table .atst-md-num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
able-test .atst-signals { display: flex; flex-wrap: wrap; gap: 6px; margin: 0; }
able-test .atst-pill .ic { width: 1em; height: 1em; vertical-align: -2px; margin-right: 4px; }
able-test .atst-opts { display: grid; gap: 8px; margin: 0; padding: 0; list-style: none; }
able-test .atst-opt {
  position: relative; display: flex; align-items: center; gap: 12px; min-height: 44px; box-sizing: border-box;
  padding: 8px 12px 8px 8px; border: 1.5px solid var(--border-default, #e6e4de); border-radius: 10px;
  background: var(--surface-card, #fff); color: var(--text-primary, #17191d); cursor: pointer; font-size: 15px; font-weight: 600; line-height: 1.3; margin: 0;
  overflow-wrap: anywhere;
}
able-test .atst-opt::before { content: ''; position: absolute; inset: -2px 0; }
/* The native control stays for the keyboard and the screen reader; the letter badge is the visible indicator. */
able-test .atst-opt input { position: absolute; width: 1px; height: 1px; margin: 0; padding: 0; opacity: 0; pointer-events: none; }
able-test .atst-opt[data-checked] { border-color: var(--accent, #1a5cff); background: var(--atst-sel-bg); box-shadow: 0 0 0 3px color-mix(in oklab, var(--accent, #1a5cff) 12%, transparent); }
able-test .atst-opt[data-v="correct"] { border-color: var(--success, #15884a); background: var(--surface-card, #fff); box-shadow: none; }
able-test .atst-opt[data-v="correct"][data-checked] { background: var(--atst-ok-bg); }
able-test .atst-opt[data-v="incorrect"] { border-color: var(--danger, #d1343b); background: var(--atst-bad-bg); box-shadow: none; }
able-test .atst-opt[data-locked]:not([data-v]) { border-color: var(--border-subtle, #f0eee9); color: var(--text-muted, #8a8f98); }
able-test .atst-opt[data-locked]:not([data-v]) .atst-opt-letter { border-color: var(--border-subtle, #f0eee9); color: var(--text-subtle, #c3c7ce); }
able-test .atst-opt[data-locked] { cursor: default; }
able-test .atst-opt[data-busy] { opacity: .7; }
able-test .atst-opt-letter {
  flex: none; width: 26px; height: 26px; box-sizing: border-box; display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%; border: 1.5px solid var(--border-strong, #d5d3cd); background: var(--surface-card, #fff); color: var(--text-secondary, #5b6069);
  font-family: var(--font-mono, monospace); font-size: 12px; font-weight: 700; line-height: 1;
}
able-test .atst-opt input[type="checkbox"] + .atst-opt-letter { border-radius: 7px; }
able-test .atst-opt[data-checked] .atst-opt-letter { border-color: var(--accent, #1a5cff); background: var(--accent, #1a5cff); color: var(--on-accent, #fff); }
able-test .atst-opt[data-v="correct"] .atst-opt-letter { border-color: var(--success, #15884a); background: var(--surface-card, #fff); color: var(--success, #15884a); }
able-test .atst-opt[data-v="correct"][data-checked] .atst-opt-letter { background: var(--success, #15884a); color: var(--on-success, #fff); }
able-test .atst-opt[data-v="incorrect"] .atst-opt-letter { border-color: var(--danger, #d1343b); background: var(--danger, #d1343b); color: var(--on-danger, #fff); }
able-test .atst-opt-text { flex: 1; min-width: 0; }
able-test .atst-opt-tag { flex: none; font-size: 11px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase; white-space: nowrap; color: var(--text-secondary, #5b6069); }
able-test .atst-opt[data-v="correct"] .atst-opt-tag { color: var(--success, #15884a); }
able-test .atst-opt[data-v="incorrect"] .atst-opt-tag { color: var(--danger, #d1343b); }
able-test .atst-opt-mark { font-weight: 700; flex: none; }
able-test .atst-input {
  font: inherit; font-size: 16px; font-weight: 500; line-height: 1.5; min-height: 48px; padding: 12px 16px; box-sizing: border-box; width: 100%;
  border: 1.5px solid var(--border-default, #e6e4de); border-radius: 10px;
  background: var(--surface-card, #fff); color: var(--text-primary, #17191d);
}
able-test .atst-input:focus { outline: none; border-color: var(--accent, #1a5cff); box-shadow: 0 0 0 4px color-mix(in oklab, var(--accent, #1a5cff) 12%, transparent); }
able-test .atst-input[data-v="correct"] { border-color: var(--success, #15884a); background: var(--atst-ok-bg); }
able-test .atst-input[data-v="incorrect"] { border-color: var(--danger, #d1343b); background: var(--atst-bad-bg); }
able-test textarea.atst-input { min-height: 56px; resize: none; overflow: auto; }
able-test .atst-input--num { font-family: var(--font-mono, monospace); font-size: 20px; font-weight: 600; letter-spacing: -.02em; }
able-test .atst-input--inline { display: inline-block; width: auto; min-width: 6ch; max-width: 100%; margin: 2px 2px; vertical-align: baseline; min-height: 40px; padding: 4px 8px; }
able-test select.atst-input { appearance: auto; }
able-test .atst-row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
able-test .atst-row .atst-input { flex: 1; min-width: 120px; }
able-test .atst-row > .atst-hint { margin: 0; padding: 0 12px; font-size: 14px; font-weight: 700; }
able-test .atst-hint { font-size: 12px; font-weight: 600; color: var(--text-muted, #8a8f98); margin: 8px 0 0; }
able-test .atst-verdict {
  position: relative; display: flex; align-items: flex-start; gap: 12px; padding: 12px 14px; margin-top: 0;
  border-radius: 12px; font-size: 14px; line-height: 1.5;
  background: var(--surface-subtle, #f3f2ee); color: var(--text-primary, #17191d);
}
able-test .atst-verdict > .ic, able-test .atst-verdict > .atst-ic { width: 24px; height: 24px; flex: none; margin-top: 0; color: var(--text-secondary, #5b6069); }
able-test .atst-verdict[data-v="correct"] { background: var(--atst-ok-bg); }
able-test .atst-verdict[data-v="correct"] > .ic, able-test .atst-verdict[data-v="correct"] > .atst-ic, able-test .atst-verdict[data-v="correct"] .atst-verdict-head { color: var(--success, #15884a); }
able-test .atst-verdict[data-v="incorrect"], able-test .atst-verdict[data-v="blank"] { background: var(--atst-bad-bg); }
able-test .atst-verdict[data-v="incorrect"] > .ic, able-test .atst-verdict[data-v="incorrect"] > .atst-ic, able-test .atst-verdict[data-v="incorrect"] .atst-verdict-head,
able-test .atst-verdict[data-v="blank"] > .ic, able-test .atst-verdict[data-v="blank"] > .atst-ic, able-test .atst-verdict[data-v="blank"] .atst-verdict-head { color: var(--danger, #d1343b); }
able-test .atst-verdict[data-v="partial"] { background: var(--atst-warn-bg); }
able-test .atst-verdict[data-v="partial"] > .ic, able-test .atst-verdict[data-v="partial"] > .atst-ic, able-test .atst-verdict[data-v="partial"] .atst-verdict-head { color: var(--warning-hover, #92400e); }
able-test .atst-verdict-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
able-test .atst-verdict-head { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; font-weight: 800; font-size: 14px; }
able-test .atst-try {
  font-family: var(--font-mono, monospace); font-size: 11px; font-weight: 700; line-height: 1; padding: 3px 7px; border-radius: 999px;
  background: var(--surface-card, #fff); color: var(--text-secondary, #5b6069);
}
able-test .atst-why { margin: 0; font-size: 14px; line-height: 1.5; color: var(--text-secondary, #3a3f47); text-wrap: pretty; }
able-test .atst-why b {
  display: inline-block; min-width: 1.5em; text-align: center; padding: 1px 5px; margin-right: 6px; border-radius: 5px;
  font-family: var(--font-mono, monospace); font-size: 11px; line-height: 1.3; background: var(--surface-card, #fff); color: var(--text-secondary, #5b6069);
}
able-test .atst-rubric {
  margin: 8px 0 0; padding: 8px 10px; border-radius: var(--radius-md, 8px);
  background: var(--surface-card, #fff); border: 1px solid var(--border-subtle, #f1f5f9); color: var(--text-primary, #1d1c17);
}
able-test .atst-rubric-h {
  display: flex; justify-content: space-between; gap: 8px; margin: 0 0 6px;
  font-size: 11px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--text-secondary, #3f3f46);
}
able-test .atst-rubric ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 4px; }
able-test .atst-rubric li { display: flex; gap: 8px; align-items: flex-start; font-size: 13px; line-height: 1.4; }
able-test .atst-rubric li .ic { width: 14px; height: 14px; margin-top: 3px; }
able-test .atst-rubric li[data-met="1"] .ic { color: var(--success, #15803d); }
able-test .atst-rubric li[data-met="0"] { color: var(--text-secondary, #3f3f46); }
able-test .atst-rubric li[data-met="0"] .ic { color: var(--danger, #b91c1c); }
able-test .atst-rubric-pts { margin-left: auto; font-variant-numeric: tabular-nums; font-size: 12px; color: var(--text-muted, #6b7280); white-space: nowrap; }
able-test .atst-rubric-fb { margin: 6px 0 0; font-size: 13px; color: var(--text-secondary, #3f3f46); }
able-test .atst-navbtn {
  min-width: 44px; min-height: 44px; padding: 0 6px; position: relative; cursor: pointer;
  border: 1px solid var(--border-default, #e5e7eb); border-radius: var(--radius-md, 8px);
  background: var(--surface-base, #fafafa); color: var(--text-primary, #1d1c17);
  font: inherit; font-size: 13px; font-weight: 600; font-variant-numeric: tabular-nums;
}
able-test .atst-navbtn[data-s="answered"] { background: var(--accent-soft, #dbeafe); border-color: var(--accent, #2563eb); }
able-test .atst-navbtn[data-flag]::after {
  content: ''; position: absolute; top: 4px; right: 4px; width: 8px; height: 8px; border-radius: 50%; background: var(--warning, #b45309);
}
able-test .atst-navbtn[aria-current="true"] { box-shadow: 0 0 0 2px var(--accent, #2563eb); }
able-test .atst-navbtn:disabled { cursor: default; opacity: .6; }
able-test .atst-sec-title {
  font-size: 11px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: var(--text-secondary, #3f3f46); margin: 10px 0 4px;
}
able-test .atst-stim {
  border: 1px solid var(--border-subtle, #f1f5f9); border-radius: var(--radius-md, 8px);
  background: var(--surface-subtle, #f1f5f9); padding: 8px 10px; margin: 0 0 10px; font-size: 14.5px; line-height: 1.5;
}
able-test .atst-stim[hidden] { display: none; }
able-test .atst-stim-toggle { margin: 0 0 6px; }
able-test .atst-bank { display: flex; flex-wrap: wrap; gap: 6px; margin: 8px 0; }
able-test .atst-chip {
  position: relative; min-height: 40px; padding: 0 12px; border-radius: 999px; border: 1px solid var(--border-default, #e5e7eb);
  background: var(--surface-base, #fafafa); font: inherit; font-size: 14px; cursor: pointer; color: var(--text-primary, #1d1c17);
}
able-test .atst-chip[data-used] { opacity: .45; }
able-test .atst-chip::before, able-test .atst-mbtn::before { content: ''; position: absolute; inset: -2px 0; }
able-test .atst-gap {
  display: inline-flex; align-items: center; min-height: 32px; min-width: 5ch; padding: 0 8px; margin: 2px; position: relative;
  border: 1px dashed var(--border-strong, #9ca3af); border-radius: var(--radius-md, 8px);
  background: var(--surface-base, #fafafa); font: inherit; font-size: 15px; cursor: pointer; vertical-align: baseline; color: var(--text-primary, #1d1c17);
}
able-test .atst-gap[data-sel] { border-style: solid; border-color: var(--accent, #2563eb); background: var(--atst-sel-bg); }
able-test .atst-gap[data-v="correct"], able-test .atst-input--inline[data-v="correct"] { border-style: solid; border-color: var(--success, #15803d); background: var(--atst-ok-bg); }
able-test .atst-gap[data-v="incorrect"], able-test .atst-input--inline[data-v="incorrect"] { border-style: solid; border-color: var(--danger, #b91c1c); background: var(--atst-bad-bg); }
able-test .atst-gapkey { font-weight: 700; padding: 0 3px; border-radius: 4px; }
able-test .atst-gapkey[data-v="correct"] { background: var(--atst-ok-bg); color: var(--success, #15803d); }
able-test .atst-gapkey[data-v="incorrect"] { background: var(--atst-bad-bg); color: var(--danger, #b91c1c); }
able-test .atst-gap::before { content: ''; position: absolute; inset: -6px 0; }
able-test .atst-order { display: grid; gap: 6px; margin: 0; padding: 0; list-style: none; }
able-test .atst-order li {
  display: flex; align-items: center; gap: 8px; padding: 4px 4px 4px 10px;
  border: 1px solid var(--border-default, #e5e7eb); border-radius: var(--radius-md, 8px); background: var(--surface-base, #fafafa); font-size: 14.5px;
}
able-test .atst-order .atst-opt-text { flex: 1; }
able-test .atst-order li[draggable="true"] { cursor: grab; }
able-test .atst-order li[data-drag] { opacity: .45; }
able-test .atst-order li[data-over] { border-color: var(--accent, #2563eb); box-shadow: 0 -2px 0 0 var(--accent, #2563eb); }
able-test .atst-grip { flex: none; display: inline-flex; width: 16px; color: var(--text-muted, #6b7280); }
able-test .atst-grip .ic { width: 1em; height: 1em; }
able-test .atst-match { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
able-test .atst-match-col { display: grid; gap: 6px; align-content: start; }
able-test .atst-mbtn {
  position: relative; display: flex; align-items: flex-start; gap: 8px; min-height: 40px; box-sizing: border-box; width: 100%;
  padding: 8px 8px; border: 1px solid var(--border-default, #e5e7eb); border-radius: var(--radius-md, 8px);
  background: var(--surface-base, #fafafa); color: var(--text-primary, #1d1c17); font: inherit; font-size: 14.5px; line-height: 1.35;
  text-align: left; cursor: pointer; overflow-wrap: anywhere;
}
able-test .atst-mbtn[data-sel] { border-color: var(--accent, #2563eb); background: var(--atst-sel-bg); box-shadow: inset 0 0 0 1px var(--accent, #2563eb); }
able-test .atst-mbtn[data-paired] { border-color: var(--border-strong, #9ca3af); }
able-test .atst-mbtn[data-v="correct"] { border-color: var(--success, #15803d); background: var(--atst-ok-bg); }
able-test .atst-mbtn[data-v="incorrect"] { border-color: var(--danger, #b91c1c); background: var(--atst-bad-bg); }
able-test .atst-mbtn:disabled { cursor: default; }
able-test .atst-mbtn:focus-visible { outline: 2px solid var(--accent, #2563eb); outline-offset: 2px; }
able-test .atst-mbtn .ic { width: 1em; height: 1em; flex: none; margin-top: 3px; }
able-test .atst-mpair { flex: none; max-width: 45%; font-size: 13px; font-weight: 600; color: var(--accent-text, var(--accent, #1b2a4a)); overflow-wrap: anywhere; }
able-test .atst-mkey { flex: none; display: inline-flex; align-items: center; gap: 3px; margin-top: 1px; padding: 1px 7px; border-radius: var(--radius-full, 999px); background: var(--success-soft, #dcfce7); color: var(--success, #15803d); font-size: 12px; font-weight: 700; }
able-test .atst-mkey .ic { margin-top: 0; }
able-test table.atst-two { width: 100%; border-collapse: collapse; font-size: 14.5px; }
able-test table.atst-two th, able-test table.atst-two td {
  padding: 7px 6px; border-bottom: 1px solid var(--border-subtle, #f1f5f9); text-align: left; vertical-align: top;
}
able-test table.atst-two th { position: sticky; top: 0; z-index: 1; background: var(--surface-card, #fff); font-size: 12px; color: var(--text-secondary, #3f3f46); }
able-test table.atst-two td.atst-two-pick { text-align: center; width: 64px; }
able-test table.atst-two input { width: 20px; height: 20px; margin: 0; accent-color: var(--accent, #2563eb); }
able-test .atst-two-lab { display: inline-flex; align-items: center; justify-content: center; min-width: 44px; min-height: 44px; margin: -10px 0; cursor: pointer; }
able-test .atst-qc { display: grid; gap: 6px; margin: 0 0 10px; }
able-test .atst-qc div { padding: 8px 10px; border-radius: var(--radius-md, 8px); background: var(--surface-subtle, #f1f5f9); }
able-test .atst-qc b { display: block; font-size: 11px; letter-spacing: .08em; text-transform: uppercase; color: var(--text-secondary, #3f3f46); margin-bottom: 3px; }
able-test .atst-score { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin: 10px 0 4px; }
able-test .atst-score-num { font-family: var(--font-mono, monospace); font-size: 34px; font-weight: 800; letter-spacing: -.03em; line-height: 1; font-variant-numeric: tabular-nums; }
able-test .atst-score-cap { font-size: 12px; color: var(--text-muted, #6b7280); }
able-test .atst-score-side { font-size: 12px; color: var(--text-secondary, #3f3f46); font-variant-numeric: tabular-nums; line-height: 1.2; }
able-test .atst-bar { position: relative; height: 8px; border-radius: 999px; background: var(--surface-hover, #f0f1f2); margin: 8px 0 12px; }
able-test .atst-bar[data-marked] { margin-bottom: 28px; }
able-test .atst-bar-fill { height: 100%; border-radius: 999px; background: var(--accent, #2563eb); }
able-test .atst-bar-fill[data-t="pass"] { background: var(--success, #15803d); }
able-test .atst-bar-fill[data-t="fail"] { background: var(--danger, #b91c1c); }
able-test .atst-bar-mark { position: absolute; top: -4px; width: 2px; height: 16px; background: var(--text-primary, #1d1c17); }
able-test .atst-bar-mark span { position: absolute; top: 16px; left: 50%; transform: translateX(-50%); font-size: 10px; white-space: nowrap; color: var(--text-muted, #6b7280); }
able-test .atst-h {
  font-size: 9px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase;
  color: var(--text-secondary, #3f3f46); margin: 14px 0 6px; padding-top: 8px; border-top: 1px solid var(--border-subtle, #f1f5f9);
}
able-test .atst-res-top { display: grid; gap: 4px 16px; align-items: start; }
able-test .atst-res-score { min-width: 0; }
able-test .atst-res-skills { min-width: 0; }
able-test .atst-skill { margin: 0 0 6px; }
able-test .atst-skill-head { display: flex; justify-content: space-between; gap: 8px; align-items: baseline; font-size: 12.5px; }
able-test .atst-skill-name { font-weight: 600; overflow-wrap: anywhere; }
able-test .atst-skill-pct { font-variant-numeric: tabular-nums; white-space: nowrap; color: var(--text-secondary, #3f3f46); font-size: 12px; }
able-test .atst-skill-bar { height: 4px; border-radius: 999px; background: var(--surface-hover, #f0f1f2); margin: 3px 0 2px; overflow: hidden; }
able-test .atst-skill-fill { height: 100%; border-radius: 999px; background: var(--text-subtle, #9ca3af); }
able-test .atst-skill-fill[data-e="strong"] { background: var(--success, #15803d); }
able-test .atst-skill-fill[data-e="mixed"] { background: var(--warning, #b45309); }
able-test .atst-skill-fill[data-e="weak"] { background: var(--danger, #b91c1c); }
able-test .atst-qlist { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: minmax(0, 1fr); gap: 6px; }
able-test .atst-qlist li { min-width: 0; }
able-test .atst-q { display: flex; flex-wrap: wrap; gap: 6px 12px; align-items: center; min-height: 40px; padding: 8px 12px; border-radius: 10px; background: var(--surface-subtle, #faf9f6); font-size: 14px; }
able-test .atst-q-stem { flex: 1 1 12em; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
able-test .atst-q-tries { display: flex; gap: 4px; align-items: center; flex: none; margin-left: auto; font-size: 11px; font-variant-numeric: tabular-nums; color: var(--text-secondary, #3f3f46); }
able-test .atst-tv { display: inline-flex; align-items: center; gap: 3px; padding: 3px 6px; border-radius: 999px; line-height: 1; background: var(--surface-subtle, #f1f5f9); white-space: nowrap; }
able-test .atst-tv[data-v="correct"] { background: var(--success-soft, #dcfce7); color: var(--success, #15803d); }
able-test .atst-tv[data-v="incorrect"], able-test .atst-tv[data-v="blank"] { background: var(--danger-soft, #fee2e2); color: var(--danger, #b91c1c); }
able-test .atst-tv[data-v="partial"] { background: var(--warning-soft, #fef3c7); color: var(--warning-hover, #92400e); }
able-test .atst-tv[data-v="repaired"] { background: var(--success-soft, #dcfce7); color: var(--success, #15803d); border: 1px dashed var(--success, #15803d); }
able-test .atst-q-detail { margin: 0 0 6px; padding: 10px 12px; border-radius: var(--radius-md, 8px); background: var(--surface-subtle, #f1f5f9); font-size: 13px; }
able-test .atst-q-detail .atst-stem { font-size: 14px; margin-bottom: 6px; }
able-test .atst-kv { margin: 0 0 3px; font-size: 13px; }
able-test .atst-kv b { color: var(--text-secondary, #3f3f46); font-weight: 600; margin-right: 6px; }
able-test .atst-kv[data-v="incorrect"] span { color: var(--danger, #b91c1c); }
/* The results face fits the pane it stands in: the question list gives way first, the whole
   sheet scrolls only when the score and skills alone exceed the budget. */
able-test .atst-results { display: flex; flex-direction: column; min-height: 0; max-height: calc(var(--atst-view-h, 100dvh) - 176px); overflow: auto; overscroll-behavior: contain; }
able-test .atst-results > * { flex: none; }
able-test .atst-results > .atst-qscroll { flex: 0 1 auto; min-height: 96px; max-height: none; }
able-test .atst-kv[data-v="correct"] span { color: var(--success, #15803d); }
able-test .atst-note { font-size: 13px; color: var(--text-secondary, #3f3f46); margin: 8px 0 0; }
able-test .atst-error { font-size: 13px; color: var(--danger, #b91c1c); margin: 8px 0 0; }
able-test .atst-stub { font-size: 12px; color: var(--text-muted, #6b7280); }
/* The review list: a stem wraps in its column (the track is minmax(0, 1fr), so a long stem never
   widens the list past its container); the number stays at the top of a wrapped stem. */
able-test .atst-review { display: grid; grid-template-columns: minmax(0, 1fr); gap: 6px; margin: 0; padding: 0; list-style: none; }
able-test .atst-review li { display: flex; gap: 10px; align-items: flex-start; min-width: 0; font-size: 14px; }
able-test .atst-review .atst-navbtn { flex: none; }
able-test .atst-review .atst-review-stem { flex: 1; min-width: 0; padding-top: 12px; line-height: 1.4; overflow-wrap: anywhere; }
able-test .atst-review li > .atst-hint { flex: none; margin: 0; padding-top: 14px; }
/* What the card mounts beside its bands (a results sheet, the graded face, a note) sits at the same inset. */
able-test .atst-card > :not(.atst-hd):not(.atst-lead):not(.atst-steprow):not(.atst-body):not(.atst-actions):not(.atst-live) { margin-left: var(--atst-inset); margin-right: var(--atst-inset); }
able-test .atst-card > .atst-results { margin-top: 4px; margin-bottom: 16px; }
able-test .atst-card > .atst-face { margin-bottom: 4px; }
able-test .atst-card > .atst-hint:last-child, able-test .atst-card > .atst-note:last-child, able-test .atst-card > .atst-error:last-child { margin-bottom: 16px; }
@container (max-width: 479.98px) {
  /* A container query never matches the container itself: the inset for a narrow card is set on the card, whose bands inherit it. */
  able-test .atst-card { --atst-inset: 16px; }
  able-test .atst-lead { font-size: 14.5px; }
  able-test .atst-btn-l { display: none; }
  able-test .atst-btn-s { display: inline; }
  able-test .atst-actions { gap: 10px; }
  /* A graded row's four doors fit one row without their icons; they — and the results doors — take a second row before any label ellipsizes. */
  able-test .atst-actions[data-graded], able-test .atst-actions[data-results] { flex-wrap: wrap; row-gap: 8px; }
  able-test .atst-actions[data-graded] .atst-btn .ic, able-test .atst-actions[data-graded] .atst-btn .atst-ic { display: none; }
  able-test .atst-actions[data-graded] .atst-btn:not(.atst-btn--quiet) { padding-left: 12px; padding-right: 12px; }
  able-test .atst-actions .atst-btn--quiet { padding: 0 4px; }
  able-test .atst-actions .atst-btn--quiet .ic, able-test .atst-actions .atst-btn--quiet .atst-ic { display: none; }
  able-test .atst-hd[data-phase="item"] .atst-hd-skill { max-width: 34%; }
  able-test .atst-body { padding-top: 12px; gap: 10px; }
  able-test .atst-stem { font-size: 16px; }
  able-test .atst-stem-ic { width: 22px; height: 22px; font-size: 12px; }
  able-test .atst-opt { font-size: 14.5px; padding: 9px 10px 9px 8px; }
  able-test .atst-keyhint, able-test .atst-hint--keys { display: none; }
}
@container (min-width: 480px) {
  able-test .atst-hd { flex-wrap: nowrap; }
  able-test .atst-hd-sp { display: none; }
  able-test .atst-hd-title { order: 2; flex: 1 1 auto; display: block; white-space: nowrap; text-overflow: ellipsis; }
  able-test .atst-hd-skill { max-width: 32%; }
  able-test .atst-qc, able-test .atst-opts--tf { grid-template-columns: 1fr 1fr; }
  able-test .atst-res-top { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  able-test .atst-res-skills .atst-h { margin-top: 10px; padding-top: 0; border-top: 0; }
}
@container (min-width: 720px) {
  able-test .atst-opts--cols { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (pointer: coarse) {
  /* Touch floors: a 44-px hit area on every control a finger lands on — the pills through their
     hit inset, the footer's doors and the chips through their box. */
  able-test .atst-steps { gap: 6px; }
  able-test .atst-step { flex-basis: 36px; min-width: 36px; height: 36px; }
  able-test .atst-step::before { inset: -4px -3px; }
  able-test .atst-actions .atst-btn, able-test .atst-chips .atst-chip { min-height: 44px; }
  able-test .atst-actions { min-height: 44px; }
  able-test .atst-keyhint, able-test .atst-hint--keys { display: none; }
}
@media (hover: hover) and (pointer: fine) {
  able-test .atst-opt:not([data-locked]):hover { border-color: var(--accent, #1a5cff); background: var(--accent-soft, #f6f8ff); transform: translateY(-1px); }
  able-test .atst-btn:not(:disabled):hover { border-color: var(--border-strong, #9ca3af); }
  able-test .atst-btn--primary:not(:disabled):hover { background: var(--accent-hover, #0b46d6); border-color: transparent; }
  able-test .atst-btn--quiet:not(:disabled):hover { border-color: transparent; text-decoration: underline; }
  able-test button.atst-step:hover { filter: brightness(.95); }
}
@media (prefers-reduced-motion: no-preference) {
  able-test .atst-btn, able-test .atst-opt { transition: background-color 150ms ease, border-color 150ms ease, transform 150ms ease, box-shadow 150ms ease; }
  able-test .atst-opt-letter { transition: background-color 150ms ease, border-color 150ms ease, color 150ms ease; }
  able-test .atst-verdict { animation: atst-fade-up 250ms ease; }
  able-test .atst-verdict > .ic, able-test .atst-verdict > .atst-ic,
  able-test .atst-opt[data-v][data-checked] .atst-opt-letter { animation: atst-pop 350ms ease; }
  @keyframes atst-fade-up { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
  @keyframes atst-pop { 0% { transform: scale(.92); } 60% { transform: scale(1.04); } 100% { transform: scale(1); } }
}


/* able-time-grid */

/* ⚠ ACCENT-COLOURED TEXT USES --accent-text, NEVER --accent.
   Measured on the live build theme 2026-08-24: --accent is a FILL token and
   computes the SAME #2563eb in both modes, so accent text sits at 3.35:1 on the
   dark card ground — under AA. --accent-text re-binds (#2563eb / #A5B4FC) and
   measures 5.17 light / 8.68 dark. Because the TOKEN flips, no media query and no
   [data-theme] guard is needed here at all, and the tenant keeps its own colour
   instead of a hard-coded blue. Fills and borders keep --accent. */
able-time-grid {
  display: block;
  /* TONE is the contract's colour vocabulary (CALENDAR-REVAMP.md 2c): ten closed
     NAMES, resolved here to --cal-tone-<name>. The THEME owns every value — these
     are only the fallbacks so the grid is legible in a theme that has not declared
     them yet. A tenant re-tones its whole calendar by declaring --cal-tone-* once,
     and a native renderer maps the same ten names to its own palette. */
  --cal-tone-primary:  var(--cal-tone-primary-x,  var(--accent));
  --cal-tone-accent:   var(--cal-tone-accent-x,   var(--accent));
  --cal-tone-info:     var(--cal-tone-info-x,     var(--info));
  --cal-tone-warn:     var(--cal-tone-warn-x,     var(--warning));
  --cal-tone-critical: var(--cal-tone-critical-x, var(--danger));
  --cal-tone-money:    var(--cal-tone-money-x,    var(--success));
  --cal-tone-travel:   var(--cal-tone-travel-x,   var(--info));
  --cal-tone-muted:    var(--cal-tone-muted-x,    var(--text-muted));
  --cal-tone-social:   var(--cal-tone-social-x,   var(--success));
  --cal-tone-neutral:  var(--cal-tone-neutral-x,  var(--text-muted));

  --atg-hour-h: 56px;
  /* W2 — a compressed gutter under 900px; the hours still read, and the columns
     get the width back where it matters. */
  --atg-rail-w: 44px;
  --atg-col-min: 120px;
}
@media (min-width: 900px) {
  able-time-grid { --atg-rail-w: 56px; }
}
able-time-grid .atg {
  display: flex;
  flex-direction: column;
  min-height: 0;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-lg, 10px);
  background: var(--surface-card);
  overflow: hidden;
}

/* ── THE ONE SCROLL BOX, both axes ────────────────────────────────────────────
      A day is taller than any sensible viewport and a week is wider than 320px,
      so this box owns both. The PAGE body never scrolls sideways — that is the
      mobile rule; a deliberately pannable inner container is how a week stays a
      week instead of being crushed into unreadable 38px columns.
      The hour rail freezes to the left and the column heads freeze to the top,
      so you always know which day and which hour you are looking at. ────────── */
able-time-grid .atg-scroll {
  overflow: auto;
  /* W4 — the canvas must be BOUNDED, not merely tall: on an auto-height page host
     an unbounded canvas grows to its full 24-hour content, the PAGE scrolls
     instead of the grid, and the open-on-the-morning scroll silently becomes a
     no-op (scrollTop on an already-full-height container does nothing). */
  max-height: var(--atg-max-h, clamp(300px, calc(100dvh - 330px), 1000px));
  /* …and never run under the shell's launcher FAB, which is fixed over the
     bottom-right of every page. --able-claw-safe-bottom is published by
     able-claw.js and tracks the pill's own offsets, so a shell that moves the
     FAB moves this with it. Absent (a page with no claw) it costs nothing. */
  margin-bottom: var(--able-claw-safe-bottom, 0px);
  -webkit-overflow-scrolling: touch;
}
able-time-grid .atg-inner {
  min-width: 100%;
  display: grid;
  grid-template-columns: var(--atg-rail-w) 1fr;
}

/* ── column heads ─────────────────────────────────────────────────────────── */
/* the corner: frozen on BOTH axes, so it must outrank both rails */
able-time-grid .atg-head-pad {
  border-right: 1px solid var(--border-subtle);
  border-bottom: 1px solid var(--border-default);
  background: var(--surface-card);
  position: sticky; left: 0; top: 0; z-index: 6;
}
able-time-grid .atg-heads {
  display: grid;
  border-bottom: 1px solid var(--border-default);
  background: var(--surface-card);
  position: sticky; top: 0; z-index: 5;
}
able-time-grid .atg-head {
  padding: 6px 4px 5px;
  text-align: center;
  border-left: 1px solid var(--border-subtle);
  min-width: 0;
}
able-time-grid .atg-head:first-child { border-left: 0; }
able-time-grid .atg-head-wd {
  font-size: 10px; font-weight: 700; letter-spacing: .08em;
  text-transform: uppercase; color: var(--text-muted);
}
able-time-grid .atg-head-num {
  font-size: 15px; line-height: 1.2; font-variant-numeric: tabular-nums;
  color: var(--text-primary);
}
able-time-grid .atg-head[data-today="true"] .atg-head-wd,
able-time-grid .atg-head[data-today="true"] .atg-head-num { color: var(--accent-text, var(--accent)); font-weight: 800; }

/* ── the MARKER rail — all-day + due-at-an-instant. Reserved even when empty so
      blocks never jump when a deadline lands after hydration. ─────────────── */
able-time-grid .atg-rail-pad {
  border-right: 1px solid var(--border-subtle);
  border-bottom: 1px solid var(--border-default);
  max-height: var(--atg-rail-max-h, 96px);
  background: var(--surface-card);
  /* W3 — the rail freezes with the heads. Scrolling to the afternoon must not
     take the day names and the deadlines off screen; that is the whole point of
     a 7-column view. --atg-head-h is measured after layout (see _freezeRail). */
  position: sticky; left: 0; top: var(--atg-head-h, 0px); z-index: 4;
  display: flex; align-items: center; justify-content: flex-end;
  padding-right: 6px;
}
able-time-grid .atg-rail-pad span {
  font-size: 9px; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--text-subtle);
}
able-time-grid .atg-rail {
  display: grid;
  border-bottom: 1px solid var(--border-default);
  background: var(--surface-subtle);
  position: sticky; top: var(--atg-head-h, 0px); z-index: 3;
  min-height: var(--atg-rail-min-h, 26px);
  /* CAPPED, and it scrolls past the cap. The rail's height is set by the BUSIEST
     day in view, so one Wednesday with four deadlines would otherwise push the
     actual hour grid off a 680px phone — the markers would crowd out the thing
     they are context for. Four chips fit; the rest scroll. */
  max-height: var(--atg-rail-max-h, 96px);
  overflow-y: auto;
}
able-time-grid .atg-rail-col {
  border-left: 1px solid var(--border-subtle);
  padding: 3px;
  display: flex; flex-direction: column; gap: 2px;
  min-width: 0;
}
able-time-grid .atg-rail-col:first-child { border-left: 0; }

able-time-grid .atg-chip {
  display: flex; align-items: center; gap: 4px;
  width: 100%;
  border: 0; border-left: 3px solid var(--atg-c, var(--text-muted));
  border-radius: var(--radius-sm, 4px);
  background: var(--surface-card);
  padding: 2px 4px;
  font-family: inherit; font-size: 11px; line-height: 1.25;
  color: var(--text-primary);
  text-align: left; cursor: pointer;
  min-width: 0;
}
able-time-grid .atg-chip-t {
  font-variant-numeric: tabular-nums; color: var(--text-muted);
  flex: 0 0 auto; font-size: 10px;
}
able-time-grid .atg-chip-n {
  flex: 1 1 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ── the hour rail + the body ─────────────────────────────────────────────── */
able-time-grid .atg-hours {
  border-right: 1px solid var(--border-subtle);
  background: var(--surface-card);
  position: sticky; left: 0; z-index: 2;
}
able-time-grid .atg-hour {
  height: var(--atg-hour-h);
  position: relative;
}
able-time-grid .atg-hour span {
  position: absolute; top: -6px; right: 6px;
  font-size: 10px; font-variant-numeric: tabular-nums;
  color: var(--text-muted);
}
/* The first label has no gridline above it to straddle, so it sits INSIDE its row
   instead of over the line. Deliberately NOT "fixed" by padding the rail: the hour
   rail and the block canvas must stay pixel-aligned with each other, and nudging
   one without the other is how a 9:00 class ends up drawn at 9:06. */
able-time-grid .atg-hour:first-child span { top: 1px; }

able-time-grid .atg-cols {
  display: grid;
  position: relative;
}
able-time-grid .atg-col {
  position: relative;
  border-left: 1px solid var(--border-subtle);
  min-width: 0;
}
able-time-grid .atg-col:first-child { border-left: 0; }
able-time-grid .atg-col[data-today="true"] { background: var(--surface-hover); }
able-time-grid .atg-line {
  position: absolute; left: 0; right: 0;
  border-top: 1px solid var(--border-subtle);
  pointer-events: none;
}
able-time-grid .atg-line[data-half="true"] { border-top-style: dotted; opacity: .55; }

/* ── a timed block ────────────────────────────────────────────────────────── */
able-time-grid .atg-item {
  position: absolute;
  border: 0;
  border-left: 3px solid var(--atg-c, var(--accent));
  border-radius: var(--radius-sm, 4px);
  background: var(--surface-card);
  box-shadow: var(--shadow-sm, 0 1px 2px rgba(0,0,0,.06));
  padding: 2px 5px;
  overflow: hidden;
  font-family: inherit;
  text-align: left;
  color: var(--text-primary);
  cursor: pointer;
  display: flex; flex-direction: column; gap: 1px;
  min-width: 0;
}
able-time-grid .atg-item::before {
  /* the kind's colour as a wash, so the block reads as its kind at a glance
     without hard-coding an opaque fill that would fight the theme */
  content: ""; position: absolute; inset: 0;
  background: var(--atg-c, var(--accent)); opacity: .10; pointer-events: none;
}
able-time-grid .atg-item:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
able-time-grid .atg-item-n {
  font-size: 11px; font-weight: 600; line-height: 1.2;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  position: relative;
}
able-time-grid .atg-item-s {
  font-size: 10px; line-height: 1.2; color: var(--text-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  position: relative;
}
/* a short block cannot hold two lines — hide the subtitle instead of clipping it */
able-time-grid .atg-item[data-short="true"] .atg-item-s { display: none; }
able-time-grid .atg-item[data-tiny="true"] { padding: 0 4px; justify-content: center; }
able-time-grid .atg-item[data-tiny="true"] .atg-item-n { font-size: 10px; font-weight: 500; }

/* ── the now-line ─────────────────────────────────────────────────────────── */
able-time-grid .atg-now {
  position: absolute; left: 0; right: 0; height: 0;
  border-top: 2px solid var(--danger);
  z-index: 4; pointer-events: none;
}
able-time-grid .atg-now::before {
  content: ""; position: absolute; left: -3px; top: -4px;
  width: 7px; height: 7px; border-radius: 50%; background: var(--danger);
}

able-time-grid .atg-empty {
  padding: var(--space-6, 24px);
  text-align: center; color: var(--text-muted); font-size: 13px;
}

@media (prefers-reduced-motion: reduce) {
  able-time-grid .atg-scroll { scroll-behavior: auto; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   variant="week" — THE FITTED WEEK SURFACE
   ═══════════════════════════════════════════════════════════════════════════
   The default variant frames all 24 hours in a bounded scroll box and puts every
   marker in a rail above the grid. That is the right DAY shape, and it is the
   shape this tag shipped with. A WEEK asked four further questions, and four
   review rounds on a live household answered them:

     1. NO INTERNAL SCROLL. A week the reader must scroll is a week they cannot
        compare. 'fit' narrows the frame to the hours the data actually occupies
        and then sizes the hour so the whole span lands inside the box.
     2. A DEADLINE AT A REAL INSTANT IS A GRID FACT. "due 13:30" belongs at
        13:30, not in a rail — but it must never lie across another item's ink,
        so with pins="pack" it JOINS the column packing as a chip beside the
        block (the same treatment Google gives zero-length reminders).
     2b. …BUT 23:59 IS NOT AN INSTANT, IT IS A DATE. An LMS writes 23:59 to mean
        "due Wednesday", and 91% of this product's homework carries it (measured
        2026-08-24). Drawn as a clock fact it lands at the bottom edge AND drags
        the fitted frame to midnight, so one deadline turns a 12-hour week into
        16 hours of empty ruled paper and then hides itself under the fold. Those
        rows go to the DUE RAIL: a dense strip pinned below the scroller, where
        the end of the day already is and where no scroll position can lose it.
        The line between "instant" and "date" is the eod-due knob (23:50).
     3. MULTI-DAY THINGS SPAN. banners="on" renders extent-less markers as one
        bar across the days they cover, capped per day with a "+N" overflow.
     4. WHAT ALREADY HAPPENED STOPS COMPETING. past-fade quiets finished rows
        against the caller's clock — with an exemption list, because a passed
        deadline is OVERDUE, which is the opposite of quiet.

   Every one of those is DATA-driven and carries no tenant vocabulary. The colour
   axis is the clearest case: this file never learns what a "category" is. It
   stamps a caller-named attribute from a caller-named field and paints from a
   caller-named custom property (swatch-attr / swatch-field / swatch-var), so a
   theme that already maps its own values lights the grid up with no new CSS and
   no hex ever crosses the data boundary. ───────────────────────────────────── */
able-time-grid[variant="week"] {
  --atg-w-gut: 44px;
  /* the ink a block is drawn from: the caller's swatch var, then tone, then a
     visible grey — an unknown value must FAIL VISIBLE, never invisible */
  --atg-ink: var(--cal-tone-neutral);

  /* ── DENSITY: the type scale AND the geometry it implies, in ONE block ─────
     ⟪RULING 2026-08-24⟫ "max still complaining about the calendar font being
     too small, need to increase font size i think vertically we can have less
     hours showing so we can see larger cells."  READABILITY WINS; fewer hours
     on screen is the accepted price, and the reader scrolls for the rest —
     which is what the BASE variant has always done (see the scroll-to attr).

     The geometry tokens are here, beside the type they are derived FROM, on
     purpose: an hour floor that lives somewhere else drifts the first time
     someone bumps a font-size, and then a 30-minute card clips its own title
     again. Read them in this order —
       --atgw-title 14px, line-height 1.2  -> 16.8px of ink
       + 4px block padding                 -> 20.8px for ONE title line
       so a 30-min block (hourPx/2) needs   hourPx >= 41.6  -> floor 44
       two lines (title + meta 12px*1.2)    -> 35.2px       -> l2 earned at 36
       three lines                          -> 49.6px       -> l3 earned at 52 */
  /* How far ABOVE the frame top the week rests, so the frame's own hour label
     clears the fold. The label is centred on its rule (translateY(-50%)), so at
     an exact-gridline resting position it renders sliced in half — /day names
     this same defect in its scrollGrid() and pays the same kind of nudge for it.
     Derived, not picked: --atgw-hourlab 12px at line-height 1.2 is 14.4px of
     ink, half of which is 7.2px above the rule, +2px for rounding -> 9. It
     lives HERE, beside the label size it comes from, for the reason every other
     number in this block does. */
  --atgw-fold-clear: 9px;
  --atgw-title: 14px;
  --atgw-title-narrow: 12.5px;
  --atgw-title-min: 11.5px;
  /* A THIN block yields its padding and its time chip, not its type: at this
     scale thin means 21-31px, and 14px at line-height 1.2 is 16.8px of ink,
     which fits with padding-block:0. Shrinking here would have put the 30-minute
     card back at the size the ruling was about. Compact still shrinks — its thin
     starts at 16px, where 12.5px does not fit. */
  --atgw-title-thin: var(--atgw-title);
  --atgw-meta: 12px;
  --atgw-hourlab: 12px;
  --atgw-dow: 12px;
  --atgw-daynum: 17px;
  --atgw-pin: 12px;
  --atgw-pin-h: 20px;
  --atgw-banner: 13px;
  --atgw-banner-h: 24px;
  /* The due rail runs one notch tighter than the band: it is a NAME and nothing
     else (no time to print — that is the point), so it needs less line than a
     banner that may carry a range, and it is buying its height from the grid. */
  --atgw-due: 12.5px;
  --atgw-due-h: 22px;
  --atgw-chip: 9.5px;
  --atgw-chip-h: 16px;
  /* unitless — the JS reads these back out of the computed style, so the ONE
     place a density is authored is the block you are reading */
  --atgw-hour-floor: 44;
  --atgw-blk-min: 21;
  --atgw-thin-at: 32;
  --atgw-l2-at: 36;
  --atgw-l3-at: 52;
  /* WRAP-AT — a slim block wraps as soon as it can hold TWO lines of the size it
     actually renders at (the narrow rung, 12.5px x 1.15 = 14.4 each, + 4px
     padding = 32.8). It used to borrow data-tall (l2At+4 = 40) and a 50-minute
     class at 37px missed by 3px — so 'ECON 25200' rendered 'ECO...' inside a
     block with room for both words. The two thresholds answer different
     questions: tall = deep enough to spend height on a second FACT; wrap = deep
     enough to spend it on more of the FIRST one. */
  --atgw-wrap-at: 33;
}
/* COMPACT — the round-5 scale, kept expressible and regression-tested. For a
   surface that genuinely trades reading for span (an operator wallboard, a
   14-column range); NOT the default any more. */
able-time-grid[variant="week"][density="compact"] {
  --atgw-title: 12.5px;
  --atgw-title-narrow: 11px;
  --atgw-title-min: 10px;
  --atgw-title-thin: 11px;
  --atgw-meta: 10.5px;
  --atgw-hourlab: 11px;
  --atgw-dow: 11px;
  --atgw-daynum: 15px;
  --atgw-pin: 10.5px;
  --atgw-pin-h: 17px;
  --atgw-banner: 12px;
  --atgw-banner-h: 22px;
  --atgw-due: 11.5px;
  --atgw-due-h: 20px;
  --atgw-chip: 8.5px;
  --atgw-chip-h: 15px;
  --atgw-hour-floor: 24;
  --atgw-blk-min: 16;
  --atgw-thin-at: 24;
  --atgw-l2-at: 36;
  --atgw-l3-at: 54;
  --atgw-wrap-at: 28;
}
@media (min-width: 900px) {
  able-time-grid[variant="week"] { --atg-w-gut: 52px; }
}
/* flex: 1 1 auto — the wrapper must FILL a host that gives it a definite
   height, or the scroller below it sizes to its own content and the fit reads
   its own output as its input (see atgWeekBox). */
able-time-grid[variant="week"] .atg-w {
  display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0;
  border: 1px solid var(--border-default); border-radius: var(--radius-lg, 10px);
  background: var(--surface-card); overflow: hidden;
}
able-time-grid[variant="week"] .atg-w-head,
able-time-grid[variant="week"] .atg-w-band {
  display: grid; grid-template-columns: var(--atg-w-gut) repeat(var(--atg-w-n, 7), minmax(0, 1fr));
  flex: none;
  /* THE HEAD LAYS OUT OVER THE SAME WIDTH THE CANVAS DOES, and this pad is the
     whole of how. The head and the band are siblings of ".atg-w-scroll", not
     children of it, so they span the card while the canvas spans the scroller's
     CONTENT box — narrower by whatever the scrollbar takes. Seven "1fr" columns
     divided over two different widths cannot line up: each column comes out
     ~1.57px narrow and the error ACCUMULATES left-to-right, so Monday is right
     and Sunday is a third of a column out. Measured on the live pages at 1440
     with a classic bar: 9.44px at the last column's left edge on ableU, 9.42 on
     fryaway, 11px at its right edge on both — one number, both tenants, because
     this is the blueprint's geometry and not a tenant's CSS.

     ⚠ It is MEASURED (--atg-w-sb, published by _wkPublishChrome), never the
     10px this file asks for. The width a scrollbar actually takes is a platform
     answer: 0 on macOS overlay and on every touch device, 0 whenever the week
     does not overflow, ~10 with a classic bar, ~15-17 on Windows. A constant
     would be right on one machine and would MINT the same drift, mirrored, on
     every other. At 0 this declaration is inert and the output is byte-identical
     to before it existed — which is the property that lets it ship to a tenant
     that never reported the bug.

     The pad, not a width on the scroller: padding is inside the background and
     the border-bottom, so the head's rule and the band's fill still reach the
     card edge. Nothing about the affordance moves — ".atg-w-scroll" keeps its
     classic bar (§2c-i, "a day that scrolls must look like one"); this stops
     that bar from being paid for by the headers. */
  padding-right: var(--atg-w-sb, 0px);
}
able-time-grid[variant="week"] .atg-w-head { border-bottom: 1px solid var(--border-subtle); }
able-time-grid[variant="week"] .atg-w-headpad { position: relative; }
able-time-grid[variant="week"] .atg-w-tz {
  position: absolute; right: 5px; bottom: 5px;
  font-size: 10px; font-weight: 700; letter-spacing: .02em;
  color: var(--text-muted); text-transform: uppercase;
}
/* The day head is the DAY-PEEK trigger (it emits day-select), so it is a real
   button with a real hit area — the whole surface Raul opens the day from. */
/* DOW + number ride ONE line ("MON 31"), baseline-aligned — the two-line stack
   spent vertical room the head does not have to give (Raul 2026-08-29). The
   number keeps the today disc; the dow is the small-caps lead beside it. */
able-time-grid[variant="week"] .atg-w-day {
  appearance: none; border: 0; background: transparent; font: inherit;
  display: flex; flex-direction: row; align-items: baseline; justify-content: center;
  gap: 5px; min-height: 34px; padding: 5px 2px;
  border-left: 1px solid var(--border-subtle);
  cursor: pointer;
}
able-time-grid[variant="week"] .atg-w-day-dow {
  font-size: var(--atgw-dow); font-weight: 800; letter-spacing: .06em; text-transform: uppercase;
  color: var(--text-muted); line-height: 1;
}
able-time-grid[variant="week"] .atg-w-day-num {
  font-size: var(--atgw-daynum); font-weight: 700; line-height: 1;
  color: var(--text-primary); font-variant-numeric: tabular-nums;
}
/* The today disc rides the number as a pill — inline, so the baseline the dow
   aligns to is the number's own, never a centred box's bottom edge. */
able-time-grid[variant="week"] .atg-w-day[data-today] .atg-w-day-num {
  background: var(--accent); color: var(--on-accent); font-weight: 800;
  border-radius: var(--radius-full); padding: 2px 7px; margin: -2px 0;
}
able-time-grid[variant="week"] .atg-w-day[data-today] .atg-w-day-dow { color: var(--accent-text); }
able-time-grid[variant="week"] .atg-w-day[data-sel]:not([data-today]) {
  background: var(--surface-subtle); box-shadow: inset 0 -2px 0 0 var(--border-strong);
}
@media (hover: hover) and (pointer: fine) {
  able-time-grid[variant="week"] .atg-w-day:hover { background: var(--surface-hover); }
}
able-time-grid[variant="week"] .atg-w-day:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* ── THE TOP BAND: ONE header region above the hours ──────────────────────────
   It holds everything the timetable cannot place on a clock — all-day markers,
   and (when the tenant opts in with "dues") that day's deadlines beneath them.
   ONE region, ONE scroller, ONE cap: see the ".atg-w-bandhost" note below. */
able-time-grid[variant="week"] .atg-w-band {
  border-bottom: 1px solid var(--border-default); background: var(--surface-subtle);
}
/* ⚠ THE SCROLL REGION IS GATED ON [data-dues], NOT ON THE BAND — and that gate
   is the whole of this change's blast-radius control.
   "overflow-y" is not a cosmetic property: it opens a block formatting context
   and turns the element into a containing block for sticky descendants. Applied
   to every band it would have re-shaped a tenant that never asked for deadlines
   (measured on fryaway's live /calendar: overflow-y visible -> auto, and a third
   child in a band that had exactly two). A band that holds deadlines is a scroll
   region; a band that does not is the block it has always been.
   The tenant still owns the HEIGHT — a max-height beside this, or none, in which
   case the band grows with its content and this never engages. */
able-time-grid[variant="week"] .atg-w-band[data-dues] {
  /* THE WHOLE BAND SCROLLS, label column included — and that is the difference
     from the bottom rail this replaced. There the label was one word for one
     population, so scrolling it away would have been a loss. Here the labels are
     ROW-GROUP HEADERS ("All-day" over the lanes, "Due" over the deadlines): a
     legend that stays put while its rows move is a legend that starts lying on
     the first scroll. Both columns ride the same scrollport, so a row and its
     header cannot disagree. */
  overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain;
  position: relative;
  /* THE BAND STYLES ITS SCROLLBAR NOT AT ALL, and that is MEASURED, not lazy —
     it is the one thing ".atg-w-scroll" does that this must not copy.

     Styling the webkit pseudo-elements opts an element OUT of macOS overlay
     scrollbars, so the bar takes REAL WIDTH. The hour scroller accepts that
     trade (§2c-i: a day that scrolls must look like one) and pays ~10px for it
     — measured live, and it is why its canvas columns already sit up to 9.4px
     left of the day headers above them. Under the headers that drift would be
     unmissable, and the band is the region directly under them.

     So: no ::-webkit-scrollbar, and no "scrollbar-width" either — the second is
     probably harmless, but "probably" is not a reason to hand this element a
     way to become classic on a platform nobody measured. Left alone it is an
     overlay bar on macOS (zero width, columns aligned by construction) that
     still appears under the pointer, which is the affordance asked for. The
     resting cue is the scrim below, which costs no layout anywhere. */
}
able-time-grid[variant="week"] .atg-w-band[hidden] { display: none; }
/* "THE BAND HAS MORE UNDER IT" — the §2c-i cue, applied to the band.
   A scroll container whose overflow is invisible is indistinguishable from a
   wall, and on macOS the overlay bar is gone at rest. A zero-height sticky
   sibling pinned to the scrollport's bottom edge paints a short scrim from the
   text colour, so it reads as the band passing UNDER the hours whether or not
   the hidden row carries ink — and it flips with the theme by construction.
   position:sticky (not a ::after on the scroller) for the same reason it is
   sticky on ".atg-w-scroll": a pseudo-element on a scroll container is
   positioned against the scrollable area and scrolls away with the content. */
able-time-grid[variant="week"] .atg-w-bandedge {
  position: sticky; bottom: 0; height: 0; z-index: 4; pointer-events: none;
  grid-column: 1 / -1;
}
able-time-grid[variant="week"] .atg-w-bandedge::after {
  content: ''; position: absolute; left: var(--atg-w-gut); right: 0; bottom: 0; height: 7px;
  opacity: 0; transition: opacity 120ms linear;
  background: linear-gradient(to top,
    color-mix(in srgb, var(--text-primary) 12%, transparent) 0%, transparent 100%);
}
able-time-grid[variant="week"] .atg-w-band[data-dues][data-more-down] .atg-w-bandedge::after { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  able-time-grid[variant="week"] .atg-w-bandedge::after { transition: none; }
}
able-time-grid[variant="week"] .atg-w-bandpad {
  display: flex; align-items: center; justify-content: flex-end;
  padding: var(--space-1) 5px;
  font-size: 9.5px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: var(--text-subtle); text-align: right; line-height: 1.1;
}
/* WITH DUES PRESENT the pad stops being one centred word and becomes a legend
   grid, row-matched to the host by an inline "grid-template-rows" the renderer
   writes on BOTH. Each label is placed at the first row of the group it names
   and spans it, so "Due" sits beside the first deadline rather than floating in
   the middle of a band whose two halves mean different things. Grid, not flex,
   because only a shared row template can guarantee the match at any lane count. */
able-time-grid[variant="week"] .atg-w-bandpad[data-groups] {
  display: grid; align-items: start; justify-items: end;
  padding: 3px 5px; gap: 2px;
}
able-time-grid[variant="week"] .atg-w-bandpad-l {
  display: block; align-self: center; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  /* READABLE, not decorative — and this is the one place the legend differs from
     the single word it replaced. The pad ships "--text-subtle", which measured
     2.27:1 against the band at 9.5px (audit LOW_CONTRAST, iPad, 2026-08-25);
     "--text-muted" was 4.32 and still short of the 4.5 floor. A label small
     enough to be decoration is still a label, and two of them now carry the only
     statement of which rows are which.
     Scoped to the legend SPAN, so a pad with no groups keeps the colour it has
     always had — this is a fix for the element this change introduced, not a
     re-tone of every band in the fleet. */
  color: var(--text-secondary);
}
able-time-grid[variant="week"] .atg-w-bandhost {
  grid-column: 2 / -1;
  display: grid; grid-template-columns: repeat(var(--atg-w-n, 7), minmax(0, 1fr));
  grid-auto-rows: var(--atgw-banner-h); gap: 2px; padding: 3px; min-width: 0;
}
able-time-grid[variant="week"] .atg-w-banner {
  appearance: none; border: 0; font: inherit;
  display: flex; align-items: center; gap: 5px;
  min-width: 0; margin: 0 1px; padding: 0 7px 0 5px;
  border-radius: 4px;
  border-left: 3px solid var(--atg-ink);
  background: color-mix(in srgb, var(--atg-ink) 16%, var(--surface-card));
  color: var(--text-primary);
  font-size: var(--atgw-banner); font-weight: 700;
  line-height: var(--atgw-banner-h); text-align: left;
  overflow: hidden; cursor: pointer;
}
able-time-grid[variant="week"] .atg-w-banner-t {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
able-time-grid[variant="week"] .atg-w-banner[data-done] { opacity: .6; }
able-time-grid[variant="week"] .atg-w-banner[data-done] .atg-w-banner-t { text-decoration: line-through; }
/* A COUNTED bar — a fold chip or a coalesced group. It reads as a control that
   opens rather than as one thing's title, so it takes the quieter surface and
   sizes to its text instead of filling the column: "3 chores" pinned to the left
   edge of Saturday says the same thing at a third of the ink, and leaves the
   column visibly free rather than looking like a bar someone forgot to name. */
able-time-grid[variant="week"] .atg-w-banner[data-count] {
  justify-self: start; max-width: 100%;
  font-weight: 800; letter-spacing: .01em;
  background: color-mix(in srgb, var(--atg-ink) 10%, var(--surface-card));
}
@media (hover: hover) and (pointer: fine) {
  able-time-grid[variant="week"] .atg-w-banner:hover {
    background: color-mix(in srgb, var(--atg-ink) 26%, var(--surface-card));
  }
}
able-time-grid[variant="week"] .atg-w-banner:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* Shares the last lane's cell with whatever banner is there: pinned to the
   RIGHT edge and given the surface behind it, so it reads as a control rather
   than as text lying across a title. justify-self:end (not start) is what
   keeps it off the title's first words, which is the side you read. */
able-time-grid[variant="week"] .atg-w-more {
  appearance: none; border: 0; font: inherit;
  margin: 0 2px; padding: 0 5px; border-radius: 4px;
  background: var(--surface-hover); color: var(--text-secondary);
  font-size: var(--atgw-meta); font-weight: 800;
  line-height: var(--atgw-banner-h); text-align: center;
  cursor: pointer; justify-self: end; align-self: center; z-index: 2;
  box-shadow: 0 0 0 2px var(--surface-subtle);
}
@media (hover: hover) and (pointer: fine) {
  able-time-grid[variant="week"] .atg-w-more:hover { background: var(--surface-hover); color: var(--text-primary); }
}
able-time-grid[variant="week"] .atg-w-more:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* ── DEADLINES, IN THE BAND ────────────────────────────────────────────────────
   A due chip is a band row like any other: it is placed by the renderer into the
   SAME ".atg-w-bandhost" grid as the all-day bars, in the rows after the last
   lane. What separates the two populations is TREATMENT, not position — the chip
   keeps its own row height ("--atgw-due-h", set on the shared row template) and
   its own quieter fill, so "Wednesday is all-day busy" and "Wednesday has four
   things owed" stay legible as different facts inside one region.

   It used to be a bottom rail ("flex:none" after ".atg-w-scroll"). Two things
   were wrong with that. A deadline is a fact about the DAY, and the day's column
   is at the top where the reader already looks — the bottom edge won an argument
   about 23:59 that was never about where the eye goes. And two independent
   regions each sized by their own worst day stack their maxima: the band's lanes
   were unbounded while the rail was capped, so a heavy all-day week charged the
   timetable twice. One region takes one budget. */
/* FILLED BAR vs OUTLINED CHIP — the whole visual argument, in one declaration.
   In the bottom rail the two populations were told apart by REGION, so they could
   afford the identical "16% ink" fill they both had. Sharing a region withdraws
   that, and the difference has to move into the ink: an all-day bar stays FILLED
   (it occupies its span), a deadline is an OUTLINED chip on the card ground (it
   marks a point). Same hue, same left edge, same type marks — only the weight of
   the surface changes, which is what keeps a course's colour reading as one
   course across both rows instead of as two unrelated things. */
able-time-grid[variant="week"] .atg-w-due {
  appearance: none; border: 0; font: inherit;
  display: flex; align-items: center; gap: 5px;
  min-width: 0; margin: 0 1px; padding: 0 6px 0 5px;
  border-radius: 4px;
  border-left: 3px solid var(--atg-ink);
  background: var(--surface-card);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--atg-ink) 34%, transparent);
  color: var(--text-primary);
  font-size: var(--atgw-due); font-weight: 700;
  line-height: var(--atgw-due-h); text-align: left;
  overflow: hidden; cursor: pointer;
}
able-time-grid[variant="week"] .atg-w-due-t {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* The clock, when there is a real one, leads — it is the sort key the reader is
   scanning. flex:none so the TITLE is what yields under pressure, never the time. */
able-time-grid[variant="week"] .atg-w-due-tm {
  flex: none; font-size: var(--atgw-meta); font-weight: 600; color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
}
able-time-grid[variant="week"] .atg-w-due[data-done] { opacity: .55; }
able-time-grid[variant="week"] .atg-w-due[data-done] .atg-w-due-t { text-decoration: line-through; }
able-time-grid[variant="week"] .atg-w-due[data-past]:not([data-done]) { opacity: .72; }
/* Hover TINTS, it does not FILL — 26% ink (what the filled bars use) would make
   the chip change population under the pointer, which is the one thing hover
   must never do. A light wash plus a firmer outline reads as "this is live"
   while the outlined identity survives. */
@media (hover: hover) and (pointer: fine) {
  able-time-grid[variant="week"] .atg-w-due:hover {
    background: color-mix(in srgb, var(--atg-ink) 10%, var(--surface-card));
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--atg-ink) 55%, transparent);
  }
}
able-time-grid[variant="week"] .atg-w-due:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* The overflow CELL: chip keeps the slack, the count is intrinsic and never
   yields — a "+3" that ellipsises to "+" is worse than no marker at all. */
able-time-grid[variant="week"] .atg-w-duecell {
  display: flex; align-items: center; gap: 2px; min-width: 0;
}
able-time-grid[variant="week"] .atg-w-duecell .atg-w-due { flex: 1 1 auto; min-width: 0; }
able-time-grid[variant="week"] .atg-w-duemore {
  appearance: none; border: 0; font: inherit; flex: none;
  margin: 0 1px; padding: 0 5px; border-radius: 4px;
  background: var(--surface-hover); color: var(--text-secondary);
  font-size: var(--atgw-meta); font-weight: 800;
  line-height: var(--atgw-due-h); text-align: center;
  cursor: pointer; justify-self: start;
}
@media (hover: hover) and (pointer: fine) {
  able-time-grid[variant="week"] .atg-w-duemore:hover { background: var(--surface-hover); color: var(--text-primary); }
}
able-time-grid[variant="week"] .atg-w-duemore:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* ── band="stack" — the unified per-day stack (opt-in) ─────────────────────────
   One flex cell per day under the span lanes; happening → owed → landed in one
   vertical flow with no group gap. The scroll region + scrim ride the same
   [data-dues] gate the due rail uses — one budget for one region. */
able-time-grid[variant="week"] .atg-w-band[data-stack] {
  /* COLOURED, not grey (Raul, carried from the page this band replaced): an
     accent wash sets the top section apart from the white hour grid — and the
     deeper the wash, the more the day dividers and the white cards read
     against it. Both modes via tokens; dark leans deeper. */
  background: color-mix(in srgb, var(--accent) 10%, var(--surface-base));
}
:root[data-theme="dark"] able-time-grid[variant="week"] .atg-w-band[data-stack] {
  background: color-mix(in srgb, var(--accent) 20%, var(--surface-base));
}
able-time-grid[variant="week"] .atg-w-band[data-stack] .atg-w-bandhost {
  grid-auto-rows: auto;
  align-items: stretch;
}
able-time-grid[variant="week"] .atg-w-stackcell {
  display: flex; flex-direction: column; gap: 2px;
  min-width: 0; padding: 3px 4px;
  /* the day divider (Raul: "make the column separators more visible"): a
     full-strength rule running the band's full height — every day boundary
     carries one; the gutter column is the only cell without. */
  border-left: 1px solid var(--border-strong, var(--border-default));
}
able-time-grid[variant="week"] .atg-w-stackcell .atg-w-banner,
able-time-grid[variant="week"] .atg-w-stackcell .atg-w-due {
  width: 100%; min-width: 0;
}
able-time-grid[variant="week"] .atg-w-stackcell .atg-w-duemore { align-self: flex-start; }
/* The receipt card: past tense by construction — a quiet ink wash, never the
   past-fade (fading what already happened would read as expiry). */
able-time-grid[variant="week"] .atg-w-landed {
  appearance: none; border: 0; font: inherit;
  display: flex; align-items: center; gap: 4px;
  min-width: 0; min-height: var(--atgw-due-h);
  padding: 1px 5px; border-radius: 4px;
  background: color-mix(in srgb, var(--atg-ink, var(--text-muted)) 8%, transparent);
  color: var(--text-muted); font-size: var(--atgw-meta); line-height: 1.25;
  text-align: left; cursor: pointer;
}
able-time-grid[variant="week"] .atg-w-landed-gl { flex: none; opacity: .85; }
able-time-grid[variant="week"] .atg-w-landed-t {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
able-time-grid[variant="week"] .atg-w-landed-amt {
  margin-left: auto; flex: none; padding-left: 4px;
  font-variant-numeric: tabular-nums; font-weight: 700; color: var(--text-secondary);
}
@media (hover: hover) and (pointer: fine) {
  able-time-grid[variant="week"] .atg-w-landed:hover {
    background: color-mix(in srgb, var(--atg-ink, var(--text-muted)) 14%, transparent);
    color: var(--text-secondary);
  }
}
able-time-grid[variant="week"] .atg-w-landed:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* ── the canvas ───────────────────────────────────────────────────────────── */
able-time-grid[variant="week"] .atg-w-scroll {
  overflow-y: auto; overflow-x: hidden; min-height: 0; flex: 1 1 auto;
  /* The ceiling is measured, not guessed: --atg-w-scroll-top is this element's
     own distance from the viewport top (published by _wkPublishChrome after
     layout), so the default is right for every band height and every page —
     a quiet week's short band hands its pixels to the hours instead of leaving
     a dead strip under the grid. --atg-w-below is the breathing room kept under
     the card. A page that stacks content below the calendar overrides
     --atg-w-max-h wholesale. */
  max-height: var(--atg-w-max-h, max(300px, calc(100dvh - var(--atg-w-scroll-top, 210px) - var(--atg-w-below, 12px))));
  overscroll-behavior: contain;
  /* A CLASSIC scrollbar, deliberately, and this is the whole affordance.
     macOS ships OVERLAY scrollbars: the thumb fades out at rest, so a grid that
     scrolls looks exactly like a grid that does not, and the reader is left to
     GUESS that a gesture would do something. Styling ::-webkit-scrollbar opts
     this one scroller out of overlay mode, so the thumb is drawn at rest — its
     position states where in the day you are and its length states how much of
     the day you are seeing. Cost: 10px of the grid's width (~1.4px per column
     at 1440), and no page-level reflow, because overflow-x is hidden. */
  scrollbar-width: thin;
  scrollbar-color: var(--border-strong) transparent;
}
able-time-grid[variant="week"] .atg-w-scroll::-webkit-scrollbar { width: 10px; }
able-time-grid[variant="week"] .atg-w-scroll::-webkit-scrollbar-track { background: transparent; }
able-time-grid[variant="week"] .atg-w-scroll::-webkit-scrollbar-thumb {
  background: var(--border-strong); border-radius: 5px;
  border: 3px solid var(--surface-card); background-clip: padding-box;
}
able-time-grid[variant="week"] .atg-w-scroll::-webkit-scrollbar-thumb:hover { background: var(--text-muted); }
/* ── the edge cue — "the day continues under here" ─────────────────────────
   position: sticky with height 0, NOT a pseudo-element on the scroller: a
   ::before on a scroll container is positioned against the scrollable overflow
   area and therefore SCROLLS AWAY with the content, which is the one thing an
   edge cue must never do. Two zero-height sticky siblings pin to the scrollport
   edges instead and paint a short fade from the card ground.
   It starts AFTER the gutter (left: var(--atg-w-gut)) so the hour labels at
   the edge stay crisp — fading the clock is how you make a grid look broken.
   pointer-events:none: this states, it never intercepts. */
able-time-grid[variant="week"] .atg-w-edge {
  position: sticky; height: 0; z-index: 5; pointer-events: none;
}
able-time-grid[variant="week"] .atg-w-edge--up { top: 0; }
able-time-grid[variant="week"] .atg-w-edge--dn { bottom: 0; }
able-time-grid[variant="week"] .atg-w-edge::after {
  /* WHISPER-QUIET (Raul: "the bottom shadow is a bit too large"): the cue only
     says "the day continues past this edge" — 7px at 12% ink reads at a glance
     without drawing a bar. The up cue is on at rest (hours before the window
     always exist), so its weight is a permanent part of the frame. */
  content: ''; position: absolute; left: var(--atg-w-gut); right: 0; height: 7px;
  opacity: 0; transition: opacity 120ms linear;
}
/* A SCRIM, not a fade to the card colour — measured, and this is the whole
   point of the cue. A white-to-transparent fade only says anything where there
   is ink under the edge, and on a quiet week the hours just past the frame are
   EMPTY: the reader is told nothing precisely in the case they asked about
   ("what is after 7pm?"). A dark scrim keyed to the text colour reads as the
   grid passing UNDER the edge whether the row below carries a card or nothing,
   and it flips with the theme by construction — one declaration, no dark-mode
   twin to drift. */
able-time-grid[variant="week"] .atg-w-edge--up::after {
  top: 0;
  background: linear-gradient(to bottom,
    color-mix(in srgb, var(--text-primary) 12%, transparent) 0%, transparent 100%);
}
able-time-grid[variant="week"] .atg-w-edge--dn::after {
  bottom: 0;
  background: linear-gradient(to top,
    color-mix(in srgb, var(--text-primary) 12%, transparent) 0%, transparent 100%);
}
able-time-grid[variant="week"] .atg-w-scroll[data-more-up] .atg-w-edge--up::after,
able-time-grid[variant="week"] .atg-w-scroll[data-more-down] .atg-w-edge--dn::after { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  able-time-grid[variant="week"] .atg-w-edge::after { transition: none; }
}
able-time-grid[variant="week"] .atg-w-canvas {
  display: grid; grid-template-columns: var(--atg-w-gut) 1fr; position: relative;
}
able-time-grid[variant="week"] .atg-w-gutter { position: relative; }
able-time-grid[variant="week"] .atg-w-hr { position: relative; }
/* translateY(-50%), not a magic top offset: the label must sit ON its gridline
   at ANY font size, and -6px was tuned for an 11px label — at 12px it measured
   3px low on every row. A transform centres it by construction, so the next
   density change cannot un-centre it. */
able-time-grid[variant="week"] .atg-w-hrlab {
  position: absolute; top: 0; right: 7px; transform: translateY(-50%);
  font-size: var(--atgw-hourlab); color: var(--text-muted); font-variant-numeric: tabular-nums;
}
/* A clock the scrollport edge would cut in half is hidden instead: half a number
   reads as a rendering fault, and the row above/below still states the hour. Set
   by _wkEdges from arithmetic, on every scroll.
   ⚠ data-atfold, NOT data-fold — the band already stamps data-fold on a COUNTED
   BAR to mean "this is a folded group" (see 4a). Same tag, opposite subject; a
   bare [data-fold] selector would have caught both. */
able-time-grid[variant="week"] .atg-w-hrlab[data-atfold] { visibility: hidden; }
able-time-grid[variant="week"] .atg-w-lanes {
  display: grid; grid-template-columns: repeat(var(--atg-w-n, 7), minmax(0, 1fr));
}
/* overflow:hidden — an item at the very bottom inflated to the min-height floor
   pokes past the canvas and re-grows the scroller by a few px: the sliver
   scrollbar. The column clips its own tail instead. */
able-time-grid[variant="week"] .atg-w-col {
  position: relative; border-left: 1px solid var(--border-subtle);
  min-width: 0; overflow: hidden;
}
able-time-grid[variant="week"] .atg-w-col[data-today] { background: var(--accent-soft); }
able-time-grid[variant="week"] .atg-w-col[data-rest]:not([data-today]) { background: var(--surface-subtle); }
able-time-grid[variant="week"] .atg-w-gl {
  position: absolute; left: 0; right: 0; height: 1px;
  background: var(--border-subtle); z-index: 0;
}
able-time-grid[variant="week"] .atg-w-off {
  position: absolute; left: 0; right: 0; z-index: 0; pointer-events: none;
  background: color-mix(in srgb, var(--text-primary) 3.5%, transparent);
}

/* ── the block: tinted fill + saturated edge + primary ink ─────────────────
   Legible for any hue in both modes by construction (color-mix toward the
   theme's own surfaces), so a tenant re-colours by declaring tokens, never by
   overriding this file. */
able-time-grid[variant="week"] .atg-w-blk {
  position: absolute;
  display: flex; flex-direction: column; gap: 0;
  overflow: hidden; margin-left: 2px;
  padding: 2px 5px 2px 6px;
  border-radius: 4px;
  border-left: 3px solid var(--atg-ink);
  background: color-mix(in srgb, var(--atg-ink) 16%, var(--surface-card));
  color: var(--text-primary);
  box-shadow: var(--shadow-sm);
  text-decoration: none; line-height: 1.2; z-index: 1;
}
able-time-grid[variant="week"] .atg-w-l1 { display: flex; align-items: baseline; gap: 5px; min-width: 0; }
able-time-grid[variant="week"] .atg-w-t {
  font-size: var(--atgw-title); font-weight: 700; color: var(--text-primary);
  min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* --text-secondary, not --text-muted. MEASURED on the rendered pixels: muted
   (#6b7280) on the 16%-tint card (#e3e1fb) is 3.78:1 — under WCAG AA's 4.5 for
   normal text. It was latent while these lines almost never rendered; the
   comfortable density puts a range line on EVERY block, so a contrast failure
   that used to be invisible is now on every card. Secondary measures 7.8:1 and
   is still plainly subordinate to a 700-weight title at 15.4:1. */
able-time-grid[variant="week"] .atg-w-tm {
  flex: none; font-size: var(--atgw-meta); font-weight: 600; color: var(--text-secondary);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
able-time-grid[variant="week"] .atg-w-l2,
able-time-grid[variant="week"] .atg-w-l3 {
  font-size: var(--atgw-meta); font-weight: 600; color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
able-time-grid[variant="week"] .atg-w-l3 { font-weight: 500; }
/* THE DEGRADE LADDER — by MEASURED width, never by column count: the same
   3-wide cluster is 30px at one page width and 110px at another, and a count
   cannot tell those apart. Each step yields the LEAST valuable thing first, and
   the title is what survives to the end. */
able-time-grid[variant="week"] .atg-w-blk[data-narrow] .atg-w-tm { display: none; }
able-time-grid[variant="week"] .atg-w-blk[data-narrow] .atg-w-l2,
able-time-grid[variant="week"] .atg-w-blk[data-narrow] .atg-w-l3 { display: none; }
able-time-grid[variant="week"] .atg-w-blk[data-narrow] .atg-w-t { font-size: var(--atgw-title-narrow); }
able-time-grid[variant="week"] .atg-w-blk[data-narrow] .atg-w-chip { display: none; }
able-time-grid[variant="week"] .atg-w-blk[data-min] .atg-w-t { font-size: var(--atgw-title-min); font-weight: 600; }
able-time-grid[variant="week"] .atg-w-blk[data-min] { padding-left: 4px; padding-right: 2px; }
/* THE NARROWEST BLOCK THAT IS ALSO TALL: a 3-wide cluster gives each block ~45px
   of width but often 60-90px of HEIGHT, and one clipped line was throwing that
   height away — 'Adv…' where three wrapped lines fit. A deep block wraps. This
   is the only place the title is allowed to wrap, and it is allowed precisely
   because the alternative is a card that says nothing. Nothing is hidden and no
   event is collapsed away: a calendar that silently drops an event is worse than
   one that abbreviates it. */
able-time-grid[variant="week"] .atg-w-blk[data-narrow][data-wrap] .atg-w-t,
able-time-grid[variant="week"] .atg-w-blk[data-min][data-wrap] .atg-w-t {
  white-space: normal; overflow-wrap: anywhere; line-height: 1.15;
  display: -webkit-box; -webkit-box-orient: vertical;
  /* The 3 is only the value used before layout can speak — _wkMeasure writes the
     real one per block from the room it measured. Kept as the fallback because a
     block that never reached the measure pass must still clamp SOMEWHERE, and
     the old constant is the known-safe answer. */
  -webkit-line-clamp: var(--atgw-clamp, 3);
}
able-time-grid[variant="week"] .atg-w-blk[data-narrow][data-wrap] .atg-w-l1,
able-time-grid[variant="week"] .atg-w-blk[data-min][data-wrap] .atg-w-l1 { align-items: flex-start; }
able-time-grid[variant="week"] .atg-w-blk[data-thin] {
  padding-top: 0; padding-bottom: 0; justify-content: center;
}
/* ⚠ :not(narrow):not(min) — WIDTH outranks HEIGHT. This rule sits AFTER the two
   width rungs at equal specificity, so without the guard a block that is thin
   AND narrow renders the full title size in a column that cannot hold it:
   measured on fryaway, 14px in a 57px slice, 9 of 9 such blocks truncated. A
   short block can afford its type; a narrow one cannot, whatever its height. */
able-time-grid[variant="week"] .atg-w-blk[data-thin]:not([data-narrow]):not([data-min]) .atg-w-t {
  font-size: var(--atgw-title-thin);
}
able-time-grid[variant="week"] .atg-w-blk[data-thin] .atg-w-tm { display: none; }
able-time-grid[variant="week"] .atg-w-blk[data-done] { opacity: .55; }
able-time-grid[variant="week"] .atg-w-blk[data-done] .atg-w-t { text-decoration: line-through; }
able-time-grid[variant="week"] .atg-w-blk[data-done] .atg-w-t::before { content: "✓ "; font-weight: 800; }
@media (hover: hover) and (pointer: fine) {
  able-time-grid[variant="week"] .atg-w-blk:hover {
    background: color-mix(in srgb, var(--atg-ink) 26%, var(--surface-card)); z-index: 5;
  }
}
able-time-grid[variant="week"] .atg-w-blk:focus-visible { outline: 2px solid var(--accent); outline-offset: 0; z-index: 5; }

/* ── the pin: a deadline is an INSTANT on the grid, not a block ────────────── */
able-time-grid[variant="week"] .atg-w-pin {
  appearance: none; border: 0; font: inherit;
  position: absolute; left: 2px; right: 2px;
  display: flex; align-items: center; gap: 4px;
  min-width: 0; height: var(--atgw-pin-h); padding: 0 5px;
  border-radius: 3px;
  background: color-mix(in srgb, var(--atg-ink) 10%, var(--surface-card));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--atg-ink) 55%, transparent);
  color: var(--text-primary);
  font-size: var(--atgw-pin); font-weight: 700;
  line-height: var(--atgw-pin-h); text-align: left;
  cursor: pointer; z-index: 3;
}
able-time-grid[variant="week"] .atg-w-pin-f { flex: none; color: var(--atg-ink); font-size: var(--atgw-meta); }
able-time-grid[variant="week"] .atg-w-pin-tm {
  flex: none; color: var(--text-secondary); font-weight: 600;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
able-time-grid[variant="week"] .atg-w-pin-t { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
able-time-grid[variant="week"] .atg-w-pin[data-min] .atg-w-pin-t { display: none; }
able-time-grid[variant="week"] .atg-w-pin[data-min] .atg-w-chip { display: none; }
able-time-grid[variant="week"] .atg-w-pin[data-min] { padding: 0 4px; }
able-time-grid[variant="week"] .atg-w-pin[data-done] { opacity: .55; }
able-time-grid[variant="week"] .atg-w-pin[data-done] .atg-w-pin-t { text-decoration: line-through; }
@media (hover: hover) and (pointer: fine) {
  able-time-grid[variant="week"] .atg-w-pin:hover {
    background: color-mix(in srgb, var(--atg-ink) 22%, var(--surface-card)); z-index: 6;
  }
}
able-time-grid[variant="week"] .atg-w-pin:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; z-index: 6; }

/* ── the chip: ORIGIN, never a colour. A second colour axis on a block makes
   both unreadable; a two-letter chip states whose it is and costs 15px. ───── */
able-time-grid[variant="week"] .atg-w-chip {
  flex: none; display: inline-grid; place-items: center;
  min-width: var(--atgw-chip-h); height: var(--atgw-chip-h); padding: 0 3px;
  margin-left: 4px; vertical-align: 1px;
  border-radius: var(--radius-full);
  border: 1px solid var(--border-strong);
  background: var(--surface-card); color: var(--text-secondary);
  font-size: var(--atgw-chip); font-weight: 800; letter-spacing: .02em; line-height: 1;
}
able-time-grid[variant="week"] .atg-w-t .atg-w-chip { display: inline-grid; }

/* z-ladder: blocks(1) < now-line(2) < pins(3). The line crosses BLOCKS (the
   familiar look) but must never strike through a pin chip. */
able-time-grid[variant="week"] .atg-w-now {
  position: absolute; left: 0; right: 0; height: 2px;
  background: var(--danger); z-index: 2; pointer-events: none;
}
able-time-grid[variant="week"] .atg-w-now::before {
  content: ""; position: absolute; left: -3px; top: -3px;
  width: 8px; height: 8px; border-radius: 50%; background: var(--danger);
}

/* ── the past fade ────────────────────────────────────────────────────────── */
able-time-grid[variant="week"] .atg-w-blk[data-past] { opacity: .45; filter: saturate(.65); }
able-time-grid[variant="week"] .atg-w-banner[data-past] { opacity: .45; }
@media (hover: hover) and (pointer: fine) {
  able-time-grid[variant="week"] .atg-w-blk[data-past]:hover { opacity: .95; filter: none; }
}
@media (prefers-reduced-motion: reduce) {
  able-time-grid[variant="week"] .atg-w-blk,
  able-time-grid[variant="week"] .atg-w-banner { transition: none; }
}


/* able-toast-stack */

able-toast-stack { display: contents; }
.atst-stack {
  position: fixed; z-index: var(--z-toast, 500);
  display: flex; flex-direction: column; gap: 8px;
  padding: 16px;
  pointer-events: none;
  max-width: 360px;
}
.atst-stack[data-corner="top-right"]    { top: 0;    right: 0; }
.atst-stack[data-corner="top-left"]     { top: 0;    left: 0; }
.atst-stack[data-corner="bottom-right"] { bottom: 0; right: 0; }
.atst-stack[data-corner="bottom-left"]  { bottom: 0; left: 0; }
.atst-toast {
  pointer-events: auto;
  display: flex; gap: 8px; align-items: flex-start;
  padding: 10px 14px;
  /* Bug — toast must own its own contrast pair. Previously we read
     --text-primary which in light-themed tenants resolves to a dark
     ink colour (correct for body text on light backgrounds) but rendered
     dark-on-dark inside this toast's deliberately dark surface. Use
     the inverse-surface pair with explicit fallbacks so the toast is
     always readable regardless of the tenant theme; a tenant re-skins
     via its own theme at (0,2,0) — see the tenniswest / ablecollect
     ".atst-stack .atst-toast" blocks. */
  background: var(--surface-inverse, #1f2937);
  color: var(--text-inverse, #f8fafc);
  border: 1px solid var(--border-default, rgba(255, 255, 255, 0.12));
  border-radius: 10px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
  font-size: 13px;
  max-width: 360px;
  transition: opacity 180ms ease-out, transform 180ms ease-out;
}
.atst-toast[data-kind="success"] { border-left: 3px solid var(--success, #267e3d); }
.atst-toast[data-kind="warning"] { border-left: 3px solid var(--warning, #f9a006); }
.atst-toast[data-kind="error"]   { border-left: 3px solid var(--danger,  #BF3C00); }
.atst-toast[data-kind="info"]    { border-left: 3px solid var(--info,    #4B6D7A); }
.atst-toast.atst-enter { opacity: 0; transform: translateX(20px); }
.atst-toast.atst-exit  { opacity: 0; transform: translateX(20px); }
/* Kind must not be carried by colour alone (a11y) — every toast leads with a
   per-kind glyph. currentColor so a re-skinning theme tints it for free. */
.atst-ico { flex: none; width: 16px; height: 16px; margin-top: 1px; opacity: .95; }
.atst-ico svg { width: 100%; height: 100%; display: block; }
.atst-body { flex: 1; min-width: 0; line-height: 1.4; }
.atst-title { font-weight: 650; }
.atst-title + .atst-msg { margin-top: 2px; opacity: .85; }
.atst-count {
  display: inline-block; margin-left: 6px; padding: 0 5px;
  border-radius: 999px; font-size: 11px; font-weight: 650;
  background: rgba(127, 127, 127, 0.28);
}
.atst-actions { display: flex; gap: 6px; margin-top: 7px; }
.atst-action {
  font: inherit; font-size: 12px; font-weight: 650; cursor: pointer;
  color: inherit; background: rgba(127, 127, 127, 0.22);
  border: 0; border-radius: 6px; padding: 6px 10px;
}
.atst-action:hover { background: rgba(127, 127, 127, 0.34); }
.atst-close {
  background: transparent; color: inherit; border: 0; cursor: pointer;
  font-size: 16px; line-height: 1; opacity: 0.7;
  padding: 0 4px;
}
.atst-close:hover { opacity: 1; }
.atst-action:focus-visible, .atst-close:focus-visible {
  outline: 2px solid currentColor; outline-offset: 2px;
}
/* Mobile — anchor to the bottom, full width minus a gutter, clear of the
   safe-area. The "top: auto !important" is deliberate: it neutralises a tenant
   shell that pins the stack down from the top for its own fixed header
   (pcp@commerce ships "able-toast-stack .atst-stack { top: 64px !important }"),
   which would otherwise stretch the stack from that offset to the bottom.
   Selector carries [data-corner] so it beats the corner rules above. */
@media (max-width: 640px) {
  .atst-stack[data-corner] {
    top: auto !important; bottom: 0; left: 0; right: 0;
    max-width: none; width: auto;
    padding: 12px;
    padding-bottom: calc(12px + env(safe-area-inset-bottom, 0px));
  }
  .atst-toast { max-width: none; }
  .atst-toast.atst-enter, .atst-toast.atst-exit { transform: translateY(12px); }
}
@media (prefers-reduced-motion: reduce) {
  .atst-toast { transition: none; }
}


/* able-user-menu */

able-user-menu {
  display: inline-block;
  position: relative;
  font-family: var(--font-ui, var(--font-sans, system-ui, -apple-system, BlinkMacSystemFont, sans-serif));
}
/* 36px around a 32px avatar on desktop; the 44px touch floor moves to the
   (pointer: coarse) query below, same as the menu rows. In a sidebar footer the
   44px box made the identity row the tallest thing in the column (measured 49px
   beside 30px nav rows) to hold a 32px avatar. */
able-user-menu .aum-trigger {
  list-style: none;
  position: relative;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 36px;
  min-height: 36px;
  padding: 2px;
  border-radius: 999px;
  border: 1px solid transparent;
  background: transparent;
  transition: border-color 200ms ease, background 200ms ease;
}
@media (hover: hover) and (pointer: fine) {
  able-user-menu .aum-trigger:hover {
    border-color: var(--aum-trigger-border, rgba(252, 251, 247, 0.18));
    background: var(--aum-trigger-bg-hover, rgba(252, 251, 247, 0.04));
  }
}
able-user-menu[open] .aum-trigger {
  border-color: var(--aum-trigger-border, rgba(252, 251, 247, 0.24));
  background: var(--aum-trigger-bg-hover, rgba(252, 251, 247, 0.06));
}
able-user-menu .aum-avatar {
  width: 32px;
  height: 32px;
  display: inline-grid;
  place-items: center;
  border-radius: 999px;
  background: var(--aum-avatar-bg, var(--brand, var(--accent, #DC4F37)));
  color: var(--aum-avatar-color, #fff);
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.01em;
  user-select: none;
  line-height: 1;
}
/* Optional inline name beside the avatar (show-name): the round avatar trigger
   becomes a left-aligned pill (avatar + name). The name self-resolves from the
   session; the host owns its ink via the color property. Default OFF, so a trigger
   without show-name is the unchanged round avatar. */
able-user-menu .aum-trigger--named {
  justify-content: flex-start;
  gap: 8px;
  min-width: 0;
  max-width: 100%;
  padding: 2px 10px 2px 2px;
}
able-user-menu .aum-trigger--named .aum-avatar { flex: none; }
able-user-menu .aum-trigger-name {
  min-width: 0;
  font-size: 13.5px;
  font-weight: 600;
  line-height: 1.2;
  color: inherit;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* ── Impersonation treatment ── */
able-user-menu .aum-trigger--impersonating .aum-avatar {
  box-shadow: 0 0 0 2px var(--aum-impersonation-color, var(--warning, #b45309));
}
able-user-menu .aum-badge {
  position: absolute;
  top: 0;
  right: 0;
  width: 16px;
  height: 16px;
  display: inline-grid;
  place-items: center;
  border-radius: 999px;
  background: var(--aum-impersonation-color, var(--warning, #b45309));
  color: #fff;
  font-size: 9px;
  line-height: 1;
  box-shadow: 0 0 0 1.5px var(--aum-badge-ring, rgba(255, 255, 255, 0.85));
  pointer-events: none;
}
/* Named trigger: the badge sits on the avatar's corner, not the trigger's far
   edge, so it never eats the name's width. */
able-user-menu .aum-trigger--named .aum-badge {
  top: -2px;
  right: auto;
  left: 22px;
}
able-user-menu .aum-sub.aum-sub--impersonation {
  color: var(--aum-impersonation-color, var(--warning, #b45309));
  font-weight: 700;
  text-transform: none;
  letter-spacing: 0.02em;
}
able-user-menu .aum-impersonator {
  display: block;
  max-width: 100%;
  font-size: 11px;
  line-height: 1.3;
  color: var(--ash, var(--text-muted, #6b7280));
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
able-user-menu .aum-item--impersonation i {
  color: var(--aum-impersonation-color, var(--warning, #b45309));
  opacity: 1;
}
@media (hover: hover) and (pointer: fine) {
  able-user-menu .aum-item--impersonation:hover {
    background: var(--aum-impersonation-hover-bg, rgba(180, 83, 9, 0.08));
    color: var(--aum-impersonation-color, var(--warning, #b45309));
  }
  able-user-menu .aum-item--impersonation:hover i {
    color: var(--aum-impersonation-color, var(--warning, #b45309));
  }
}
able-user-menu .aum-menu {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  min-width: 236px;
  max-width: 300px;
  background: var(--aum-menu-bg, var(--bone-white, var(--surface-card, #fff)));
  border: 1px solid var(--aum-menu-border, var(--paper-3, var(--border-default, #e5e7eb)));
  border-radius: 10px;
  box-shadow: var(--aum-menu-shadow, 0 12px 32px -8px rgba(0, 0, 0, 0.18));
  padding: 6px;
  z-index: 60;
  display: flex;
  flex-direction: column;
  gap: 1px;
}
able-user-menu .aum-menu[hidden] { display: none; }
able-user-menu .aum-identity {
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 8px 12px 7px;
}
able-user-menu .aum-name {
  display: block;
  max-width: 100%;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--aum-item-color, var(--ink, var(--text-primary, #1d1c17)));
  line-height: 1.3;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
able-user-menu .aum-sub {
  font-size: 10px;
  color: var(--ash, var(--text-muted, #6b7280));
  text-transform: uppercase;
  letter-spacing: 0.07em;
}
/* The rule is now the menu's GROUPING device, not decoration under the identity
   line, so it has to be seen. Derived from the item ink rather than a border
   token because the border tokens are tuned for panel edges: --border-default
   measured 1.50:1 against the dark menu (#30363d on #161b22), which is fine for
   an edge and too faint for a boundary — reviewed dark, the 8px gap read as
   inconsistent spacing because the line explaining it was invisible. Mixing the
   ITEM COLOR gets a rule that is proportionally visible in whichever mode is
   showing, since that token is what flips. Tighter margin too: the boundary
   should be led by the line, not by the whitespace around it.
   The token fallback stays first so a browser without color-mix keeps the old
   rule rather than none, and --aum-divider-color lets a tenant override both. */
able-user-menu .aum-divider {
  height: 1px;
  background: var(--aum-divider-color, var(--paper-3, var(--border-default, #e5e7eb)));
  margin: 3px 0;
}
@supports (background: color-mix(in srgb, red 10%, transparent)) {
  able-user-menu .aum-divider {
    background: var(--aum-divider-color,
      color-mix(in srgb, var(--aum-item-color, var(--ink, var(--text-primary, #1d1c17))) 26%, transparent));
  }
}
/* Row density. 34px is a dense menu row (the tenant shells that mount this menu
   run their own nav at 28-32px, so this reads as one of them rather than as a
   visitor), and it is the VISUAL height only — the 44px touch floor is not
   dropped, it is moved to where it applies, in the (pointer: coarse) query
   below. That is this codebase's own doctrine for the floor, not an exception
   taken for this menu: see able-claw.js, which moved an iOS anti-zoom floor out
   of the shared rule for the same reason ("the floor belongs in a touch-only
   query, where it now lives"). A mouse does not need 44px, and paying it on
   desktop is what made a 6-item menu 360px tall. */
able-user-menu .aum-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 12px;
  font-size: 13.5px;
  font-weight: 500;
  color: var(--aum-item-color, var(--ink, var(--text-primary, #1d1c17)));
  text-decoration: none;
  border-radius: 6px;
  background: transparent;
  border: 0;
  cursor: pointer;
  min-height: 34px;
  text-align: left;
  width: 100%;
  font-family: inherit;
}
@media (pointer: coarse) {
  able-user-menu .aum-item { min-height: 44px; }
  able-user-menu .aum-trigger { min-width: 44px; min-height: 44px; padding: 4px; }
}
able-user-menu .aum-item i {
  width: 16px;
  text-align: center;
  font-size: 13px;
  opacity: 0.65;
}
@media (hover: hover) and (pointer: fine) {
  able-user-menu .aum-item:hover {
    background: var(--aum-item-hover-bg, var(--paper, var(--surface-hover, #f8f8f9)));
  }
  able-user-menu .aum-item:hover i { opacity: 1; }
}
able-user-menu .aum-item--danger i { color: var(--ash, var(--text-muted, #6b7280)); }
@media (hover: hover) and (pointer: fine) {
  able-user-menu .aum-item--danger:hover {
    background: rgba(185, 28, 28, 0.08);
    color: var(--aum-item-danger-color, var(--narrative-crimson-deep, var(--danger, #b91c1c)));
  }
  able-user-menu .aum-item--danger:hover i {
    color: var(--aum-item-danger-color, var(--narrative-crimson-deep, var(--danger, #b91c1c)));
  }
}


/* able-week-card */

/* ⚠ ACCENT-COLOURED TEXT USES --accent-text, NEVER --accent (able-day-card.js
   carries the measured contrast reasoning — the FILL token sits under AA on the
   dark card ground; the TEXT token re-binds per mode). */
able-week-card { display: block; max-width: 460px; margin: 8px 0; }
able-week-card .awkc {
  /* TONE (CALENDAR-REVAMP.md 2c): ten closed NAMES; the THEME owns the values.
     Fallbacks only, so the card is legible in a theme that has not declared
     --cal-tone-* yet. A native renderer maps the same ten names. */
  --cal-tone-primary:  var(--cal-tone-primary-x,  var(--accent));
  --cal-tone-accent:   var(--cal-tone-accent-x,   var(--accent));
  --cal-tone-info:     var(--cal-tone-info-x,     var(--info));
  --cal-tone-warn:     var(--cal-tone-warn-x,     var(--warning));
  --cal-tone-critical: var(--cal-tone-critical-x, var(--danger));
  --cal-tone-money:    var(--cal-tone-money-x,    var(--success));
  --cal-tone-travel:   var(--cal-tone-travel-x,   var(--info));
  --cal-tone-muted:    var(--cal-tone-muted-x,    var(--text-muted));
  --cal-tone-social:   var(--cal-tone-social-x,   var(--success));
  --cal-tone-neutral:  var(--cal-tone-neutral-x,  var(--text-muted));
  background: var(--surface-card, #fff);
  border: 1px solid var(--border-default, #e5e7eb);
  border-radius: var(--radius-xl, 14px);
  box-shadow: var(--shadow-sm, 0 1px 2px rgba(0,0,0,.06));
  padding: 12px 14px;
  font-family: var(--font-sans, sans-serif);
  color: var(--text-primary, #1d1c17);
}
able-week-card .awkc-eyebrow {
  font-size: 10px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase;
  color: var(--accent-text, var(--accent, #1b2a4a)); margin: 0 0 2px;
}
able-week-card .awkc-title {
  font-size: 17px; line-height: 1.2; font-weight: 700; margin: 0;
  color: var(--text-primary, #1d1c17);
}
able-week-card .awkc-summary { margin: 2px 0 0; font-size: 12px; color: var(--text-muted, #6b7280); }

/* the seven day rows */
able-week-card .awkc-days { margin: 9px 0 0; display: flex; flex-direction: column; }
able-week-card .awkc-day {
  padding: 5px 0 6px;
  border-top: 1px solid var(--border-subtle, #f1f5f9);
}
able-week-card .awkc-day:first-child { border-top: 0; }
able-week-card .awkc-day[data-today="true"] .awkc-day-lab {
  color: var(--accent-text, var(--accent, #1b2a4a));
}
able-week-card .awkc-day-head {
  display: flex; align-items: center; gap: 8px;
  width: 100%; border: 0; background: transparent; padding: 0;
  font-family: inherit; text-align: left; cursor: pointer;
  color: inherit;
}
able-week-card .awkc-day-lab {
  font-size: 11px; font-weight: 700; letter-spacing: .02em;
  min-width: 52px; font-variant-numeric: tabular-nums;
}
/* THE MARK SLOT — reserved even when empty (contract §4), so a day gaining a
   mark cannot shift its neighbours. Bar = time occupied; dots = markers. */
able-week-card .awkc-marks { display: inline-flex; align-items: center; gap: 3px; height: 7px; flex: 1 1 auto; }
able-week-card .awkc-bar {
  display: inline-block; height: 4px; border-radius: 2px;
  background: var(--cal-tone-primary);
}
able-week-card .awkc-dot {
  display: inline-block; width: 5px; height: 5px; border-radius: 50%;
  background: var(--awkc-c, var(--text-muted));
}
able-week-card .awkc-day-quiet { font-size: 11px; color: var(--text-muted, #6b7280); }
able-week-card .awkc-rows { margin: 3px 0 0; display: flex; flex-direction: column; gap: 1px; }

/* ── DONE + TYPE, the two axes the pages carry and this card did not ─────────────
   DONE: the calendar has kept a finished row ON its day since 2026-08-18, struck
   rather than removed, because a calendar that deletes what you completed cannot
   answer "did I do that". The card never carried the flag, so one HW read finished
   on /week and still-owed in chat — two halves of ONE view disagreeing about the
   most consequential bit on the row.
   TYPE: work_type has been on the wire all along and no renderer drew it. The mark
   is geometric, never a second hue: colour already means WHICH CLASS, and shape is
   what survives greyscale, which is what stops colour being the only signal. Drawn
   as a ::before on the title — the row is a grid, so a generated child of the ROW
   would become a third track; a child of the title cannot. */
able-week-card .awkc-row[data-done] { opacity: .55; }
able-week-card .awkc-row[data-done] .awkc-n { text-decoration: line-through; }
able-week-card .awkc-row[data-work]:not([data-work="class"]):not([data-work="event"]) .awkc-n::before {
  content: ''; display: inline-block; flex: none;
  width: 7px; height: 7px; margin-right: 5px;
  background: var(--awkc-c, currentColor); opacity: .9;
}
able-week-card .awkc-row[data-work="submit"] .awkc-n::before { border-radius: 50%; }
able-week-card .awkc-row[data-work="prep"] .awkc-n::before {
  border-radius: 50%; background: transparent;
  box-shadow: inset 0 0 0 2px var(--awkc-c, currentColor);
}
able-week-card .awkc-row[data-work="assess"] .awkc-n::before { transform: rotate(45deg); border-radius: 1px; }
able-week-card .awkc-row[data-work="admin"] .awkc-n::before {
  border-radius: 1px; background: transparent;
  box-shadow: inset 0 0 0 2px var(--awkc-c, currentColor);
}
able-week-card .awkc-row[data-work="chore"] .awkc-n::before { border-radius: 1px; }
able-week-card .awkc-row[data-work="engage"] .awkc-n::before {
  width: 3px; border-radius: 1px; box-shadow: 5px 0 0 0 var(--awkc-c, currentColor);
  margin-right: 9px;
}

able-week-card .awkc-row {
  display: grid; grid-template-columns: 68px 1fr; gap: 8px; align-items: baseline;
  width: 100%; border: 0; background: transparent;
  padding: 2px 4px; border-radius: var(--radius-sm, 4px);
  font-family: inherit; text-align: left; cursor: pointer;
  color: var(--text-primary, #1d1c17);
  border-left: 3px solid var(--awkc-c, var(--accent));
}
@media (hover: hover) and (pointer: fine) {
  able-week-card .awkc-row:hover, able-week-card .awkc-day-head:hover { background: var(--surface-hover, #f1f5f9); }
}
able-week-card .awkc-row:focus-visible, able-week-card .awkc-day-head:focus-visible {
  outline: 2px solid var(--accent); outline-offset: -2px;
}
able-week-card .awkc-time {
  font-size: 11px; font-variant-numeric: tabular-nums; color: var(--text-muted, #6b7280);
  line-height: 1.35; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
able-week-card .awkc-body { min-width: 0; }
able-week-card .awkc-n {
  font-size: 12px; font-weight: 600; line-height: 1.3;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: block;
}
able-week-card .awkc-s {
  font-size: 11px; color: var(--text-muted, #6b7280); line-height: 1.3;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: block;
}
/* the CLASS — carried by the row's own hue, so it needs weight, not a second colour */
able-week-card .awkc-c {
  font-weight: 700; letter-spacing: 0.01em;
  color: var(--awkc-c, var(--text-primary)); margin-right: 5px;
}
/* row + its source link, grouped so the pair stays one unit */
able-week-card .awkc-roww { display: flex; align-items: stretch; gap: 0; }
able-week-card .awkc-roww .awkc-row { flex: 1 1 auto; min-width: 0; }
able-week-card .awkc-src {
  display: inline-flex; align-items: center; justify-content: center;
  flex: 0 0 auto; min-width: 22px; padding: 0 5px;
  border-radius: 0 var(--radius-sm, 4px) var(--radius-sm, 4px) 0;
  font-size: 11px; line-height: 1; text-decoration: none;
  color: var(--awkc-c, var(--text-muted, #6b7280));
}
@media (hover: hover) and (pointer: fine) {
  able-week-card .awkc-src:hover { background: var(--surface-hover, #f1f5f9); }
}
able-week-card .awkc-src:focus-visible { outline: 2px solid var(--accent, #1b2a4a); outline-offset: 1px; }
able-week-card .awkc-more { margin: 1px 0 0 76px; font-size: 11px; font-weight: 600; color: var(--text-muted, #6b7280); }
able-week-card .awkc-nothing { margin: 10px 0 2px; font-size: 12px; color: var(--text-muted, #6b7280); font-style: italic; }
able-week-card .awkc-open {
  display: inline-block; margin: 10px 0 0;
  font-size: 12px; font-weight: 600; color: var(--accent-text, var(--accent, #1b2a4a)); text-decoration: none;
}
able-week-card .awkc-open:hover { text-decoration: underline; }

/* 320px: the time column would squeeze the title to nothing, so it goes above */
@media (max-width: 380px) {
  able-week-card .awkc-row { grid-template-columns: 1fr; gap: 0; }
  able-week-card .awkc-more { margin-left: 4px; }
}

